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:
@ -30,7 +30,8 @@ Mu::Labels::validate_label(const std::string &label)
|
|||||||
if (label.empty())
|
if (label.empty())
|
||||||
return Err(Error{Error::Code::InvalidArgument,
|
return Err(Error{Error::Code::InvalidArgument,
|
||||||
"labels cannot be empty"});
|
"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,
|
return Err(Error{Error::Code::InvalidArgument,
|
||||||
"labels must be valid UTF-8"});
|
"labels must be valid UTF-8"});
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ Mu::Labels::validate_label(const std::string &label)
|
|||||||
return Err(Error{Error::Code::InvalidArgument,
|
return Err(Error{Error::Code::InvalidArgument,
|
||||||
"labels cannot start with '+' or '-' ({})", label});
|
"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);
|
const gunichar uc = g_utf8_get_char(cur);
|
||||||
if (g_unichar_isalnum(uc))
|
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
|
// First, the delta; put in a set for uniqueness; and use a special
|
||||||
// comparison operator so "add" and "remove" deltas are considered "the same"
|
// 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.
|
// so "the last one wins", as we want.
|
||||||
|
|
||||||
// only one change per label, last one wins
|
// only one change per label, last one wins
|
||||||
|
|||||||
@ -76,6 +76,7 @@ Mu::export_labels(const Store& store, const std::string& query, Option<std::stri
|
|||||||
|
|
||||||
auto output_res = export_output(path);
|
auto output_res = export_output(path);
|
||||||
if (!output_res)
|
if (!output_res)
|
||||||
|
|
||||||
return Err(std::move(output_res.error()));
|
return Err(std::move(output_res.error()));
|
||||||
|
|
||||||
auto&[output, output_path] = *output_res;
|
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));
|
return Ok(std::move(output_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
namespace Levels {
|
||||||
log_import(bool quiet, bool verbose, const std::string& msg, bool is_err=false)
|
using Level = size_t;
|
||||||
{
|
constexpr Level quiet = 1 << 0;
|
||||||
if (is_err)
|
constexpr Level verbose = 1 << 1;
|
||||||
mu_debug("{}", msg);
|
constexpr Level error = 1 << 0;
|
||||||
else
|
|
||||||
mu_warning("{}", msg);
|
|
||||||
|
|
||||||
if (is_err && !quiet)
|
|
||||||
mu_printerrln("{}", msg);
|
|
||||||
else if (verbose)
|
|
||||||
mu_println("{}", msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
using Level = Levels::Level;
|
||||||
log_import_err(bool quiet, bool verbose, const std::string& msg)
|
|
||||||
{
|
|
||||||
log_import(quiet, verbose, msg, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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>
|
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)
|
if (auto qres = store.run_query(query, {}, {}, max); !qres)
|
||||||
return Err(std::move(qres.error()));
|
return Err(std::move(qres.error()));
|
||||||
else if (qres->empty())
|
else if (qres->empty())
|
||||||
return Err(Error{Error::Code::Query,
|
return Err(Error{Error::Code::Query,
|
||||||
"no matching messages for {}", query});
|
"no match for or '{}'", query});
|
||||||
else
|
else
|
||||||
return Ok(std::move(*qres));
|
return Ok(std::move(*qres));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
import_labels_for_message(Mu::Store& store,
|
import_labels_for_message(Mu::Store& store, bool dry_run, Level level,
|
||||||
bool dry_run, bool quiet, bool verbose,
|
|
||||||
const std::string& path, const std::string& msgid,
|
const std::string& path, const std::string& msgid,
|
||||||
const std::vector<std::string> labels)
|
const std::vector<std::string> labels)
|
||||||
{
|
{
|
||||||
@ -147,33 +150,38 @@ import_labels_for_message(Mu::Store& store,
|
|||||||
|
|
||||||
const auto qres = [&]()->Result<QueryResults>{
|
const auto qres = [&]()->Result<QueryResults>{
|
||||||
// plan A: match by path
|
// plan A: match by path
|
||||||
if (auto qres_a{log_import_get_matching(store, "path:" + path)}; !qres_a) {
|
if (auto qres_a{import_get_matching(store, "path:" + path)}; !qres_a) {
|
||||||
log_import_err(quiet, verbose, mu_format("failed to find by path: {}; try with message-id",
|
output(level, "path '{}' does not match; try message-id",
|
||||||
qres_a.error().what()));
|
qres_a.error().what());
|
||||||
// plan B: try the message-id
|
// 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
|
} else
|
||||||
return qres_a;
|
return qres_a;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
// neither plan a or b worked? we have to give up...
|
// neither plan a or b worked? we have to give up...
|
||||||
if (!qres) {
|
if (!qres) {
|
||||||
log_import_err(quiet, verbose, qres.error().what());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mu_println("{} matches", qres->size());
|
||||||
|
|
||||||
// we have match(es)!
|
// we have match(es)!
|
||||||
for (auto&& item: *qres) {
|
for (auto&& item: *qres) {
|
||||||
auto msg{*item.message()};
|
auto msg{*item.message()};
|
||||||
if (dry_run )
|
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)
|
else if (const auto res = store.update_labels(msg, delta_labels); !res)
|
||||||
log_import_err(quiet, verbose,
|
output(level | Levels::error, "failed to update labels for {}: {}",
|
||||||
mu_format("failed to update labels for {}: {}",
|
msg.path(), res.error().what());
|
||||||
msg.path(), res.error().what()));
|
|
||||||
else
|
else
|
||||||
log_import(quiet, verbose,
|
output(level, "{}: applied labels {}", msg.path(), join(labels, ","));
|
||||||
mu_format("applied labels {} to {}", join(labels, ","), path));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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::string current_path, current_msgid;
|
||||||
std::vector<std::string> current_labels;
|
std::vector<std::string> current_labels;
|
||||||
|
|
||||||
|
const Level level{(quiet ? Levels::quiet : 0) | (verbose ? Levels::verbose : 0)};
|
||||||
|
|
||||||
while (std::getline(input, line)) {
|
while (std::getline(input, line)) {
|
||||||
|
|
||||||
if (line.find(path_key) == 0)
|
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) {
|
else if (line.find(labels_key) == 0) {
|
||||||
current_labels = split(line.substr(labels_key.length()), ',');
|
current_labels = split(line.substr(labels_key.length()), ',');
|
||||||
if (!current_labels.empty())
|
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_path, current_msgid,
|
||||||
current_labels);
|
current_labels);
|
||||||
current_path.clear();
|
current_path.clear();
|
||||||
|
|||||||
@ -43,6 +43,7 @@ class Store; // fwd declaration
|
|||||||
Result<std::string> export_labels(const Store& store,
|
Result<std::string> export_labels(const Store& store,
|
||||||
const std::string& query="",
|
const std::string& query="",
|
||||||
Option<std::string> path={});
|
Option<std::string> path={});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import labels from a file
|
* Import labels from a file
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user