summaryrefslogtreecommitdiff
path: root/mu
diff options
context:
space:
mode:
authorDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2025-08-16 11:40:15 +0300
committerDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2025-08-16 12:18:21 +0300
commita6b1f47a30c7aea84697553da4848b626c9faaca (patch)
tree5d4ae6435a2382d0541102ff50cf52bb75a8f9ab /mu
parentf504289a021b0296701428467c469cbdaa351923 (diff)
labels: refactor import/export to mu-store-labels
Move the import/export code to 'lib'.
Diffstat (limited to 'mu')
-rw-r--r--mu/mu-cmd-label.cc149
1 files changed, 6 insertions, 143 deletions
diff --git a/mu/mu-cmd-label.cc b/mu/mu-cmd-label.cc
index 30396fe..b7814df 100644
--- a/mu/mu-cmd-label.cc
+++ b/mu/mu-cmd-label.cc
@@ -125,128 +125,19 @@ label_list(const Mu::Store& store, const Options& opts)
return Ok();
}
-constexpr std::string_view path_key = "path:";
-constexpr std::string_view message_id_key = "message-id:";
-constexpr std::string_view labels_key = "labels:";
-
static Result<void>
label_export(const Mu::Store& store, const Options& opts)
{
- const auto now_t{::time({})};
- const auto now_tm{::localtime(&now_t)};
-
- const auto now{mu_format("{:%F-%T}", *now_tm)};
- const auto fname = opts.label.file.value_or(
- mu_format("mu-export-{}.txt", now));
- auto output{std::ofstream{fname, std::ios::out}};
- if (!output.good())
- return Err(Error{Error::Code::File,
- "failed to open '{}' for writing", fname});
-
- const auto query{opts.label.query.value_or("")};
- auto results{store.run_query(query)};
- if (!results)
- return Err(Error{Error::Code::Query,
- "failed to run query '{}': {}",
- query, *results.error().what()});
-
- mu_println(output, ";; version:0 @ {}\n", now);
-
- for (auto&& result : *results) {
- if (auto &&msg{result.message()}; msg) {
- if (const auto labels{msg->labels()}; !labels.empty()) {
- mu_print(output,
- "{}{}\n"
- "{}{}\n"
- "{}{}\n\n",
- path_key, msg->path(),
- message_id_key, msg->message_id(),
- labels_key, join(labels,','));
- }
- }
- }
+ const auto res = export_labels(store, "", opts.label.file);
+ if (!res)
+ return Err(res.error());
if (!opts.quiet)
- mu_println("written {}", fname);
+ mu_println("written {}", *res);
return Ok();
}
-static void
-log_import(const Options& opts, const std::string& msg, bool is_err=false)
-{
- if (is_err)
- mu_debug("{}", msg);
- else
- mu_warning("{}", msg);
-
- if (is_err && !opts.quiet)
- mu_printerrln("{}", msg);
- else if (opts.verbose)
- mu_println("{}", msg);
-}
-
-static void
-log_import_err(const Options& opts, const std::string& msg)
-{
- log_import(opts, msg, true);
-}
-
-
-static Result<QueryResults>
-log_import_get_matching(Mu::Store& store, const std::string& query, int max=1)
-{
- if (auto qres = store.run_query(query, {}, {}, max); !qres)
- return Err(std::move(qres.error()));
- else if (qres->empty())
- return Err(Error{Error::Code::Query,
- "no matching messages for {}", query});
- else
- return Ok(std::move(*qres));
-}
-
-
-static void
-import_labels_for_message(Mu::Store& store, const Options& opts,
- const std::string& path, const std::string& msgid,
- const std::vector<std::string> labels)
-{
- Labels::DeltaLabelVec delta_labels{};
- std::transform(labels.begin(), labels.end(), std::back_inserter(delta_labels),
- [](const auto& label) {
- return DeltaLabel{Delta::Add, label}; });
-
- const auto qres = [&]()
- ->Result<QueryResults>{
- // plan A: match by path
- if (auto qres_a{log_import_get_matching(store, "path:" + path)}; !qres_a) {
- log_import_err(opts, mu_format("failed to find by path: {}; try with message-id",
- qres_a.error().what()));
- // plan B: try the message-id
- return log_import_get_matching(store, "msgid:" + msgid, -1/*all matching*/);
- } else
- return qres_a;
- }();
-
- // neither plan a or b worked? we have to give up...
- if (!qres) {
- log_import_err(opts, qres.error().what());
- return;
- }
-
- // we have match(es)!
- for (auto&& item: *qres) {
- auto msg{*item.message()};
- if (opts.label.dry_run )
- mu_println("labels: would apply label '{}' to {}", join(labels, ","), path);
- else if (const auto res = store.update_labels(msg, delta_labels); !res)
- log_import_err(opts, mu_format("failed to update labels for {}: {}",
- msg.path(), res.error().what()));
- else
- log_import(opts, mu_format("applied labels {} to {}", join(labels, ","), path));
- }
-}
-
static Result<void>
label_import(Mu::Store& store, const Options& opts)
{
@@ -255,36 +146,8 @@ label_import(Mu::Store& store, const Options& opts)
return Err(Error{Error::Code::InvalidArgument,
"missing input file"});
- auto input{std::ifstream{*opts.label.file, std::ios::in}};
- if (!input.good())
- return Err(Error{Error::Code::File,
- "failed to open '{}' for reading",
- *opts.label.file});
-
- std::string line;
- std::string current_path, current_msgid;
- std::vector<std::string> current_labels;
-
- while (std::getline(input, line)) {
-
- if (line.find(path_key) == 0)
- current_path = line.substr(path_key.length());
- else if (line.find(message_id_key) == 0)
- current_msgid = line.substr(message_id_key.length());
- else if (line.find(labels_key) == 0) {
- current_labels = split(line.substr(labels_key.length()), ',');
- if (!current_labels.empty())
- import_labels_for_message(store, opts,
- current_path, current_msgid,
- current_labels);
- current_path.clear();
- current_msgid.clear();
- current_labels.clear();
- }
- // ignore anything else.
- }
-
- return Ok();
+ return Mu::import_labels(store, *opts.label.file,
+ opts.label.dry_run, opts.quiet, opts.verbose);
}
Result<void>