summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2025-08-16 15:48:08 +0300
committerDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2025-08-16 16:20:33 +0300
commit8c706a77db5a2aee6bcc6af53788e35e9805f945 (patch)
tree1470215e0f915b122d5a0760d03e8091c1020dc0 /lib
parenta6b1f47a30c7aea84697553da4848b626c9faaca (diff)
mu-init: automatic export labels with --reinit
When re-initializing the store, automatically write the labels to a file in mu's cache, so user can later import them.
Diffstat (limited to 'lib')
-rw-r--r--lib/mu-store-labels.cc24
-rw-r--r--lib/mu-store-labels.hh16
-rw-r--r--lib/mu-store.cc40
-rw-r--r--lib/utils/mu-utils-file.cc3
4 files changed, 62 insertions, 21 deletions
diff --git a/lib/mu-store-labels.cc b/lib/mu-store-labels.cc
index 5aa6c94..61666aa 100644
--- a/lib/mu-store-labels.cc
+++ b/lib/mu-store-labels.cc
@@ -24,9 +24,9 @@
using namespace Mu;
namespace {
-constexpr std::string_view path_key = "path:";
-constexpr std::string_view message_id_key = "message-id:";
-constexpr std::string_view labels_key = "labels:";
+constexpr std::string_view path_key = "path:";
+constexpr std::string_view message_id_key = "message-id:";
+constexpr std::string_view labels_key = "labels:";
}
using OutputPair = std::pair<std::ofstream, std::string>;
@@ -36,14 +36,26 @@ export_output(Option<std::string> path)
{
const auto now_t{::time({})};
const auto now_tm{::localtime(&now_t)};
-
const auto now{mu_format("{:%F-%T}", *now_tm)};
- auto fname = path.value_or(mu_format("mu-export-{}.txt", now));
+
+ // if path is not specified, use a generated file name (in pwd)
+ // if path is specified but ends in '/', use the generated file in that
+ // directory (must exist)
+ // otherwise, use the path.
+ auto fname = [&]() {
+ const auto default_fname{mu_format("mu-export-{}.txt", now)};
+ if (!path || path->empty())
+ return default_fname;
+ else if (path->at(path->length() - 1) == '/')
+ return *path + default_fname;
+ else
+ return *path;
+ }();
auto output{std::ofstream{fname, std::ios::out}};
if (!output.good())
return Err(Error{Error::Code::File,
- "failed pen '{}' for writing", fname});
+ "failed to open '{}' for writing", fname});
mu_println(output, ";; version:0 @ {}\n", now);
diff --git a/lib/mu-store-labels.hh b/lib/mu-store-labels.hh
index 55b33f4..f9daae3 100644
--- a/lib/mu-store-labels.hh
+++ b/lib/mu-store-labels.hh
@@ -129,11 +129,13 @@ public:
std::string line;
while (std::getline(ss, line)) {
- if (const auto parts = Mu::split(line, SepaChar2); parts.size() != 2)
+ if (const auto parts =
+ Mu::split(line, SepaChar2); parts.size() != 2)
mu_warning("error: '{}'", line);
else
map.emplace(std::move(parts[0]),
- static_cast<std::size_t>(g_ascii_strtoll(parts[1].c_str(),{}, 10)));
+ static_cast<std::size_t>(
+ g_ascii_strtoll(parts[1].c_str(),{}, 10)));
}
return map;
}
@@ -148,14 +150,17 @@ class Store;
* Export labels to a file
*
* If path is not specified, use a file in the current directory
+ * If path ends in '/', write file in the path-directory
*
* @param store a store object
- * @param query for the message whose labels to export
+ * @param query for the message whose labels to export (empty for "all")
* @param path the path or nothing
*
* @return either the output filename or some error
*/
-Result<std::string> export_labels(const Store& store, const std::string& query="", Option<std::string> path);
+Result<std::string> export_labels(const Store& store,
+ const std::string& query="",
+ Option<std::string> path={});
/**
* Import labels from a file
@@ -170,7 +175,8 @@ Result<std::string> export_labels(const Store& store, const std::string& query="
*
* @return Ok or some error
*/
-Result<void> import_labels(Store&, const std::string& path, bool dry_run, bool quiet, bool verbose);
+Result<void> import_labels(Store&, const std::string& path, bool dry_run,
+ bool quiet, bool verbose);
} // namespace Mux
#endif /*MU_LABELS_CACHE_HH*/
diff --git a/lib/mu-store.cc b/lib/mu-store.cc
index f0a36ed..b8526e9 100644
--- a/lib/mu-store.cc
+++ b/lib/mu-store.cc
@@ -127,7 +127,6 @@ struct Store::Private {
Result<Store::Id> update_message_unlocked(Message& msg, Store::Id docid);
Result<Store::Id> update_message_unlocked(Message& msg, const std::string& old_path);
-
using PathMessage = std::pair<std::string, Message>;
Result<PathMessage> move_message_unlocked(Message&& msg,
Option<const std::string&> target_mdir,
@@ -211,9 +210,29 @@ Store::Private::find_duplicates_unlocked(const Store& store,
}
}
+static void
+reinit_export_labels(const Store& store)
+{
+ // slightly hacky way to get the cache-path...
+ const auto cache_path{canonicalize_filename(
+ join_paths(store.path(), "..")) + "/"};
+
+ if (const auto res =
+ Mu::export_labels(store, "", cache_path); !res)
+ throw Mu::Error(Error::Code::CannotReinit,
+ "cannot re-init; "
+ "failed to export labels: {}",
+ res.error().what())
+ .add_hint("see mu-init(1) for details");
+ else {
+ mu_info("exported labels to: {}", *res);
+ mu_println("exported labels to: {}", *res);
+ }
+}
Store::Store(const std::string& path, Store::Options opts)
- : priv_{std::make_unique<Private>(path, none_of(opts & Store::Options::Writable))}
+ : priv_{std::make_unique<Private>(
+ path, none_of(opts & Store::Options::Writable))}
{
if (none_of(opts & Store::Options::Writable) &&
any_of(opts & Store::Options::ReInit))
@@ -222,12 +241,19 @@ Store::Store(const std::string& path, Store::Options opts)
const auto s_version{config().get<Config::Id::SchemaVersion>()};
if (any_of(opts & Store::Options::ReInit)) {
- /* don't try to recover from version with an incompatible scheme */
+
+ /* export labels, if necessary (throws if there's an error) */
+ if (!label_map().empty())
+ reinit_export_labels(*this);
+
+ /* don't try to recover from version with an incompatible
+ * scheme */
if (s_version < 500)
- throw Mu::Error(Error::Code::CannotReinit,
- "old schema ({}) is too old to re-initialize from",
- s_version).add_hint("Invoke 'mu init' without '--reinit'; "
- "see mu-init(1) for details");
+ throw Mu::Error(
+ Error::Code::CannotReinit,
+ "old schema ({}) is too old to re-initialize from",
+ s_version).add_hint("Invoke 'mu init' without '--reinit'; "
+ "see mu-init(1) for details");
const auto old_root_maildir{root_maildir()};
MemDb mem_db;
diff --git a/lib/utils/mu-utils-file.cc b/lib/utils/mu-utils-file.cc
index bd58102..e9bccb0 100644
--- a/lib/utils/mu-utils-file.cc
+++ b/lib/utils/mu-utils-file.cc
@@ -131,9 +131,6 @@ Mu::remove_directory(const std::string& path)
return Ok();
}
-
-
-
std::string
Mu::runtime_path(Mu::RuntimePath path, const std::string& muhome)
{