mu: Update for new querying APIs

This commit is contained in:
Dirk-Jan C. Binnema 2020-11-28 10:16:43 +02:00
parent e282d80bc0
commit 01ced9a356
11 changed files with 524 additions and 561 deletions

View File

@ -80,25 +80,25 @@ test_cxxflags= \
-DABS_CURDIR=\"${abs_builddir}\" \
-DABS_SRCDIR=\"${abs_srcdir}\"
TEST_PROGS += test-mu-query
test_mu_query_SOURCES= test-mu-query.cc
test_mu_query_CXXFLAGS=$(test_cxxflags)
test_mu_query_LDADD=${top_builddir}/lib/libtestmucommon.la $(CODE_COVERAGE_LIBS)
TEST_PROGS += test-query
test_query_SOURCES= test-mu-query.cc
test_query_CXXFLAGS=$(test_cxxflags)
test_query_LDADD=${top_builddir}/lib/libtestmucommon.la $(CODE_COVERAGE_LIBS)
TEST_PROGS += test-mu-cmd
test_mu_cmd_SOURCES= test-mu-cmd.cc
test_mu_cmd_CXXFLAGS=$(test_cxxflags)
test_mu_cmd_LDADD=${top_builddir}/lib/libtestmucommon.la $(CODE_COVERAGE_LIBS)
TEST_PROGS += test-cmd
test_cmd_SOURCES= test-mu-cmd.cc
test_cmd_CXXFLAGS=$(test_cxxflags)
test_cmd_LDADD=${top_builddir}/lib/libtestmucommon.la $(CODE_COVERAGE_LIBS)
TEST_PROGS += test-mu-cmd-cfind
test_mu_cmd_cfind_SOURCES= test-mu-cmd-cfind.cc
test_mu_cmd_cfind_CXXFLAGS=$(test_cxxflags)
test_mu_cmd_cfind_LDADD=${top_builddir}/lib/libtestmucommon.la $(CODE_COVERAGE_LIBS)
TEST_PROGS += test-cmd-cfind
test_cmd_cfind_SOURCES= test-mu-cmd-cfind.cc
test_cmd_cfind_CXXFLAGS=$(test_cxxflags)
test_cmd_cfind_LDADD=${top_builddir}/lib/libtestmucommon.la $(CODE_COVERAGE_LIBS)
TEST_PROGS += test-mu-threads
test_mu_threads_SOURCES= test-mu-threads.cc
test_mu_threads_CXXFLAGS=$(test_cxxflags)
test_mu_threads_LDADD=${top_builddir}/lib/libtestmucommon.la $(CODE_COVERAGE_LIBS)
TEST_PROGS += test-threads
test_threads_SOURCES= test-mu-threads.cc
test_threads_CXXFLAGS=$(test_cxxflags)
test_threads_LDADD=${top_builddir}/lib/libtestmucommon.la $(CODE_COVERAGE_LIBS)
TESTS=$(TEST_PROGS)
include $(top_srcdir)/aminclude_static.am

View File

@ -35,6 +35,8 @@
#include "utils/mu-str.h"
#include "utils/mu-date.h"
using namespace Mu;
/**
* guess the last name for the given name; clearly,
* this is just a rough guess for setting an initial value.
@ -404,7 +406,7 @@ cfind_params_valid (const MuConfig *opts)
}
MuError
mu_cmd_cfind (const Mu::Store& store, const MuConfig *opts, GError **err)
Mu::mu_cmd_cfind (const 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_CFIND,

View File

@ -22,12 +22,14 @@
#include <stdlib.h>
#include <string.h>
#include "mu-msg.h"
#include "mu-msg-part.h"
#include "mu-msg.hh"
#include "mu-msg-part.hh"
#include "mu-cmd.hh"
#include "utils/mu-util.h"
#include "utils/mu-str.h"
using namespace Mu;
static gboolean
save_part (MuMsg *msg, const char *targetdir, guint partidx, const MuConfig *opts)
@ -393,7 +395,7 @@ check_params (const MuConfig *opts, GError **err)
}
MuError
mu_cmd_extract (const MuConfig *opts, GError **err)
Mu::mu_cmd_extract (const MuConfig *opts, GError **err)
{
int rv;

View File

@ -28,10 +28,10 @@
#include <stdlib.h>
#include <signal.h>
#include "mu-msg.h"
#include "mu-maildir.h"
#include "mu-msg.hh"
#include "mu-maildir.hh"
#include "mu-query-match-deciders.hh"
#include "mu-query.hh"
#include "mu-msg-iter.h"
#include "mu-bookmarks.hh"
#include "mu-runtime.hh"
@ -40,15 +40,24 @@
#include "utils/mu-date.h"
#include "mu-cmd.hh"
#include "mu-threader.hh"
using namespace Mu;
typedef gboolean (OutputFunc) (MuMsg *msg, MuMsgIter *iter,
const MuConfig *opts, GError **err);
struct OutputInfo{
Xapian::docid docid{};
bool is_first{};
bool is_last{};
Option<QueryMatch&> match_info;
};
constexpr auto FirstOutput{OutputInfo{0, true, false}};
constexpr auto LastOutput{OutputInfo{0, false, true}};
using OutputFunc = std::function<bool(MuMsg*, const OutputInfo&,
const MuConfig*, GError**)>;
static gboolean
print_internal (const Query& query, const gchar *expr, gboolean xapian,
print_internal (const Query& query, const std::string& expr, gboolean xapian,
gboolean warn, GError **err)
{
std::cout << query.parse(expr, xapian) << "\n";
@ -75,59 +84,35 @@ sort_field_from_string (const char* fieldstr, GError **err)
return mfid;
}
static MuMsg*
get_message (MuMsgIter *iter, time_t after)
{
MuMsg *msg;
if (mu_msg_iter_is_done (iter))
return NULL;
msg = mu_msg_iter_get_msg_floating (iter);
if (!msg)
return NULL; /* error */
if (!mu_msg_is_readable (msg)) {
mu_msg_iter_next (iter);
return get_message (iter, after);
}
if (after != 0 && after > mu_msg_get_timestamp (msg)) {
mu_msg_iter_next (iter);
return get_message (iter, after);
}
return msg;
}
static MuMsgIter*
static Option<QueryResults>
run_query (const Query& q, const std::string& expr, const MuConfig *opts, GError **err)
{
MuMsgIter *iter;
MuMsgFieldId sortid;
sortid = MU_MSG_FIELD_ID_NONE;
if (opts->sortfield) {
sortid = sort_field_from_string (opts->sortfield, err);
if (sortid == MU_MSG_FIELD_ID_NONE) /* error occurred? */
return FALSE;
return Nothing;
}
Mu::Query::Flags qflags{Query::Flags::None};
Mu::QueryFlags qflags{QueryFlags::None};
if (opts->reverse)
qflags |= Query::Flags::Descending;
qflags |= QueryFlags::Descending;
if (opts->skip_dups)
qflags |= Query::Flags::SkipDups;
qflags |= QueryFlags::SkipDuplicates;
if (opts->include_related)
qflags |= Query::Flags::IncludeRelated;
qflags |= QueryFlags::IncludeRelated;
if (opts->threads)
qflags |= Query::Flags::Threading;
qflags |= QueryFlags::Threading;
return q.run(expr, sortid, qflags, opts->maxnum, err);
return q.run(expr, sortid, qflags, opts->maxnum);
}
static gboolean
exec_cmd (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
exec_cmd (MuMsg *msg, const OutputInfo& info,
const MuConfig *opts, GError **err)
{
gint status;
char *cmdline, *escpath;
@ -170,7 +155,7 @@ resolve_bookmark (const MuConfig *opts, GError **err)
return val;
}
static gchar*
static Option<std::string>
get_query (const MuConfig *opts, GError **err)
{
gchar *query, *bookmarkval;
@ -179,14 +164,14 @@ get_query (const MuConfig *opts, GError **err)
if (!opts->bookmark && !opts->params[1]) {
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
"error in parameters");
return NULL;
return Nothing;
}
bookmarkval = NULL;
if (opts->bookmark) {
bookmarkval = resolve_bookmark (opts, err);
if (!bookmarkval)
return NULL;
return Nothing;
}
query = g_strjoinv (" ", &opts->params[1]);
@ -199,7 +184,10 @@ get_query (const MuConfig *opts, GError **err)
g_free (bookmarkval);
return query;
std::string q{query};
g_free(query);
return q;
}
static Mu::Query
@ -239,10 +227,11 @@ prepare_links (const MuConfig *opts, GError **err)
return TRUE;
}
static gboolean
output_link (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
static bool
output_link (MuMsg *msg, const OutputInfo& info,
const MuConfig *opts, GError **err)
{
if (mu_msg_iter_is_first (iter) && !prepare_links (opts, err))
if (info.is_first && !prepare_links (opts, err))
return FALSE;
return mu_maildir_link (mu_msg_get_path (msg),
@ -377,40 +366,27 @@ print_summary (MuMsg *msg, const MuConfig *opts)
}
static void
thread_indent (MuMsgIter *iter)
thread_indent (const QueryMatch& info)
{
const MuMsgIterThreadInfo *ti;
const char* threadpath;
int i;
gboolean is_root, first_child, empty_parent, is_dup;
ti = mu_msg_iter_get_thread_info (iter);
if (!ti) {
g_printerr ("cannot get thread-info for message %u\n",
mu_msg_iter_get_docid (iter));
return;
}
threadpath = ti->threadpath;
/* fputs (threadpath, stdout); */
/* fputs (" ", stdout); */
is_root = ti->prop & MU_MSG_ITER_THREAD_PROP_ROOT;
first_child = ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD;
empty_parent = ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT;
is_dup = ti->prop & MU_MSG_ITER_THREAD_PROP_DUP;
/* FIXME: count the colons... */
for (i = 0; *threadpath; ++threadpath)
i += (*threadpath == ':') ? 1 : 0;
const auto is_root{any_of(info.flags & QueryMatch::Flags::Root)};
const auto first_child{any_of(info.flags & QueryMatch::Flags::First)};
const auto last_child{any_of(info.flags & QueryMatch::Flags::Last)};
const auto empty_parent{any_of(info.flags & QueryMatch::Flags::Orphan)};
const auto is_dup{any_of(info.flags & QueryMatch::Flags::Duplicate)};
const auto is_related{any_of(info.flags & QueryMatch::Flags::Related)};
/* indent */
while (i --> 0)
fputs (" ", stdout);
for (auto i = info.thread_level; i > 1; --i)
::fputs (" ", stdout);
if (!is_root) {
fputs (first_child ? "`" : "|", stdout);
fputs (empty_parent ? "*> " : is_dup ? "=> " : "-> ", stdout);
if (first_child)
::fputs ("\\", stdout);
else if (last_child)
::fputs ("/", stdout);
else
::fputs (" ", stdout);
::fputs (empty_parent ? "*> " : is_dup ? "=> " : "-> ", stdout);
}
}
@ -446,13 +422,16 @@ output_plain_fields (MuMsg *msg, const char *fields,
}
static gboolean
output_plain (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
output_plain (MuMsg *msg, const OutputInfo& info, const MuConfig *opts, GError **err)
{
if (!msg)
return true;
/* we reuse the color (whatever that may be)
* for message-priority for threads, too */
ansi_color_maybe (MU_MSG_FIELD_ID_PRIO, !opts->nocolor);
if (opts->threads)
thread_indent (iter);
if (opts->threads && info.match_info)
thread_indent (*info.match_info);
output_plain_fields (msg, opts->fields, !opts->nocolor, opts->threads);
@ -511,39 +490,32 @@ to_string (const Mu::Sexp& sexp, bool color, size_t level = 0)
}
static gboolean
output_sexp (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
static bool
output_sexp (MuMsg *msg, const OutputInfo& info, const MuConfig *opts, GError **err)
{
const auto *ti{opts->threads ? mu_msg_iter_get_thread_info (iter) : NULL};
const auto sexp{Mu::msg_to_sexp(msg , mu_msg_iter_get_docid (iter), ti,
MU_MSG_OPTION_HEADERS_ONLY)};
fputs (to_string(sexp, !opts->nocolor).c_str(), stdout);
fputs(msg_to_sexp(msg, 0, {}, MU_MSG_OPTION_HEADERS_ONLY)
.to_sexp_string().c_str(), stdout);
fputs ("\n", stdout);
return TRUE;
return true;
}
static gboolean
output_json (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
static bool
output_json (MuMsg *msg, const OutputInfo& info, const MuConfig *opts, GError **err)
{
const MuMsgIterThreadInfo *ti;
char *s;
if (mu_msg_iter_is_first(iter))
if (info.is_first) {
g_print ("[\n");
return true;
}
ti = opts->threads ? mu_msg_iter_get_thread_info (iter) : NULL;
s = mu_msg_to_json (msg, mu_msg_iter_get_docid (iter),
ti, MU_MSG_OPTION_HEADERS_ONLY);
fputs (s, stdout);
g_free (s);
if (info.is_last) {
g_print("]\n");
return true;
}
if (mu_msg_iter_is_last(iter))
fputs("]\n", stdout);
else
fputs (",\n", stdout);
g_print("%s\n", msg_to_sexp(msg, info.docid, {}, MU_MSG_OPTION_HEADERS_ONLY)
.to_sexp_string().c_str());
return TRUE;
return true;
}
static void
@ -559,12 +531,18 @@ print_attr_xml (const char* elm, const char *str)
g_free (esc);
}
static gboolean
output_xml (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
static bool
output_xml (MuMsg *msg, const OutputInfo& info, const MuConfig *opts, GError **err)
{
if (mu_msg_iter_is_first(iter)) {
if (info.is_first) {
g_print ("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
g_print ("<messages>\n");
return true;
}
if (info.is_last) {
g_print ("</messages>\n");
return true;
}
g_print ("\t<message>\n");
@ -580,13 +558,10 @@ output_xml (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
print_attr_xml ("maildir", mu_msg_get_maildir (msg));
g_print ("\t</message>\n");
if (mu_msg_iter_is_last(iter))
g_print ("</messages>\n");
return TRUE;
return true;
}
static OutputFunc*
static OutputFunc
get_output_func (const MuConfig *opts, GError **err)
{
switch (opts->format) {
@ -603,80 +578,68 @@ get_output_func (const MuConfig *opts, GError **err)
}
}
static gboolean
output_query_results (MuMsgIter *iter, const MuConfig *opts, GError **err)
static bool
output_query_results (const QueryResults& qres, const MuConfig *opts, GError **err)
{
int count;
gboolean rv;
OutputFunc *output_func;
output_func = get_output_func (opts, err);
const auto output_func{get_output_func (opts, err)};
if (!output_func)
return FALSE;
return false;
for (count = 0, rv = TRUE; !mu_msg_iter_is_done(iter);
mu_msg_iter_next (iter)) {
gboolean rv{true};
output_func (NULL, FirstOutput, NULL, NULL);
MuMsg *msg;
for (auto&& item: qres) {
if (count == opts->maxnum)
break;
msg = get_message (iter, opts->after);
auto msg{item.floating_msg()};
if (!msg)
break;
/* { */
/* const char* thread_id; */
/* thread_id = mu_msg_iter_get_thread_id (iter); */
/* g_print ("%s ", thread_id ? thread_id : "<none>"); */
continue;
/* } */
rv = output_func (msg, iter, opts, err);
if (opts->after != 0 && mu_msg_get_timestamp(msg) < opts->after)
continue;
rv = output_func (msg, {item.doc_id(), false, false, item.query_match()},
opts, err);
if (!rv)
break;
else
++count;
}
if (rv && count == 0) {
mu_util_g_set_error (err, MU_ERROR_NO_MATCHES,
"no matches for search expression");
return FALSE;
}
output_func (NULL, LastOutput, NULL, NULL);
return rv;
}
static gboolean
process_query (const Query& q, const gchar *expr, const MuConfig *opts, GError **err)
process_query (const Query& q, const std::string& expr, const MuConfig *opts, GError **err)
{
gboolean rv;
auto iter = run_query (q, expr, opts, err);
if (!iter)
auto qres{run_query (q, expr, opts, err)};
if (!qres)
return FALSE;
rv = output_query_results (iter, opts, err);
mu_msg_iter_destroy (iter);
if (qres->empty()) {
mu_util_g_set_error (err, MU_ERROR_NO_MATCHES,
"no matches for search expression");
return false;
}
return rv;
return output_query_results (*qres, opts, err);
}
static gboolean
execute_find (const Store& store, const MuConfig *opts, GError **err)
{
auto q{get_query_obj (store, err)};
auto expr{get_query (opts, err)};
if (!expr)
return FALSE;
if (opts->format == MU_CONFIG_FORMAT_XQUERY)
return print_internal (q, expr, TRUE, FALSE, err);
return print_internal (q, *expr, TRUE, FALSE, err);
else if (opts->format == MU_CONFIG_FORMAT_MQUERY)
return print_internal (q, expr, FALSE,
return print_internal (q, *expr, FALSE,
opts->verbose, err);
else
return process_query (q, expr, opts, err);
return process_query (q, *expr, opts, err);
}
static gboolean
@ -742,7 +705,7 @@ query_params_valid (const MuConfig *opts, GError **err)
}
MuError
mu_cmd_find (const Store& store, const MuConfig *opts, GError **err)
Mu::mu_cmd_find (const 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_FIND,

View File

@ -30,7 +30,7 @@
#include <signal.h>
#include <unistd.h>
#include "mu-msg.h"
#include "mu-msg.hh"
#include "index/mu-indexer.hh"
#include "mu-store.hh"
#include "mu-runtime.hh"
@ -81,7 +81,7 @@ print_stats (const Indexer::Progress& stats, bool color)
MuError
mu_cmd_index (Mu::Store& store, const MuConfig *opts, GError **err)
Mu::mu_cmd_index (Mu::Store& store, const MuConfig *opts, GError **err)
{
g_return_val_if_fail (opts, MU_ERROR);
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_INDEX, MU_ERROR);

View File

@ -41,6 +41,8 @@
#define COL(C) ((color)?C:"")
using namespace Mu;
static void
print_script (const char *name, const char *oneline, const char *descr,
gboolean color, gboolean verbose)
@ -163,7 +165,7 @@ check_params (const MuConfig *opts, GError **err)
MuError
mu_cmd_script (const MuConfig *opts, GError **err)
Mu::mu_cmd_script (const MuConfig *opts, GError **err)
{
MuScriptInfo *msi;
GSList *scripts;

View File

@ -88,7 +88,7 @@ output_sexp_stdout (Sexp&& sexp)
}
MuError
mu_cmd_server (const MuConfig *opts, GError **err) try {
Mu::mu_cmd_server (const MuConfig *opts, GError **err) try {
Store store{mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), false/*writable*/};
Server server{store, output_sexp_stdout};

View File

@ -29,13 +29,13 @@
#include <errno.h>
#include "mu-msg.h"
#include "mu-msg-part.h"
#include "mu-msg.hh"
#include "mu-msg-part.hh"
#include "mu-cmd.hh"
#include "mu-maildir.h"
#include "mu-maildir.hh"
#include "mu-contacts.hh"
#include "mu-runtime.hh"
#include "mu-flags.h"
#include "mu-flags.hh"
#include "utils/mu-util.h"
#include "utils/mu-str.h"
@ -45,15 +45,13 @@
#define VIEW_TERMINATOR '\f' /* form-feed */
using namespace Mu;
static gboolean
view_msg_sexp (MuMsg *msg, const MuConfig *opts)
{
char *sexp;
sexp = mu_msg_to_sexp (msg, 0, NULL, mu_config_get_msg_options(opts));
fputs (sexp, stdout);
g_free (sexp);
::fputs(msg_to_sexp(msg,0,{}, mu_config_get_msg_options(opts))
.to_sexp_string(). c_str(), stdout);
return TRUE;
}
@ -689,7 +687,7 @@ check_params (const MuConfig *opts, GError **err)
}
MuError
mu_cmd_execute (const MuConfig *opts, GError **err) try
Mu::mu_cmd_execute (const MuConfig *opts, GError **err) try
{
MuError merr;

View File

@ -17,15 +17,14 @@
**
*/
#ifndef __MU_CMD_H__
#define __MU_CMD_H__
#ifndef MU_CMD_HH__
#define MU_CMD_HH__
#include <glib.h>
#include <mu-config.hh>
#include <mu-store.hh>
G_BEGIN_DECLS
namespace Mu {
/**
* execute the 'find' command
*
@ -107,6 +106,7 @@ MuError mu_cmd_index (Mu::Store& store, const MuConfig *opt, GError **err);
*/
MuError mu_cmd_server (const MuConfig *opts, GError **err);
G_END_DECLS
} // namespace Mu
#endif /*__MU_CMD_H__*/

View File

@ -26,14 +26,13 @@
#include "mu-config.hh"
#include "mu-cmd.hh"
#include "mu-msg.h"
using namespace Mu;
static MuConfig MU_CONFIG;
#define color_maybe(C) (MU_CONFIG.nocolor ? "" : (C))
static MuConfigFormat
get_output_format (const char *formatstr)
{
@ -627,7 +626,7 @@ get_help_string (MuConfigCmd cmd, gboolean long_help)
void
mu_config_show_help (MuConfigCmd cmd)
Mu::mu_config_show_help (MuConfigCmd cmd)
{
GOptionContext *ctx;
GOptionGroup *group;
@ -725,14 +724,12 @@ parse_params (int *argcp, char ***argvp, GError **err)
MuConfig*
mu_config_init (int *argcp, char ***argvp, GError **err)
Mu::mu_config_init (int *argcp, char ***argvp, GError **err)
{
g_return_val_if_fail (argcp && argvp, NULL);
memset (&MU_CONFIG, 0, sizeof(MU_CONFIG));
MU_CONFIG.maxnum = -1; /* By default, output all matching entries. */
if (!parse_cmd (argcp, argvp, err))
goto errexit;
@ -757,7 +754,7 @@ errexit:
void
mu_config_uninit (MuConfig *opts)
Mu::mu_config_uninit (MuConfig *opts)
{
if (!opts)
return;
@ -782,7 +779,7 @@ mu_config_uninit (MuConfig *opts)
}
size_t
mu_config_param_num (const MuConfig *opts)
Mu::mu_config_param_num (const MuConfig *opts)
{
size_t n;
@ -794,7 +791,7 @@ mu_config_param_num (const MuConfig *opts)
MuMsgOptions
mu_config_get_msg_options (const MuConfig *muopts)
Mu::mu_config_get_msg_options (const MuConfig *muopts)
{
int opts;

View File

@ -17,21 +17,20 @@
**
*/
#ifndef __MU_CONFIG_H__
#define __MU_CONFIG_H__
#ifndef MU_CONFIG_HH__
#define MU_CONFIG_HH__
#include <glib.h>
#include <sys/types.h> /* for mode_t */
#include <mu-msg-fields.h>
#include <mu-msg.h>
#include <mu-msg.hh>
#include <utils/mu-util.h>
G_BEGIN_DECLS
namespace Mu {
/* env var; if non-empty, color are disabled */
#define MU_NOCOLOR "MU_NOCOLOR"
typedef enum {
MU_CONFIG_FORMAT_UNKNOWN = 0,
@ -257,6 +256,6 @@ MuMsgOptions mu_config_get_msg_options (const MuConfig *opts);
*/
void mu_config_show_help (const MuConfigCmd cmd);
G_END_DECLS
} // namespace Mu.
#endif /*__MU_CONFIG_H__*/