|
| 1 | +/* |
| 2 | +** Copyright (C) 2023 Dirk-Jan C. Binnema <[email protected]> |
| 3 | +** |
| 4 | +** This program is free software; you can redistribute it and/or modify it |
| 5 | +** under the terms of the GNU General Public License as published by the |
| 6 | +** Free Software Foundation; either version 3, or (at your option) any |
| 7 | +** later version. |
| 8 | +** |
| 9 | +** This program is distributed in the hope that it will be useful, |
| 10 | +** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | +** GNU General Public License for more details. |
| 13 | +** |
| 14 | +** You should have received a copy of the GNU General Public License |
| 15 | +** along with this program; if not, write to the Free Software Foundation, |
| 16 | +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 17 | +** |
| 18 | +*/ |
| 19 | + |
| 20 | +#include "mu-query-macros.hh" |
| 21 | + |
| 22 | +#include <glib.h> |
| 23 | +#include <unordered_map> |
| 24 | + |
| 25 | +#include "utils/mu-utils.hh" |
| 26 | + |
| 27 | +using namespace Mu; |
| 28 | + |
| 29 | +constexpr auto MU_BOOKMARK_GROUP = "mu"; |
| 30 | + |
| 31 | +struct QueryMacros::Private { |
| 32 | + Private(const Config& conf): conf_{conf} {} |
| 33 | + |
| 34 | + |
| 35 | + Result<void> import_key_file(GKeyFile *kfile); |
| 36 | + |
| 37 | + const Config& conf_; |
| 38 | + std::unordered_map<std::string, std::string> macros_{}; |
| 39 | +}; |
| 40 | + |
| 41 | +Result<void> |
| 42 | +QueryMacros::Private::import_key_file(GKeyFile *kfile) |
| 43 | +{ |
| 44 | + if (!kfile) |
| 45 | + return Err(Error::Code::InvalidArgument, "invalid key-file"); |
| 46 | + |
| 47 | + GError *err{}; |
| 48 | + size_t num{}; |
| 49 | + gchar **keys{g_key_file_get_keys(kfile, MU_BOOKMARK_GROUP, &num, &err)}; |
| 50 | + if (!keys) |
| 51 | + return Err(Error::Code::File, &err/*cons*/,"failed to read keys"); |
| 52 | + |
| 53 | + for (auto key = keys; key && *key; ++key) { |
| 54 | + |
| 55 | + auto rawval{g_key_file_get_string(kfile, MU_BOOKMARK_GROUP, *key, &err)}; |
| 56 | + if (!rawval) { |
| 57 | + g_strfreev(keys); |
| 58 | + return Err(Error::Code::File, &err/*cons*/,"failed to read key '{}'", *key); |
| 59 | + } |
| 60 | + |
| 61 | + auto val{to_string_gchar(std::move(rawval))}; |
| 62 | + macros_.erase(val); // we want to replace |
| 63 | + macros_.emplace(std::string(*key), std::move(val)); |
| 64 | + ++num; |
| 65 | + } |
| 66 | + |
| 67 | + g_strfreev(keys); |
| 68 | + mu_debug("imported {} query macro(s); total {}", num, macros_.size()); |
| 69 | + return Ok(); |
| 70 | +} |
| 71 | + |
| 72 | +QueryMacros::QueryMacros(const Config& conf): |
| 73 | + priv_{std::make_unique<Private>(conf)} {} |
| 74 | + |
| 75 | +QueryMacros::~QueryMacros() = default; |
| 76 | + |
| 77 | +Result<void> |
| 78 | +QueryMacros::load_bookmarks(const std::string& path) |
| 79 | +{ |
| 80 | + GError *err{}; |
| 81 | + GKeyFile *kfile{g_key_file_new()}; |
| 82 | + if (!g_key_file_load_from_file(kfile, path.c_str(), G_KEY_FILE_NONE, &err)) { |
| 83 | + g_key_file_unref(kfile); |
| 84 | + return Err(Error::Code::File, &err/*cons*/, |
| 85 | + "failed to read bookmarks from {}", path); |
| 86 | + } |
| 87 | + |
| 88 | + auto&& res = priv_->import_key_file(kfile); |
| 89 | + g_key_file_unref(kfile); |
| 90 | + |
| 91 | + return res; |
| 92 | +} |
| 93 | + |
| 94 | +Option<std::string> |
| 95 | +QueryMacros::find_macro(const std::string& name) const |
| 96 | +{ |
| 97 | + if (const auto it{priv_->macros_.find(name)}; it != priv_->macros_.end()) |
| 98 | + return it->second; |
| 99 | + else |
| 100 | + return Nothing; |
| 101 | +} |
| 102 | + |
| 103 | + |
| 104 | +#ifdef BUILD_TESTS |
| 105 | +/* |
| 106 | + * Tests. |
| 107 | + * |
| 108 | + */ |
| 109 | + |
| 110 | +#include "utils/mu-test-utils.hh" |
| 111 | +#include "utils/mu-utils-file.hh" |
| 112 | + |
| 113 | +static void |
| 114 | +test_bookmarks() |
| 115 | +{ |
| 116 | + MemDb db; |
| 117 | + Config conf_db{db}; |
| 118 | + QueryMacros qm{conf_db}; |
| 119 | + |
| 120 | + TempDir tdir{}; |
| 121 | + const auto bmfile{join_paths(tdir.path(), "bookmarks.ini")}; |
| 122 | + std::ofstream os{bmfile}; |
| 123 | + |
| 124 | + mu_println(os, "# test\n" |
| 125 | + "[mu]\n" |
| 126 | + "foo=subject:bar"); |
| 127 | + os.close(); |
| 128 | + |
| 129 | + auto res = qm.load_bookmarks(bmfile); |
| 130 | + assert_valid_result(res); |
| 131 | + |
| 132 | + assert_equal(qm.find_macro("foo").value_or(""), "subject:bar"); |
| 133 | + assert_equal(qm.find_macro("bar").value_or("nope"), "nope"); |
| 134 | +} |
| 135 | + |
| 136 | + |
| 137 | +static void |
| 138 | +test_bookmarks_fail() |
| 139 | +{ |
| 140 | + |
| 141 | + MemDb db; |
| 142 | + Config conf_db{db}; |
| 143 | + QueryMacros qm{conf_db}; |
| 144 | + |
| 145 | + auto res = qm.load_bookmarks("/foo/bar/non-existent"); |
| 146 | + g_assert_false(!!res); |
| 147 | +} |
| 148 | + |
| 149 | + |
| 150 | +int |
| 151 | +main(int argc, char* argv[]) |
| 152 | +{ |
| 153 | + mu_test_init(&argc, &argv); |
| 154 | + |
| 155 | + g_test_add_func("/query/macros/bookmarks", test_bookmarks); |
| 156 | + g_test_add_func("/query/macros/bookmarks-fail", test_bookmarks_fail); |
| 157 | + |
| 158 | + return g_test_run(); |
| 159 | +} |
| 160 | +#endif /*BUILD_TESTS*/ |
0 commit comments