mu: Make commands Result-based

Use Result<void> as the return value, simplifying some code.
This commit is contained in:
Dirk-Jan C. Binnema
2022-05-12 08:48:28 +03:00
parent 34c5ca1627
commit 158117e843
8 changed files with 252 additions and 401 deletions

View File

@ -334,17 +334,17 @@ each_contact(const Mu::Contact& ci, ECData& ecdata)
} }
} }
static MuError static Result<void>
run_cmd_cfind(const Mu::Store& store, run_cmd_cfind(const Mu::Store& store,
const char* pattern, const char* pattern,
gboolean personal, gboolean personal,
time_t after, time_t after,
int maxnum, int maxnum,
const MuConfigFormat format, const MuConfigFormat format,
gboolean color, gboolean color)
GError** err)
{ {
ECData ecdata{}; ECData ecdata{};
GError *err{};
memset(&ecdata, 0, sizeof(ecdata)); memset(&ecdata, 0, sizeof(ecdata));
@ -352,11 +352,10 @@ run_cmd_cfind(const Mu::Store& store,
ecdata.rx = g_regex_new( ecdata.rx = g_regex_new(
pattern, pattern,
(GRegexCompileFlags)(G_REGEX_CASELESS | G_REGEX_OPTIMIZE), (GRegexCompileFlags)(G_REGEX_CASELESS | G_REGEX_OPTIMIZE),
(GRegexMatchFlags)0, (GRegexMatchFlags)0, &err);
err);
if (!ecdata.rx) if (!ecdata.rx)
return MU_ERROR_CONTACTS; return Err(Error::Code::Internal, &err, "invalid cfind regexp");
} }
ecdata.personal = personal; ecdata.personal = personal;
@ -378,17 +377,18 @@ run_cmd_cfind(const Mu::Store& store,
if (ecdata.rx) if (ecdata.rx)
g_regex_unref(ecdata.rx); g_regex_unref(ecdata.rx);
if (ecdata.n == 0) { if (ecdata.n == 0)
g_printerr("no matching contacts found\n"); return Err(Error::Code::ContactNotFound, "no matching contacts found");
return MU_ERROR_NO_MATCHES; else
} return Ok();
return MU_OK;
} }
static gboolean static gboolean
cfind_params_valid(const MuConfig* opts) cfind_params_valid(const MuConfig* opts)
{ {
if (!opts || opts->cmd != MU_CONFIG_CMD_CFIND)
return FALSE;
switch (opts->format) { switch (opts->format) {
case MU_CONFIG_FORMAT_PLAIN: case MU_CONFIG_FORMAT_PLAIN:
case MU_CONFIG_FORMAT_MUTT_ALIAS: case MU_CONFIG_FORMAT_MUTT_ALIAS:
@ -413,29 +413,17 @@ cfind_params_valid(const MuConfig* opts)
return TRUE; return TRUE;
} }
MuError Result<void>
Mu::mu_cmd_cfind(const Mu::Store& store, const MuConfig* opts, GError** err) Mu::mu_cmd_cfind(const Mu::Store& store, const MuConfig* opts)
{ {
g_return_val_if_fail(opts, MU_ERROR_INTERNAL);
g_return_val_if_fail(opts->cmd == MU_CONFIG_CMD_CFIND, MU_ERROR_INTERNAL);
if (!cfind_params_valid(opts)) if (!cfind_params_valid(opts))
throw Mu::Error(Mu::Error::Code::InvalidArgument, return Err(Error::Code::InvalidArgument, "error in parameters");
"invalid parameters"); else
return run_cmd_cfind(store,
auto res = run_cmd_cfind(store, opts->params[1],
opts->params[1], opts->personal,
opts->personal, opts->after,
opts->after, opts->maxnum,
opts->maxnum, opts->format,
opts->format, !opts->nocolor);
!opts->nocolor,
err);
if (res != MU_OK && res != MU_ERROR_NO_MATCHES)
throw Mu::Error(Mu::Error::Code::Internal,
err /*consumes*/,
"error in cfind");
return res;
} }

View File

@ -48,7 +48,7 @@ show_fields(const MuConfig* opts)
Table fields; Table fields;
fields.add_row({"field-name", "alias", "short", "search", fields.add_row({"field-name", "alias", "short", "search",
"value", "example", "description"}); "value", "example query", "description"});
auto disp= [&](std::string_view sv)->std::string { auto disp= [&](std::string_view sv)->std::string {
if (sv.empty()) if (sv.empty())

View File

@ -77,33 +77,20 @@ print_stats(const Indexer::Progress& stats, bool color)
<< "; cleaned-up: " << col.fg(Color::Green) << stats.removed << col.reset(); << "; cleaned-up: " << col.fg(Color::Green) << stats.removed << col.reset();
} }
MuError Result<void>
Mu::mu_cmd_index(Mu::Store& store, const MuConfig* opts, GError** err) Mu::mu_cmd_index(Mu::Store& store, const MuConfig* opts)
{ {
g_return_val_if_fail(opts, MU_ERROR); if (!opts || opts->cmd != MU_CONFIG_CMD_INDEX || opts->params[1])
g_return_val_if_fail(opts->cmd == MU_CONFIG_CMD_INDEX, MU_ERROR); return Err(Error::Code::InvalidArgument, "error in parameters");
/* param[0] == 'index' there should be no param[1] */ if (opts->max_msg_size < 0)
if (opts->params[1]) { return Err(Error::Code::InvalidArgument,
mu_util_g_set_error(err, MU_ERROR_IN_PARAMETERS, "unexpected parameter");
return MU_ERROR;
}
if (opts->max_msg_size < 0) {
mu_util_g_set_error(err,
MU_ERROR_IN_PARAMETERS,
"the maximum message size must be >= 0"); "the maximum message size must be >= 0");
return MU_ERROR;
}
const auto mdir{store.properties().root_maildir}; const auto mdir{store.properties().root_maildir};
if (G_UNLIKELY(access(mdir.c_str(), R_OK) != 0)) { if (G_UNLIKELY(access(mdir.c_str(), R_OK) != 0))
mu_util_g_set_error(err, return Err(Error::Code::File, "'%s' is not readable: %s",
MU_ERROR_FILE, mdir.c_str(), g_strerror(errno));
"'%s' is not readable: %s",
mdir.c_str(),
g_strerror(errno));
return MU_ERROR;
}
MaybeAnsi col{!opts->nocolor}; MaybeAnsi col{!opts->nocolor};
using Color = MaybeAnsi::Color; using Color = MaybeAnsi::Color;
@ -146,5 +133,5 @@ Mu::mu_cmd_index(Mu::Store& store, const MuConfig* opts, GError** err)
std::cout << std::endl; std::cout << std::endl;
} }
return MU_OK; return Ok();
} }

View File

@ -43,20 +43,20 @@ using namespace Mu;
static void static void
print_script(const char* name, print_script(const char* name,
const char* oneline, const char* oneline,
const char* descr, const char* descr,
gboolean color, gboolean color,
gboolean verbose) gboolean verbose)
{ {
g_print("%s%s%s%s%s%s%s%s", g_print("%s%s%s%s%s%s%s%s",
verbose ? "\n" : " * ", verbose ? "\n" : " * ",
COL(MU_COLOR_GREEN), COL(MU_COLOR_GREEN),
name, name,
COL(MU_COLOR_DEFAULT), COL(MU_COLOR_DEFAULT),
oneline ? ": " : "", oneline ? ": " : "",
COL(MU_COLOR_BLUE), COL(MU_COLOR_BLUE),
oneline ? oneline : "", oneline ? oneline : "",
MU_COLOR_DEFAULT); MU_COLOR_DEFAULT);
if (verbose && descr) if (verbose && descr)
g_print("%s%s%s", COL(MU_COLOR_MAGENTA), descr, COL(MU_COLOR_DEFAULT)); g_print("%s%s%s", COL(MU_COLOR_MAGENTA), descr, COL(MU_COLOR_DEFAULT));
@ -109,10 +109,10 @@ get_userpath(const char* muhome)
return g_build_path(G_DIR_SEPARATOR_S, muhome, "scripts", NULL); return g_build_path(G_DIR_SEPARATOR_S, muhome, "scripts", NULL);
else else
return g_build_path(G_DIR_SEPARATOR_S, return g_build_path(G_DIR_SEPARATOR_S,
g_get_user_data_dir(), g_get_user_data_dir(),
"mu", "mu",
"scripts", "scripts",
NULL); NULL);
} }
static GSList* static GSList*
@ -122,9 +122,9 @@ get_script_info_list(const char* muhome, GError** err)
char* userpath; char* userpath;
scripts = mu_script_get_script_info_list(MU_SCRIPTS_DIR, scripts = mu_script_get_script_info_list(MU_SCRIPTS_DIR,
MU_GUILE_EXT, MU_GUILE_EXT,
MU_GUILE_DESCR_PREFIX, MU_GUILE_DESCR_PREFIX,
err); err);
if (err && *err) if (err && *err)
return NULL; return NULL;
@ -158,51 +158,44 @@ get_script_info_list(const char* muhome, GError** err)
return userscripts; /* apparently, scripts was NULL */ return userscripts; /* apparently, scripts was NULL */
} }
static gboolean Mu::Result<void>
check_params(const MuConfig* opts, GError** err) Mu::mu_cmd_script(const MuConfig* opts)
{ {
if (!mu_util_supports(MU_FEATURE_GUILE)) { GError *err{};
mu_util_g_set_error(err, MuScriptInfo* msi;
MU_ERROR_IN_PARAMETERS, GSList* scripts;
"the 'script' command is not available "
"in this version of mu");
return FALSE;
}
return TRUE; if (!mu_util_supports(MU_FEATURE_GUILE))
} return Err(Error::Code::InvalidArgument,
"<script> sub-command not available (requires guile)");
MuError scripts = get_script_info_list(opts->muhome, &err);
Mu::mu_cmd_script(const MuConfig* opts, GError** err) if (err)
{
MuScriptInfo* msi;
GSList* scripts;
g_return_val_if_fail(opts, MU_ERROR_INTERNAL);
g_return_val_if_fail(opts->cmd == MU_CONFIG_CMD_SCRIPT, MU_ERROR_INTERNAL);
if (!check_params(opts, err))
return MU_ERROR;
scripts = get_script_info_list(opts->muhome, err);
if (err && *err)
goto leave; goto leave;
if (g_strcmp0(opts->cmdstr, "script") == 0) { if (g_strcmp0(opts->cmdstr, "script") == 0) {
print_scripts(scripts, !opts->nocolor, opts->verbose, opts->script_params[0], err); print_scripts(scripts, !opts->nocolor, opts->verbose,
opts->script_params[0], &err);
goto leave; goto leave;
} }
msi = mu_script_find_script_with_name(scripts, opts->script); msi = mu_script_find_script_with_name(scripts, opts->script);
if (!msi) { if (!msi) {
mu_util_g_set_error(err, MU_ERROR_SCRIPT_NOT_FOUND, "command or script not found"); mu_util_g_set_error(&err, MU_ERROR_SCRIPT_NOT_FOUND,
"command or script not found");
goto leave; goto leave;
} }
/* do it! */ /* do it! */
mu_script_guile_run(msi, mu_runtime_path(MU_RUNTIME_PATH_CACHE), opts->script_params, err); mu_script_guile_run(msi, mu_runtime_path(MU_RUNTIME_PATH_CACHE),
opts->script_params, &err);
leave: leave:
/* this won't be reached, unless there is some error */ /* this won't be reached, unless there is some error */
mu_script_info_list_destroy(scripts); mu_script_info_list_destroy(scripts);
return (err && *err) ? MU_ERROR : MU_OK;
if (err)
return Err(Error::Code::InvalidArgument, &err,
"error running script");
else
return Ok();
} }

View File

@ -113,12 +113,14 @@ report_error(const Mu::Error& err) noexcept
Server::OutputFlags::Flush); Server::OutputFlags::Flush);
} }
MuError
Mu::mu_cmd_server(const MuConfig* opts, GError** err) Result<void>
try { Mu::mu_cmd_server(const MuConfig* opts) try {
auto store = Store::make(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), Store::Options::Writable);
auto store = Store::make(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB),
Store::Options::Writable);
if (!store) if (!store)
throw store.error(); return Err(store.error());
Server server{*store, output_sexp_stdout}; Server server{*store, output_sexp_stdout};
g_message("created server with store @ %s; maildir @ %s; debug-mode %s", g_message("created server with store @ %s; maildir @ %s; debug-mode %s",
@ -132,7 +134,7 @@ try {
: ""}; : ""};
if (!eval.empty()) { if (!eval.empty()) {
server.invoke(eval); server.invoke(eval);
return MU_OK; return Ok();
} }
// Note, the readline stuff is inactive unless on a tty. // Note, the readline stuff is inactive unless on a tty.
@ -155,15 +157,14 @@ try {
} }
shutdown_readline(); shutdown_readline();
return MU_OK; return Ok();
} catch (const Error& er) { } catch (const Error& er) {
/* note: user-level error, "OK" for mu */ /* note: user-level error, "OK" for mu */
report_error(er); report_error(er);
g_warning("server caught exception: %s", er.what()); g_warning("server caught exception: %s", er.what());
return MU_OK; return Ok();
} catch (...) { } catch (...) {
g_critical("server caught exception"); g_critical("server caught exception");
g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR, "%s", "caught exception"); return Err(Error::Code::Internal, "caught exception");
return MU_ERROR;
} }

View File

@ -75,10 +75,10 @@ get_attach_str(const Message& message, const MuConfig* opts)
return str; return str;
} }
#define color_maybe(C) \ #define color_maybe(C) \
do { \ do { \
if (color) \ if (color) \
fputs((C), stdout); \ fputs((C), stdout); \
} while (0) } while (0)
static void static void
@ -212,129 +212,64 @@ cmd_view(const MuConfig* opts)
return Ok(); return Ok();
} }
static MuError static Mu::Result<void>
cmd_mkdir(const MuConfig* opts, GError** err) cmd_mkdir(const MuConfig* opts)
{ {
int i; int i;
g_return_val_if_fail(opts, MU_ERROR_INTERNAL); if (!opts->params[1])
g_return_val_if_fail(opts->cmd == MU_CONFIG_CMD_MKDIR, MU_ERROR_INTERNAL); return Err(Error::Code::InvalidArgument,
"missing directory parameter");
if (!opts->params[1]) {
mu_util_g_set_error(err, MU_ERROR_IN_PARAMETERS, "missing directory parameter");
return MU_ERROR_IN_PARAMETERS;
}
for (i = 1; opts->params[i]; ++i) { for (i = 1; opts->params[i]; ++i) {
if (auto&& res{maildir_mkdir(opts->params[i], if (auto&& res =
opts->dirmode, FALSE)}; !res) { maildir_mkdir(opts->params[i], opts->dirmode, FALSE); !res)
g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR_FILE, return res;
"%s", res.error().what());
return MU_ERROR_FILE_CANNOT_MKDIR;
}
} }
return MU_OK; return Ok();
} }
static gboolean static Result<void>
check_file_okay(const char* path, gboolean cmd_add) cmd_add(Mu::Store& store, const MuConfig* opts)
{ {
if (!g_path_is_absolute(path)) {
g_printerr("path is not absolute: %s\n", path);
return FALSE;
}
if (cmd_add && access(path, R_OK) != 0) {
g_printerr("path is not readable: %s: %s\n", path, g_strerror(errno));
return FALSE;
}
return TRUE;
}
typedef bool (*ForeachMsgFunc)(Mu::Store& store, const char* path, GError** err);
static MuError
foreach_msg_file(Mu::Store& store, const MuConfig* opts, ForeachMsgFunc foreach_func, GError** err)
{
unsigned u;
gboolean all_ok;
/* note: params[0] will be 'add' */ /* note: params[0] will be 'add' */
if (!opts->params[0] || !opts->params[1]) { if (!opts->params[0] || !opts->params[1])
g_print("usage: mu %s <file> [<files>]\n", return Err(Error::Code::InvalidArgument,
opts->params[0] ? opts->params[0] : "<cmd>"); "expected some files to add");
mu_util_g_set_error(err, MU_ERROR_IN_PARAMETERS, "missing parameters");
return MU_ERROR_IN_PARAMETERS; for (auto u = 1; opts->params[u]; ++u) {
const auto docid{store.add_message(opts->params[u])};
if (!docid)
return Err(docid.error());
else
g_debug("added message @ %s, docid=%u",
opts->params[u], docid.value());
} }
for (u = 1, all_ok = TRUE; opts->params[u]; ++u) { return Ok();
const char* path; }
path = opts->params[u]; static Result<void>
cmd_remove(Mu::Store& store, const MuConfig* opts)
{
/* note: params[0] will be 'remove' */
if (!opts->params[0] || !opts->params[1])
return Err(Error::Code::InvalidArgument,
"expected some files to remove");
if (!check_file_okay(path, TRUE)) { for (auto u = 1; opts->params[u]; ++u) {
all_ok = FALSE;
g_printerr("not a valid message file: %s\n", path);
continue;
}
if (!foreach_func(store, path, err)) { const auto res = store.remove_message(opts->params[u]);
all_ok = FALSE; if (!res)
g_printerr("error with %s: %s\n", return Err(Error::Code::File, "failed to remove %s",
path, opts->params[u]);
(err && *err) ? (*err)->message : "something went wrong"); else
g_clear_error(err); g_debug("removed message @ %s", opts->params[u]);
continue;
}
} }
if (!all_ok) { return Ok();
mu_util_g_set_error(err,
MU_ERROR_XAPIAN_STORE_FAILED,
"%s failed for some message(s)",
opts->params[0]);
return MU_ERROR_XAPIAN_STORE_FAILED;
}
return MU_OK;
}
static bool
add_path_func(Mu::Store& store, const char* path, GError** err)
{
const auto docid{store.add_message(path)};
g_debug("added message @ %s, docid=%u", path, docid.value());
return true;
}
static MuError
cmd_add(Mu::Store& store, const MuConfig* opts, GError** err)
{
g_return_val_if_fail(opts, MU_ERROR_INTERNAL);
g_return_val_if_fail(opts->cmd == MU_CONFIG_CMD_ADD, MU_ERROR_INTERNAL);
return foreach_msg_file(store, opts, add_path_func, err);
}
static bool
remove_path_func(Mu::Store& store, const char* path, GError** err)
{
const auto res = store.remove_message(path);
g_debug("removed %s (%s)", path, res ? "yes" : "no");
return true;
}
static MuError
cmd_remove(Mu::Store& store, const MuConfig* opts, GError** err)
{
g_return_val_if_fail(opts, MU_ERROR_INTERNAL);
g_return_val_if_fail(opts->cmd == MU_CONFIG_CMD_REMOVE, MU_ERROR_INTERNAL);
return foreach_msg_file(store, opts, remove_path_func, err);
} }
@ -466,8 +401,8 @@ cmd_verify(const MuConfig* opts)
"failed to verify one or more signatures"); "failed to verify one or more signatures");
} }
static MuError static Result<void>
cmd_info(const Mu::Store& store, const MuConfig* opts, GError** err) cmd_info(const Mu::Store& store, const MuConfig* opts)
{ {
using namespace tabulate; using namespace tabulate;
@ -510,30 +445,24 @@ cmd_info(const Mu::Store& store, const MuConfig* opts, GError** err)
std::cout << info << '\n'; std::cout << info << '\n';
return MU_OK; return Ok();
} }
static MuError static Result<void>
cmd_init(const MuConfig* opts, GError** err) cmd_init(const MuConfig* opts)
{ {
/* not provided, nor could we find a good default */ /* not provided, nor could we find a good default */
if (!opts->maildir) { if (!opts->maildir)
mu_util_g_set_error(err, return Err(Error::Code::InvalidArgument,
MU_ERROR_IN_PARAMETERS, "missing --maildir parameter and could "
"missing --maildir parameter and could " "not determine default");
"not determine default");
return MU_ERROR_IN_PARAMETERS;
}
if (opts->max_msg_size < 0) { if (opts->max_msg_size < 0)
mu_util_g_set_error(err, return Err(Error::Code::InvalidArgument,
MU_ERROR_IN_PARAMETERS, "invalid value for max-message-size");
"invalid value for max-message-size"); else if (opts->batch_size < 0)
return MU_ERROR_IN_PARAMETERS; return Err(Error::Code::InvalidArgument,
} else if (opts->batch_size < 0) { "invalid value for batch-size");
mu_util_g_set_error(err, MU_ERROR_IN_PARAMETERS, "invalid value for batch-size");
return MU_ERROR_IN_PARAMETERS;
}
Mu::Store::Config conf{}; Mu::Store::Config conf{};
conf.max_message_size = opts->max_msg_size; conf.max_message_size = opts->max_msg_size;
@ -549,14 +478,14 @@ cmd_init(const MuConfig* opts, GError** err)
auto store = Store::make_new(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), auto store = Store::make_new(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB),
opts->maildir, my_addrs, conf); opts->maildir, my_addrs, conf);
if (!store) if (!store)
throw store.error(); return Err(store.error());
if (!opts->quiet) { if (!opts->quiet) {
cmd_info(*store, opts, NULL); cmd_info(*store, opts);
std::cout << "\nstore created; use the 'index' command to fill/update it.\n"; std::cout << "\nstore created; use the 'index' command to fill/update it.\n";
} }
return MU_OK; return Ok();
} }
static Result<void> static Result<void>
@ -579,124 +508,95 @@ show_usage(void)
"more information\n"); "more information\n");
} }
typedef MuError (*readonly_store_func)(const Mu::Store&, const MuConfig*, GError** err);
typedef MuError (*writable_store_func)(Mu::Store&, const MuConfig*, GError** err);
static MuError using ReadOnlyStoreFunc = std::function<Result<void>(const Store&, const MuConfig*)>;
with_readonly_store(readonly_store_func func, const MuConfig* opts, GError** err) using WritableStoreFunc = std::function<Result<void>(Store&, const MuConfig*)>;
static Result<void>
with_readonly_store(const ReadOnlyStoreFunc& func, const MuConfig* opts)
{ {
auto store{Store::make(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB))}; auto store{Store::make(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB))};
if (!store) if (!store)
throw store.error(); return Err(store.error());
return func(*store, opts, err); return func(store.value(), opts);
} }
static MuError static Result<void>
with_writable_store(writable_store_func func, const MuConfig* opts, GError** err) with_writable_store(const WritableStoreFunc func, const MuConfig* opts)
{ {
auto store{Store::make(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), auto store{Store::make(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB),
Store::Options::Writable)}; Store::Options::Writable)};
if (!store) if (!store)
throw store.error(); return Err(store.error());
return func(*store, opts, err); return func(store.value(), opts);
} }
static gboolean Result<void>
check_params(const MuConfig* opts, GError** err) Mu::mu_cmd_execute(const MuConfig* opts) try {
{
if (!opts->params || !opts->params[0]) { /* no command? */
show_usage();
mu_util_g_set_error(err, MU_ERROR_IN_PARAMETERS,
"error in parameters");
return FALSE;
}
return TRUE; if (!opts || !opts->params || !opts->params[0])
} return Err(Error::Code::InvalidArgument, "error in parameters");
MuError
Mu::mu_cmd_execute(const MuConfig* opts, GError** err) try {
MuError merr;
g_return_val_if_fail(opts, MU_ERROR_INTERNAL);
if (!check_params(opts, err))
return MU_G_ERROR_CODE(err);
auto mu_error_from_result = [](auto&& result, GError **gerr) {
if (result)
return MU_OK;
result.error().fill_g_error(gerr);
switch(result.error().code()) {
case Error::Code::NoMatches:
return MU_ERROR_NO_MATCHES;
case Error::Code::UnverifiedSignature:
return MU_ERROR_CRYPTO;
default:
break;
}
return MU_ERROR;
};
switch (opts->cmd) { switch (opts->cmd) {
/* already handled in mu-config.c */ case MU_CONFIG_CMD_HELP: /* already handled in mu-config.c */
case MU_CONFIG_CMD_HELP: return Ok();
return MU_OK;
/* /*
* no store needed * no store needed
*/ */
case MU_CONFIG_CMD_FIELDS: case MU_CONFIG_CMD_FIELDS:
merr = mu_error_from_result(mu_cmd_fields(opts), err); return mu_cmd_fields(opts);
break; case MU_CONFIG_CMD_MKDIR:
case MU_CONFIG_CMD_MKDIR: merr = cmd_mkdir(opts, err); break; return cmd_mkdir(opts);
case MU_CONFIG_CMD_SCRIPT: merr = mu_cmd_script(opts, err); break; case MU_CONFIG_CMD_SCRIPT:
return mu_cmd_script(opts);
case MU_CONFIG_CMD_VIEW: case MU_CONFIG_CMD_VIEW:
merr = mu_error_from_result(cmd_view(opts), err); return cmd_view(opts);
break; case MU_CONFIG_CMD_VERIFY:
case MU_CONFIG_CMD_VERIFY: { return cmd_verify(opts);
merr = mu_error_from_result(cmd_verify(opts), err);
break;
}
case MU_CONFIG_CMD_EXTRACT: case MU_CONFIG_CMD_EXTRACT:
merr = mu_error_from_result(mu_cmd_extract(opts), err); return mu_cmd_extract(opts);
break;
/* /*
* read-only store * read-only store
*/ */
case MU_CONFIG_CMD_CFIND: merr = with_readonly_store(mu_cmd_cfind, opts, err); break; case MU_CONFIG_CMD_CFIND:
case MU_CONFIG_CMD_FIND: return with_readonly_store(mu_cmd_cfind, opts);
merr = mu_error_from_result(cmd_find(opts), err);
break; break;
case MU_CONFIG_CMD_FIND:
return cmd_find(opts);
case MU_CONFIG_CMD_INFO: case MU_CONFIG_CMD_INFO:
merr = with_readonly_store(cmd_info, opts, err); return with_readonly_store(cmd_info, opts);
break; break;
/* writable store */ /* writable store */
case MU_CONFIG_CMD_ADD: merr = with_writable_store(cmd_add, opts, err); break; case MU_CONFIG_CMD_ADD:
case MU_CONFIG_CMD_REMOVE: merr = with_writable_store(cmd_remove, opts, err); break; return with_writable_store(cmd_add, opts);
case MU_CONFIG_CMD_INDEX: merr = with_writable_store(mu_cmd_index, opts, err); break; case MU_CONFIG_CMD_REMOVE:
return with_writable_store(cmd_remove, opts);
case MU_CONFIG_CMD_INDEX:
return with_writable_store(mu_cmd_index, opts);
/* commands instantiate store themselves */ /* commands instantiate store themselves */
case MU_CONFIG_CMD_INIT: merr = cmd_init(opts, err); break; case MU_CONFIG_CMD_INIT:
case MU_CONFIG_CMD_SERVER: merr = mu_cmd_server(opts, err); break; return cmd_init(opts);
case MU_CONFIG_CMD_SERVER:
return mu_cmd_server(opts);
default: merr = MU_ERROR_IN_PARAMETERS; break; default:
show_usage();
return Ok();
} }
return merr;
} catch (const Mu::Error& er) { } catch (const Mu::Error& er) {
g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR, "%s", er.what()); return Err(er);
return MU_ERROR; } catch (const std::runtime_error& re) {
return Err(Error::Code::Internal, "runtime-error: %s", re.what());
} catch (const std::exception& ex) {
return Err(Error::Code::Internal, "error: %s", ex.what());
} catch (...) { } catch (...) {
g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR, "%s", "caught exception"); return Err(Error::Code::Internal, "caught exception");
return MU_ERROR;
} }

View File

@ -60,44 +60,29 @@ Result<void> mu_cmd_fields(const MuConfig* opts);
* @param opts configuration options * @param opts configuration options
* @param err receives error information, or NULL * @param err receives error information, or NULL
* *
* @return MU_OK (0) if the command succeeds, * @return Ok() or some error
* some error code otherwise
*/ */
MuError mu_cmd_script(const MuConfig* opts, GError** err); Result<void> mu_cmd_script(const MuConfig* opts);
/** /**
* execute the cfind command * execute the cfind command
* *
* @param store store object to use * @param store store object to use
* @param opts configuration options * @param opts configuration options
* @param err receives error information, or NULL
* *
* @return MU_OK (0) if the command succeeds, * @return Ok() or some error
* some error code otherwise
*/ */
MuError mu_cmd_cfind(const Mu::Store& store, const MuConfig* opts, GError** err); Result<void> mu_cmd_cfind(const Mu::Store& store, const MuConfig* opts);
/**
* execute some mu command, based on 'opts'
*
* @param opts configuration option
* @param err receives error information, or NULL
*
* @return MU_OK if all went wall, some error code otherwise
*/
MuError mu_cmd_execute(const MuConfig* opts, GError** err);
/** /**
* execute the 'index' command * execute the 'index' command
* *
* @param store store object to use * @param store store object to use
* @param opts configuration options * @param opts configuration options
* @param err receives error information, or NULL
* *
* @return MU_OK (0) if the command succeeded, * @return Ok() or some error
* some error code otherwise
*/ */
MuError mu_cmd_index(Mu::Store& store, const MuConfig* opt, GError** err); Result<void> mu_cmd_index(Mu::Store& store, const MuConfig* opt);
/** /**
* execute the server command * execute the server command
@ -106,7 +91,17 @@ MuError mu_cmd_index(Mu::Store& store, const MuConfig* opt, GError** err);
* *
* @return MU_OK (0) if the command succeeds, some error code otherwise * @return MU_OK (0) if the command succeeds, some error code otherwise
*/ */
MuError mu_cmd_server(const MuConfig* opts, GError** err); Result<void> mu_cmd_server(const MuConfig* opts);
/**
* execute some mu command, based on 'opts'
*
* @param opts configuration option
* @param err receives error information, or NULL
*
* @return Ok() or some error
*/
Result<void> mu_cmd_execute(const MuConfig* opts);
} // namespace Mu } // namespace Mu

View File

@ -44,90 +44,77 @@ show_version(void)
g_print("%s\n", blurb); g_print("%s\n", blurb);
} }
static void static int
handle_error(MuConfig* conf, MuError merr, GError** err) handle_result(const Result<void>& res, MuConfig* conf)
{ {
if (!(err && *err)) if (res)
return; return 0;
using Color = MaybeAnsi::Color; using Color = MaybeAnsi::Color;
MaybeAnsi col{conf ? !conf->nocolor : false}; MaybeAnsi col{conf ? !conf->nocolor : false};
if (*err) // show the error and some help, but not if it's only a softerror.
if (!res.error().is_soft_error()) {
std::cerr << col.fg(Color::Red) << "error" << col.reset() << ": " std::cerr << col.fg(Color::Red) << "error" << col.reset() << ": "
<< col.fg(Color::BrightYellow) << col.fg(Color::BrightYellow)
<< ((*err) ? (*err)->message : "something when wrong") << "\n"; << res.error().what() << "something went wrong" << "\n";
} else
std::cerr << col.fg(Color::BrightBlue) << res.error().what() << '\n';
std::cerr << col.fg(Color::Green); std::cerr << col.fg(Color::Green);
switch ((*err)->code) { // perhaps give some useful hint on how to solve it.
case MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK: switch (res.error().code()) {
std::cerr << "Maybe mu is already running?\n"; case Error::Code::InvalidArgument:
break;
case MU_ERROR_XAPIAN_NEEDS_REINDEX:
std::cerr << "Database needs (re)indexing.\n"
<< "try 'mu index' "
<< "(see mu-index(1) for details)\n";
return;
case MU_ERROR_IN_PARAMETERS:
if (conf && mu_config_cmd_is_valid(conf->cmd)) if (conf && mu_config_cmd_is_valid(conf->cmd))
mu_config_show_help(conf->cmd); mu_config_show_help(conf->cmd);
break; break;
case MU_ERROR_SCRIPT_NOT_FOUND: case Error::Code::SchemaMismatch:
std::cerr << "See the mu manpage for commands, or " std::cerr << "Please (re)initialize mu with 'mu init' "
<< "'mu script' for the scripts\n"; << "see mu-init(1) for details\n";
break; break;
case MU_ERROR_XAPIAN_CANNOT_OPEN: default:
std::cerr << "Please (re)initialize mu with 'mu init' " break; /* nothing to do */
<< "see mu-init(1) for details\n";
return;
case MU_ERROR_XAPIAN_SCHEMA_MISMATCH:
std::cerr << "Please (re)initialize mu with 'mu init' "
<< "see mu-init(1) for details\n";
return;
default: break; /* nothing to do */
} }
std::cerr << col.reset(); std::cerr << col.reset();
return res.error().exit_code();
} }
int int
main(int argc, char* argv[]) main(int argc, char* argv[])
{ {
GError* err; int rv{};
MuError rv; MuConfig *conf{};
MuConfig* conf; GError* err{};
using Color = MaybeAnsi::Color;
MaybeAnsi col{conf ? !conf->nocolor : false};
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
err = NULL;
rv = MU_OK;
conf = mu_config_init(&argc, &argv, &err); conf = mu_config_init(&argc, &argv, &err);
if (!conf) { if (!conf) {
rv = err ? (MuError)err->code : MU_ERROR; std::cerr << col.fg(Color::Red) << "error" << col.reset() << ": "
<< col.fg(Color::BrightYellow)
<< (err ? err->message : "something went wrong") << "\n";
rv = 1;
goto cleanup; goto cleanup;
} else if (conf->version) { } else if (conf->version) {
show_version(); show_version();
goto cleanup; goto cleanup;
} } else if (conf->cmd == MU_CONFIG_CMD_NONE) /* nothing to do */
goto cleanup;
/* nothing to do */ else if (!mu_runtime_init(conf->muhome, PACKAGE_NAME, conf->debug)) {
if (conf->cmd == MU_CONFIG_CMD_NONE) std::cerr << col.fg(Color::Red) << "error initializing mu\n"
return 0; << col.reset();
rv = 2;
if (!mu_runtime_init(conf->muhome, PACKAGE_NAME, conf->debug)) { } else
mu_config_uninit(conf); rv = handle_result(mu_cmd_execute(conf), conf);
return 1;
}
rv = mu_cmd_execute(conf, &err);
cleanup: cleanup:
handle_error(conf, rv, &err);
g_clear_error(&err); g_clear_error(&err);
mu_config_uninit(conf); mu_config_uninit(conf);
mu_runtime_uninit(); mu_runtime_uninit();