labels: improve logging for import

Clear up the code a bit using the new logging functions.

Don't log a failure for "plan A" as an error, it just scares user unnecessarily.
This commit is contained in:
Dirk-Jan C. Binnema
2025-12-13 13:22:12 +02:00
committed by Seth Ladygo
parent f66b58cd41
commit dc5bbd9884
3 changed files with 48 additions and 36 deletions

View File

@ -30,7 +30,8 @@ Mu::Labels::validate_label(const std::string &label)
if (label.empty())
return Err(Error{Error::Code::InvalidArgument,
"labels cannot be empty"});
else if (!g_utf8_validate(label.c_str(), label.size(), {})) // perhaps put hex in err str?
else if (!g_utf8_validate(label.c_str(), label.size(), {}))
// perhaps put hex in err str?
return Err(Error{Error::Code::InvalidArgument,
"labels must be valid UTF-8"});
@ -43,7 +44,7 @@ Mu::Labels::validate_label(const std::string &label)
return Err(Error{Error::Code::InvalidArgument,
"labels cannot start with '+' or '-' ({})", label});
for (auto cur = cstr; cur && *cur; cur = g_utf8_next_char(cur)) {
for (auto cur = cstr; cur && *cur; cur = g_utf8_next_char(cur)) { // NOLINT
const gunichar uc = g_utf8_get_char(cur);
if (g_unichar_isalnum(uc))
@ -117,7 +118,7 @@ Mu::Labels::updated_labels(const LabelVec& labels, const DeltaLabelVec& deltas)
// First, the delta; put in a set for uniqueness; and use a special
// comparison operator so "add" and "remove" deltas are considered "the same"
// for the set; then fill the set from the end of the deltas vec to the begining,
// for the set; then fill the set from the end of the deltas vec to the beginning,
// so "the last one wins", as we want.
// only one change per label, last one wins

View File

@ -76,6 +76,7 @@ Mu::export_labels(const Store& store, const std::string& query, Option<std::stri
auto output_res = export_output(path);
if (!output_res)
return Err(std::move(output_res.error()));
auto&[output, output_path] = *output_res;
@ -97,43 +98,45 @@ Mu::export_labels(const Store& store, const std::string& query, Option<std::stri
return Ok(std::move(output_path));
}
static void
log_import(bool quiet, bool verbose, const std::string& msg, bool is_err=false)
{
if (is_err)
mu_debug("{}", msg);
else
mu_warning("{}", msg);
if (is_err && !quiet)
mu_printerrln("{}", msg);
else if (verbose)
mu_println("{}", msg);
namespace Levels {
using Level = size_t;
constexpr Level quiet = 1 << 0;
constexpr Level verbose = 1 << 1;
constexpr Level error = 1 << 0;
}
static void
log_import_err(bool quiet, bool verbose, const std::string& msg)
{
log_import(quiet, verbose, msg, true);
}
using Level = Levels::Level;
template<typename...T>
static void output(Level level,
fmt::format_string<T...> frm, T&&... args) noexcept {
GLogLevelFlags lflags = level & Levels::error ?
G_LOG_LEVEL_WARNING : G_LOG_LEVEL_DEBUG;
mu_log(lflags, frm, std::forward<T>(args)...);
if ((level & Levels::error))
mu_printerrln(frm, std::forward<T>(args)... );
else if ((level & Levels::verbose))
mu_println(frm, std::forward<T>(args)... );
}
static Result<QueryResults>
log_import_get_matching(Mu::Store& store, const std::string& query, int max=1)
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});
"no match for or '{}'", query});
else
return Ok(std::move(*qres));
}
static void
import_labels_for_message(Mu::Store& store,
bool dry_run, bool quiet, bool verbose,
import_labels_for_message(Mu::Store& store, bool dry_run, Level level,
const std::string& path, const std::string& msgid,
const std::vector<std::string> labels)
{
@ -147,33 +150,38 @@ import_labels_for_message(Mu::Store& store,
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(quiet, verbose, mu_format("failed to find by path: {}; try with message-id",
qres_a.error().what()));
if (auto qres_a{import_get_matching(store, "path:" + path)}; !qres_a) {
output(level, "path '{}' does not match; try message-id",
qres_a.error().what());
// plan B: try the message-id
return log_import_get_matching(store, "msgid:" + msgid, -1/*all matching*/);
auto qres_b{import_get_matching(store, "msgid:" + msgid, -1/*all matching*/)};
if (!qres_b) { // plan-B failed too?
output(level | Levels::error,
"import failed: cannot find message by path '{}' or message-id '{}'",
path, msgid);
}
return qres_b;
} else
return qres_a;
}();
// neither plan a or b worked? we have to give up...
if (!qres) {
log_import_err(quiet, verbose, qres.error().what());
return;
}
mu_println("{} matches", qres->size());
// we have match(es)!
for (auto&& item: *qres) {
auto msg{*item.message()};
if (dry_run )
mu_println("labels: would apply label '{}' to {}", join(labels, ","), path);
output(level, "{}: would have applied labels {}", msg.path(), join(labels, ","));
else if (const auto res = store.update_labels(msg, delta_labels); !res)
log_import_err(quiet, verbose,
mu_format("failed to update labels for {}: {}",
msg.path(), res.error().what()));
output(level | Levels::error, "failed to update labels for {}: {}",
msg.path(), res.error().what());
else
log_import(quiet, verbose,
mu_format("applied labels {} to {}", join(labels, ","), path));
output(level, "{}: applied labels {}", msg.path(), join(labels, ","));
}
}
@ -190,6 +198,8 @@ Mu::import_labels(Mu::Store& store, const std::string& path, bool dry_run, bool
std::string current_path, current_msgid;
std::vector<std::string> current_labels;
const Level level{(quiet ? Levels::quiet : 0) | (verbose ? Levels::verbose : 0)};
while (std::getline(input, line)) {
if (line.find(path_key) == 0)
@ -199,7 +209,7 @@ Mu::import_labels(Mu::Store& store, const std::string& path, bool dry_run, bool
else if (line.find(labels_key) == 0) {
current_labels = split(line.substr(labels_key.length()), ',');
if (!current_labels.empty())
import_labels_for_message(store, dry_run, quiet, verbose,
import_labels_for_message(store, dry_run, level,
current_path, current_msgid,
current_labels);
current_path.clear();

View File

@ -43,6 +43,7 @@ class Store; // fwd declaration
Result<std::string> export_labels(const Store& store,
const std::string& query="",
Option<std::string> path={});
/**
* Import labels from a file
*