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

View File

@ -48,7 +48,7 @@ show_fields(const MuConfig* opts)
Table fields;
fields.add_row({"field-name", "alias", "short", "search",
"value", "example", "description"});
"value", "example query", "description"});
auto disp= [&](std::string_view sv)->std::string {
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();
}
MuError
Mu::mu_cmd_index(Mu::Store& store, const MuConfig* opts, GError** err)
Result<void>
Mu::mu_cmd_index(Mu::Store& store, const MuConfig* opts)
{
g_return_val_if_fail(opts, MU_ERROR);
g_return_val_if_fail(opts->cmd == MU_CONFIG_CMD_INDEX, MU_ERROR);
if (!opts || opts->cmd != MU_CONFIG_CMD_INDEX || opts->params[1])
return Err(Error::Code::InvalidArgument, "error in parameters");
/* param[0] == 'index' there should be no param[1] */
if (opts->params[1]) {
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,
if (opts->max_msg_size < 0)
return Err(Error::Code::InvalidArgument,
"the maximum message size must be >= 0");
return MU_ERROR;
}
const auto mdir{store.properties().root_maildir};
if (G_UNLIKELY(access(mdir.c_str(), R_OK) != 0)) {
mu_util_g_set_error(err,
MU_ERROR_FILE,
"'%s' is not readable: %s",
mdir.c_str(),
g_strerror(errno));
return MU_ERROR;
}
if (G_UNLIKELY(access(mdir.c_str(), R_OK) != 0))
return Err(Error::Code::File, "'%s' is not readable: %s",
mdir.c_str(), g_strerror(errno));
MaybeAnsi col{!opts->nocolor};
using Color = MaybeAnsi::Color;
@ -146,5 +133,5 @@ Mu::mu_cmd_index(Mu::Store& store, const MuConfig* opts, GError** err)
std::cout << std::endl;
}
return MU_OK;
return Ok();
}

View File

@ -43,20 +43,20 @@ using namespace Mu;
static void
print_script(const char* name,
const char* oneline,
const char* descr,
gboolean color,
gboolean verbose)
const char* oneline,
const char* descr,
gboolean color,
gboolean verbose)
{
g_print("%s%s%s%s%s%s%s%s",
verbose ? "\n" : " * ",
COL(MU_COLOR_GREEN),
name,
COL(MU_COLOR_DEFAULT),
oneline ? ": " : "",
COL(MU_COLOR_BLUE),
oneline ? oneline : "",
MU_COLOR_DEFAULT);
verbose ? "\n" : " * ",
COL(MU_COLOR_GREEN),
name,
COL(MU_COLOR_DEFAULT),
oneline ? ": " : "",
COL(MU_COLOR_BLUE),
oneline ? oneline : "",
MU_COLOR_DEFAULT);
if (verbose && descr)
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);
else
return g_build_path(G_DIR_SEPARATOR_S,
g_get_user_data_dir(),
"mu",
"scripts",
NULL);
g_get_user_data_dir(),
"mu",
"scripts",
NULL);
}
static GSList*
@ -122,9 +122,9 @@ get_script_info_list(const char* muhome, GError** err)
char* userpath;
scripts = mu_script_get_script_info_list(MU_SCRIPTS_DIR,
MU_GUILE_EXT,
MU_GUILE_DESCR_PREFIX,
err);
MU_GUILE_EXT,
MU_GUILE_DESCR_PREFIX,
err);
if (err && *err)
return NULL;
@ -158,51 +158,44 @@ get_script_info_list(const char* muhome, GError** err)
return userscripts; /* apparently, scripts was NULL */
}
static gboolean
check_params(const MuConfig* opts, GError** err)
Mu::Result<void>
Mu::mu_cmd_script(const MuConfig* opts)
{
if (!mu_util_supports(MU_FEATURE_GUILE)) {
mu_util_g_set_error(err,
MU_ERROR_IN_PARAMETERS,
"the 'script' command is not available "
"in this version of mu");
return FALSE;
}
GError *err{};
MuScriptInfo* msi;
GSList* scripts;
return TRUE;
}
if (!mu_util_supports(MU_FEATURE_GUILE))
return Err(Error::Code::InvalidArgument,
"<script> sub-command not available (requires guile)");
MuError
Mu::mu_cmd_script(const MuConfig* opts, GError** 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)
scripts = get_script_info_list(opts->muhome, &err);
if (err)
goto leave;
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;
}
msi = mu_script_find_script_with_name(scripts, opts->script);
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;
}
/* 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:
/* this won't be reached, unless there is some error */
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);
}
MuError
Mu::mu_cmd_server(const MuConfig* opts, GError** err)
try {
auto store = Store::make(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), Store::Options::Writable);
Result<void>
Mu::mu_cmd_server(const MuConfig* opts) try {
auto store = Store::make(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB),
Store::Options::Writable);
if (!store)
throw store.error();
return Err(store.error());
Server server{*store, output_sexp_stdout};
g_message("created server with store @ %s; maildir @ %s; debug-mode %s",
@ -132,7 +134,7 @@ try {
: ""};
if (!eval.empty()) {
server.invoke(eval);
return MU_OK;
return Ok();
}
// Note, the readline stuff is inactive unless on a tty.
@ -155,15 +157,14 @@ try {
}
shutdown_readline();
return MU_OK;
return Ok();
} catch (const Error& er) {
/* note: user-level error, "OK" for mu */
report_error(er);
g_warning("server caught exception: %s", er.what());
return MU_OK;
return Ok();
} catch (...) {
g_critical("server caught exception");
g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR, "%s", "caught exception");
return MU_ERROR;
return Err(Error::Code::Internal, "caught exception");
}

View File

@ -75,10 +75,10 @@ get_attach_str(const Message& message, const MuConfig* opts)
return str;
}
#define color_maybe(C) \
do { \
if (color) \
fputs((C), stdout); \
#define color_maybe(C) \
do { \
if (color) \
fputs((C), stdout); \
} while (0)
static void
@ -212,129 +212,64 @@ cmd_view(const MuConfig* opts)
return Ok();
}
static MuError
cmd_mkdir(const MuConfig* opts, GError** err)
static Mu::Result<void>
cmd_mkdir(const MuConfig* opts)
{
int i;
g_return_val_if_fail(opts, MU_ERROR_INTERNAL);
g_return_val_if_fail(opts->cmd == MU_CONFIG_CMD_MKDIR, MU_ERROR_INTERNAL);
if (!opts->params[1]) {
mu_util_g_set_error(err, MU_ERROR_IN_PARAMETERS, "missing directory parameter");
return MU_ERROR_IN_PARAMETERS;
}
if (!opts->params[1])
return Err(Error::Code::InvalidArgument,
"missing directory parameter");
for (i = 1; opts->params[i]; ++i) {
if (auto&& res{maildir_mkdir(opts->params[i],
opts->dirmode, FALSE)}; !res) {
g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR_FILE,
"%s", res.error().what());
return MU_ERROR_FILE_CANNOT_MKDIR;
}
if (auto&& res =
maildir_mkdir(opts->params[i], opts->dirmode, FALSE); !res)
return res;
}
return MU_OK;
return Ok();
}
static gboolean
check_file_okay(const char* path, gboolean cmd_add)
static Result<void>
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' */
if (!opts->params[0] || !opts->params[1]) {
g_print("usage: mu %s <file> [<files>]\n",
opts->params[0] ? opts->params[0] : "<cmd>");
mu_util_g_set_error(err, MU_ERROR_IN_PARAMETERS, "missing parameters");
return MU_ERROR_IN_PARAMETERS;
if (!opts->params[0] || !opts->params[1])
return Err(Error::Code::InvalidArgument,
"expected some files to add");
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) {
const char* path;
return Ok();
}
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)) {
all_ok = FALSE;
g_printerr("not a valid message file: %s\n", path);
continue;
}
for (auto u = 1; opts->params[u]; ++u) {
if (!foreach_func(store, path, err)) {
all_ok = FALSE;
g_printerr("error with %s: %s\n",
path,
(err && *err) ? (*err)->message : "something went wrong");
g_clear_error(err);
continue;
}
const auto res = store.remove_message(opts->params[u]);
if (!res)
return Err(Error::Code::File, "failed to remove %s",
opts->params[u]);
else
g_debug("removed message @ %s", opts->params[u]);
}
if (!all_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);
return Ok();
}
@ -466,8 +401,8 @@ cmd_verify(const MuConfig* opts)
"failed to verify one or more signatures");
}
static MuError
cmd_info(const Mu::Store& store, const MuConfig* opts, GError** err)
static Result<void>
cmd_info(const Mu::Store& store, const MuConfig* opts)
{
using namespace tabulate;
@ -510,30 +445,24 @@ cmd_info(const Mu::Store& store, const MuConfig* opts, GError** err)
std::cout << info << '\n';
return MU_OK;
return Ok();
}
static MuError
cmd_init(const MuConfig* opts, GError** err)
static Result<void>
cmd_init(const MuConfig* opts)
{
/* not provided, nor could we find a good default */
if (!opts->maildir) {
mu_util_g_set_error(err,
MU_ERROR_IN_PARAMETERS,
"missing --maildir parameter and could "
"not determine default");
return MU_ERROR_IN_PARAMETERS;
}
if (!opts->maildir)
return Err(Error::Code::InvalidArgument,
"missing --maildir parameter and could "
"not determine default");
if (opts->max_msg_size < 0) {
mu_util_g_set_error(err,
MU_ERROR_IN_PARAMETERS,
"invalid value for max-message-size");
return MU_ERROR_IN_PARAMETERS;
} else if (opts->batch_size < 0) {
mu_util_g_set_error(err, MU_ERROR_IN_PARAMETERS, "invalid value for batch-size");
return MU_ERROR_IN_PARAMETERS;
}
if (opts->max_msg_size < 0)
return Err(Error::Code::InvalidArgument,
"invalid value for max-message-size");
else if (opts->batch_size < 0)
return Err(Error::Code::InvalidArgument,
"invalid value for batch-size");
Mu::Store::Config conf{};
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),
opts->maildir, my_addrs, conf);
if (!store)
throw store.error();
return Err(store.error());
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";
}
return MU_OK;
return Ok();
}
static Result<void>
@ -579,124 +508,95 @@ show_usage(void)
"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
with_readonly_store(readonly_store_func func, const MuConfig* opts, GError** err)
using ReadOnlyStoreFunc = std::function<Result<void>(const Store&, const MuConfig*)>;
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))};
if (!store)
throw store.error();
return Err(store.error());
return func(*store, opts, err);
return func(store.value(), opts);
}
static MuError
with_writable_store(writable_store_func func, const MuConfig* opts, GError** err)
static Result<void>
with_writable_store(const WritableStoreFunc func, const MuConfig* opts)
{
auto store{Store::make(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB),
Store::Options::Writable)};
if (!store)
throw store.error();
return Err(store.error());
return func(*store, opts, err);
return func(store.value(), opts);
}
static gboolean
check_params(const MuConfig* opts, GError** err)
{
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;
}
Result<void>
Mu::mu_cmd_execute(const MuConfig* opts) try {
return TRUE;
}
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;
};
if (!opts || !opts->params || !opts->params[0])
return Err(Error::Code::InvalidArgument, "error in parameters");
switch (opts->cmd) {
/* already handled in mu-config.c */
case MU_CONFIG_CMD_HELP:
return MU_OK;
case MU_CONFIG_CMD_HELP: /* already handled in mu-config.c */
return Ok();
/*
* no store needed
*/
case MU_CONFIG_CMD_FIELDS:
merr = mu_error_from_result(mu_cmd_fields(opts), err);
break;
case MU_CONFIG_CMD_MKDIR: merr = cmd_mkdir(opts, err); break;
case MU_CONFIG_CMD_SCRIPT: merr = mu_cmd_script(opts, err); break;
return mu_cmd_fields(opts);
case MU_CONFIG_CMD_MKDIR:
return cmd_mkdir(opts);
case MU_CONFIG_CMD_SCRIPT:
return mu_cmd_script(opts);
case MU_CONFIG_CMD_VIEW:
merr = mu_error_from_result(cmd_view(opts), err);
break;
case MU_CONFIG_CMD_VERIFY: {
merr = mu_error_from_result(cmd_verify(opts), err);
break;
}
return cmd_view(opts);
case MU_CONFIG_CMD_VERIFY:
return cmd_verify(opts);
case MU_CONFIG_CMD_EXTRACT:
merr = mu_error_from_result(mu_cmd_extract(opts), err);
break;
return mu_cmd_extract(opts);
/*
* read-only store
*/
case MU_CONFIG_CMD_CFIND: merr = with_readonly_store(mu_cmd_cfind, opts, err); break;
case MU_CONFIG_CMD_FIND:
merr = mu_error_from_result(cmd_find(opts), err);
case MU_CONFIG_CMD_CFIND:
return with_readonly_store(mu_cmd_cfind, opts);
break;
case MU_CONFIG_CMD_FIND:
return cmd_find(opts);
case MU_CONFIG_CMD_INFO:
merr = with_readonly_store(cmd_info, opts, err);
return with_readonly_store(cmd_info, opts);
break;
/* writable store */
case MU_CONFIG_CMD_ADD: merr = with_writable_store(cmd_add, opts, err); break;
case MU_CONFIG_CMD_REMOVE: merr = with_writable_store(cmd_remove, opts, err); break;
case MU_CONFIG_CMD_INDEX: merr = with_writable_store(mu_cmd_index, opts, err); break;
case MU_CONFIG_CMD_ADD:
return with_writable_store(cmd_add, opts);
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 */
case MU_CONFIG_CMD_INIT: merr = cmd_init(opts, err); break;
case MU_CONFIG_CMD_SERVER: merr = mu_cmd_server(opts, err); break;
case MU_CONFIG_CMD_INIT:
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) {
g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR, "%s", er.what());
return MU_ERROR;
return Err(er);
} 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 (...) {
g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR, "%s", "caught exception");
return MU_ERROR;
return Err(Error::Code::Internal, "caught exception");
}

View File

@ -60,44 +60,29 @@ Result<void> mu_cmd_fields(const MuConfig* opts);
* @param opts configuration options
* @param err receives error information, or NULL
*
* @return MU_OK (0) if the command succeeds,
* some error code otherwise
* @return Ok() or some error
*/
MuError mu_cmd_script(const MuConfig* opts, GError** err);
Result<void> mu_cmd_script(const MuConfig* opts);
/**
* execute the cfind command
*
* @param store store object to use
* @param opts configuration options
* @param err receives error information, or NULL
*
* @return MU_OK (0) if the command succeeds,
* some error code otherwise
* @return Ok() or some error
*/
MuError mu_cmd_cfind(const Mu::Store& store, const MuConfig* opts, GError** err);
/**
* 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);
Result<void> mu_cmd_cfind(const Mu::Store& store, const MuConfig* opts);
/**
* execute the 'index' command
*
* @param store store object to use
* @param opts configuration options
* @param err receives error information, or NULL
*
* @return MU_OK (0) if the command succeeded,
* some error code otherwise
* @return Ok() or some error
*/
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
@ -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
*/
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

View File

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