mirror of https://github.com/djcb/mu.git
lib: replace mu-bookmarks with mu-query-macros
And add some unit tests.
This commit is contained in:
parent
e290158bcd
commit
8287b9802e
|
@ -27,7 +27,7 @@ lib_mu=static_library(
|
|||
'mu-store.cc',
|
||||
'mu-xapian-db.cc',
|
||||
# querying
|
||||
'mu-bookmarks.cc',
|
||||
'mu-query-macros.cc',
|
||||
'mu-query-match-deciders.cc',
|
||||
'mu-query-parser.cc',
|
||||
'mu-query-processor.cc',
|
||||
|
@ -114,6 +114,13 @@ test('test-config',
|
|||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, lib_mu_dep]))
|
||||
|
||||
test('test-query-macros',
|
||||
executable('test-query-macros',
|
||||
'mu-query-macros.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [lib_mu_dep]))
|
||||
|
||||
test('test-query-processor',
|
||||
executable('test-query-processor',
|
||||
'mu-query-processor.cc',
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
** Copyright (C) 2010-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify it
|
||||
** under the terms of the GNU General Public License as published by the
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include "mu-bookmarks.hh"
|
||||
|
||||
#define MU_BOOKMARK_GROUP "mu"
|
||||
|
||||
struct MuBookmarks {
|
||||
char* _bmpath;
|
||||
GHashTable* _hash;
|
||||
};
|
||||
|
||||
static void
|
||||
fill_hash(GHashTable* hash, GKeyFile* kfile)
|
||||
{
|
||||
gchar **keys, **cur;
|
||||
|
||||
keys = g_key_file_get_keys(kfile, MU_BOOKMARK_GROUP, NULL, NULL);
|
||||
if (!keys)
|
||||
return;
|
||||
|
||||
for (cur = keys; *cur; ++cur) {
|
||||
gchar* val;
|
||||
val = g_key_file_get_string(kfile, MU_BOOKMARK_GROUP, *cur, NULL);
|
||||
if (val)
|
||||
g_hash_table_insert(hash, *cur, val);
|
||||
}
|
||||
|
||||
/* don't use g_strfreev, because we put them in the hash table;
|
||||
* only free the gchar** itself */
|
||||
g_free(keys);
|
||||
}
|
||||
|
||||
static GHashTable*
|
||||
create_hash_from_key_file(const gchar* bmpath)
|
||||
{
|
||||
GKeyFile* kfile;
|
||||
GHashTable* hash;
|
||||
|
||||
kfile = g_key_file_new();
|
||||
|
||||
if (!g_key_file_load_from_file(kfile, bmpath, G_KEY_FILE_NONE, NULL)) {
|
||||
g_key_file_free(kfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
||||
fill_hash(hash, kfile);
|
||||
|
||||
g_key_file_free(kfile);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
MuBookmarks*
|
||||
mu_bookmarks_new(const gchar* bmpath)
|
||||
{
|
||||
MuBookmarks* bookmarks;
|
||||
GHashTable* hash;
|
||||
|
||||
g_return_val_if_fail(bmpath, NULL);
|
||||
|
||||
hash = create_hash_from_key_file(bmpath);
|
||||
if (!hash)
|
||||
return NULL;
|
||||
|
||||
bookmarks = g_new(MuBookmarks, 1);
|
||||
|
||||
bookmarks->_bmpath = g_strdup(bmpath);
|
||||
bookmarks->_hash = hash;
|
||||
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
void
|
||||
mu_bookmarks_destroy(MuBookmarks* bm)
|
||||
{
|
||||
if (!bm)
|
||||
return;
|
||||
|
||||
g_free(bm->_bmpath);
|
||||
g_hash_table_destroy(bm->_hash);
|
||||
g_free(bm);
|
||||
}
|
||||
|
||||
const gchar*
|
||||
mu_bookmarks_lookup(MuBookmarks* bm, const gchar* name)
|
||||
{
|
||||
g_return_val_if_fail(bm, NULL);
|
||||
g_return_val_if_fail(name, NULL);
|
||||
|
||||
return (const char*)g_hash_table_lookup(bm->_hash, name);
|
||||
}
|
||||
|
||||
struct _BMData {
|
||||
MuBookmarksForeachFunc _func;
|
||||
gpointer _user_data;
|
||||
};
|
||||
typedef struct _BMData BMData;
|
||||
|
||||
static void
|
||||
each_bookmark(const gchar* key, const gchar* val, BMData* bmdata)
|
||||
{
|
||||
bmdata->_func(key, val, bmdata->_user_data);
|
||||
}
|
||||
|
||||
void
|
||||
mu_bookmarks_foreach(MuBookmarks* bm, MuBookmarksForeachFunc func, gpointer user_data)
|
||||
{
|
||||
BMData bmdata;
|
||||
|
||||
g_return_if_fail(bm);
|
||||
g_return_if_fail(func);
|
||||
|
||||
bmdata._func = func;
|
||||
bmdata._user_data = user_data;
|
||||
|
||||
g_hash_table_foreach(bm->_hash, (GHFunc)each_bookmark, &bmdata);
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
** Copyright (C) 2008-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify it
|
||||
** under the terms of the GNU General Public License as published by the
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef MU_BOOKMARKS_HH__
|
||||
#define MU_BOOKMARKS_HH__
|
||||
|
||||
#include <glib.h>
|
||||
/**
|
||||
* @addtogroup MuBookmarks
|
||||
* Functions for dealing with bookmarks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*! \struct MuBookmarks
|
||||
* \brief Opaque structure representing a sequence of bookmarks
|
||||
*/
|
||||
struct MuBookmarks;
|
||||
|
||||
/**
|
||||
* create a new bookmarks object. when it's no longer needed, use
|
||||
* mu_bookmarks_destroy
|
||||
*
|
||||
* @param bmpath path to the bookmarks file
|
||||
*
|
||||
* @return a new BookMarks object, or NULL in case of error
|
||||
*/
|
||||
MuBookmarks* mu_bookmarks_new(const gchar* bmpath) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* destroy a bookmarks object
|
||||
*
|
||||
* @param bm a bookmarks object, or NULL
|
||||
*/
|
||||
void mu_bookmarks_destroy(MuBookmarks* bm);
|
||||
|
||||
/**
|
||||
* get the value for some bookmark
|
||||
*
|
||||
* @param bm a valid bookmarks object
|
||||
* @param name name of the bookmark to retrieve
|
||||
*
|
||||
* @return the value of the bookmark or NULL in case in error, e.g. if
|
||||
* the bookmark was not found
|
||||
*/
|
||||
const gchar* mu_bookmarks_lookup(MuBookmarks* bm, const gchar* name);
|
||||
|
||||
typedef void (*MuBookmarksForeachFunc)(const gchar* key, const gchar* val, gpointer user_data);
|
||||
|
||||
/**
|
||||
* call a function for each bookmark
|
||||
*
|
||||
* @param bm a valid bookmarks object
|
||||
* @param func a callback function to be called for each bookmarks
|
||||
* @param user_data a user pointer passed to the callback
|
||||
*/
|
||||
void mu_bookmarks_foreach(MuBookmarks* bm, MuBookmarksForeachFunc func, gpointer user_data);
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /*__MU_BOOKMARKS_H__*/
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
** Copyright (C) 2023 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify it
|
||||
** under the terms of the GNU General Public License as published by the
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#include "mu-query-macros.hh"
|
||||
|
||||
#include <glib.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "utils/mu-utils.hh"
|
||||
|
||||
using namespace Mu;
|
||||
|
||||
constexpr auto MU_BOOKMARK_GROUP = "mu";
|
||||
|
||||
struct QueryMacros::Private {
|
||||
Private(const Config& conf): conf_{conf} {}
|
||||
|
||||
|
||||
Result<void> import_key_file(GKeyFile *kfile);
|
||||
|
||||
const Config& conf_;
|
||||
std::unordered_map<std::string, std::string> macros_{};
|
||||
};
|
||||
|
||||
Result<void>
|
||||
QueryMacros::Private::import_key_file(GKeyFile *kfile)
|
||||
{
|
||||
if (!kfile)
|
||||
return Err(Error::Code::InvalidArgument, "invalid key-file");
|
||||
|
||||
GError *err{};
|
||||
size_t num{};
|
||||
gchar **keys{g_key_file_get_keys(kfile, MU_BOOKMARK_GROUP, &num, &err)};
|
||||
if (!keys)
|
||||
return Err(Error::Code::File, &err/*cons*/,"failed to read keys");
|
||||
|
||||
for (auto key = keys; key && *key; ++key) {
|
||||
|
||||
auto rawval{g_key_file_get_string(kfile, MU_BOOKMARK_GROUP, *key, &err)};
|
||||
if (!rawval) {
|
||||
g_strfreev(keys);
|
||||
return Err(Error::Code::File, &err/*cons*/,"failed to read key '{}'", *key);
|
||||
}
|
||||
|
||||
auto val{to_string_gchar(std::move(rawval))};
|
||||
macros_.erase(val); // we want to replace
|
||||
macros_.emplace(std::string(*key), std::move(val));
|
||||
++num;
|
||||
}
|
||||
|
||||
g_strfreev(keys);
|
||||
mu_debug("imported {} query macro(s); total {}", num, macros_.size());
|
||||
return Ok();
|
||||
}
|
||||
|
||||
QueryMacros::QueryMacros(const Config& conf):
|
||||
priv_{std::make_unique<Private>(conf)} {}
|
||||
|
||||
QueryMacros::~QueryMacros() = default;
|
||||
|
||||
Result<void>
|
||||
QueryMacros::load_bookmarks(const std::string& path)
|
||||
{
|
||||
GError *err{};
|
||||
GKeyFile *kfile{g_key_file_new()};
|
||||
if (!g_key_file_load_from_file(kfile, path.c_str(), G_KEY_FILE_NONE, &err)) {
|
||||
g_key_file_unref(kfile);
|
||||
return Err(Error::Code::File, &err/*cons*/,
|
||||
"failed to read bookmarks from {}", path);
|
||||
}
|
||||
|
||||
auto&& res = priv_->import_key_file(kfile);
|
||||
g_key_file_unref(kfile);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Option<std::string>
|
||||
QueryMacros::find_macro(const std::string& name) const
|
||||
{
|
||||
if (const auto it{priv_->macros_.find(name)}; it != priv_->macros_.end())
|
||||
return it->second;
|
||||
else
|
||||
return Nothing;
|
||||
}
|
||||
|
||||
|
||||
#ifdef BUILD_TESTS
|
||||
/*
|
||||
* Tests.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "utils/mu-test-utils.hh"
|
||||
#include "utils/mu-utils-file.hh"
|
||||
|
||||
static void
|
||||
test_bookmarks()
|
||||
{
|
||||
MemDb db;
|
||||
Config conf_db{db};
|
||||
QueryMacros qm{conf_db};
|
||||
|
||||
TempDir tdir{};
|
||||
const auto bmfile{join_paths(tdir.path(), "bookmarks.ini")};
|
||||
std::ofstream os{bmfile};
|
||||
|
||||
mu_println(os, "# test\n"
|
||||
"[mu]\n"
|
||||
"foo=subject:bar");
|
||||
os.close();
|
||||
|
||||
auto res = qm.load_bookmarks(bmfile);
|
||||
assert_valid_result(res);
|
||||
|
||||
assert_equal(qm.find_macro("foo").value_or(""), "subject:bar");
|
||||
assert_equal(qm.find_macro("bar").value_or("nope"), "nope");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_bookmarks_fail()
|
||||
{
|
||||
|
||||
MemDb db;
|
||||
Config conf_db{db};
|
||||
QueryMacros qm{conf_db};
|
||||
|
||||
auto res = qm.load_bookmarks("/foo/bar/non-existent");
|
||||
g_assert_false(!!res);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
mu_test_init(&argc, &argv);
|
||||
|
||||
g_test_add_func("/query/macros/bookmarks", test_bookmarks);
|
||||
g_test_add_func("/query/macros/bookmarks-fail", test_bookmarks_fail);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
#endif /*BUILD_TESTS*/
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
** Copyright (C) 2023 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify it
|
||||
** under the terms of the GNU General Public License as published by the
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MU_QUERY_MACROS_HH__
|
||||
#define MU_QUERY_MACROS_HH__
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <utils/mu-result.hh>
|
||||
#include <utils/mu-option.hh>
|
||||
|
||||
#include "mu-config.hh"
|
||||
|
||||
namespace Mu {
|
||||
|
||||
class QueryMacros{
|
||||
public:
|
||||
/**
|
||||
* Construct QueryMacros object
|
||||
*
|
||||
* @param conf config object ref
|
||||
*/
|
||||
QueryMacros(const Config& conf);
|
||||
|
||||
/**
|
||||
* DTOR
|
||||
*/
|
||||
~QueryMacros();
|
||||
|
||||
/**
|
||||
* Read bookmarks (ie. macros) from a bookmark-file
|
||||
*
|
||||
* @param bookmarks_file path to the bookmarks file
|
||||
*
|
||||
* @return Ok or some error
|
||||
*/
|
||||
Result<void> load_bookmarks(const std::string& bookmarks_file);
|
||||
|
||||
|
||||
/**
|
||||
* Find a macro (aka 'bookmark') by its name
|
||||
*
|
||||
* @param name the name of the bookmark
|
||||
*
|
||||
* @return the macro value or Nothing if not found
|
||||
*/
|
||||
Option<std::string> find_macro(const std::string& name) const;
|
||||
|
||||
private:
|
||||
struct Private;
|
||||
std::unique_ptr<Private> priv_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Mu
|
||||
|
||||
#endif /* MU_QUERY_MACROS_HH__ */
|
|
@ -32,7 +32,7 @@
|
|||
#include "mu-maildir.hh"
|
||||
#include "mu-query-match-deciders.hh"
|
||||
#include "mu-query.hh"
|
||||
#include "mu-bookmarks.hh"
|
||||
#include "mu-query-macros.hh"
|
||||
#include "mu-query-parser.hh"
|
||||
#include "message/mu-message.hh"
|
||||
|
||||
|
@ -129,28 +129,20 @@ exec_cmd(const Option<Message>& msg, const OutputInfo& info, const Options& opts
|
|||
}
|
||||
|
||||
static Result<std::string>
|
||||
resolve_bookmark(const Options& opts)
|
||||
resolve_bookmark(const Store& store, const Options& opts)
|
||||
{
|
||||
const auto bmfile = opts.runtime_path(RuntimePath::Bookmarks);
|
||||
auto bm = mu_bookmarks_new(bmfile.c_str());
|
||||
if (!bm)
|
||||
return Err(Error::Code::File,
|
||||
"failed to open bookmarks file '{}'", bmfile);
|
||||
|
||||
const auto bookmark{opts.find.bookmark};
|
||||
const auto val = mu_bookmarks_lookup(bm, bookmark.c_str());
|
||||
if (!val) {
|
||||
mu_bookmarks_destroy(bm);
|
||||
return Err(Error::Code::NoMatches,
|
||||
"bookmark '{}' not found", bookmark);
|
||||
}
|
||||
|
||||
mu_bookmarks_destroy(bm);
|
||||
return Ok(std::string(val));
|
||||
QueryMacros macros{store.config()};
|
||||
if (auto&& res{macros.load_bookmarks(opts.runtime_path(RuntimePath::Bookmarks))}; !res)
|
||||
return Err(res.error());
|
||||
else if (auto&& bm{macros.find_macro(opts.find.bookmark)}; !bm)
|
||||
return Err(Error::Code::InvalidArgument, "bookmark '{}' not found",
|
||||
opts.find.bookmark);
|
||||
else
|
||||
return Ok(std::move(*bm));
|
||||
}
|
||||
|
||||
static Result<std::string>
|
||||
get_query(const Options& opts)
|
||||
get_query(const Store& store, const Options& opts)
|
||||
{
|
||||
if (opts.find.bookmark.empty() && opts.find.query.empty())
|
||||
return Err(Error::Code::InvalidArgument,
|
||||
|
@ -158,7 +150,7 @@ get_query(const Options& opts)
|
|||
|
||||
std::string bookmark;
|
||||
if (!opts.find.bookmark.empty()) {
|
||||
const auto res = resolve_bookmark(opts);
|
||||
const auto res = resolve_bookmark(store, opts);
|
||||
if (!res)
|
||||
return Err(std::move(res.error()));
|
||||
bookmark = res.value() + " ";
|
||||
|
@ -507,7 +499,7 @@ process_store_query(const Store& store, const std::string& expr, const Options&
|
|||
Result<void>
|
||||
Mu::mu_cmd_find(const Store& store, const Options& opts)
|
||||
{
|
||||
auto expr{get_query(opts)};
|
||||
auto expr{get_query(store, opts)};
|
||||
if (!expr)
|
||||
return Err(expr.error());
|
||||
|
||||
|
|
Loading…
Reference in New Issue