summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2025-06-05 19:31:53 +0300
committerDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2025-06-10 10:04:53 +0300
commit15279767293ce424f43fc5329dd9f1aaf8b7670d (patch)
treead8959b8a066fafb96d7cd163d869237455d8572
parent155725ff748ec05c19c088fec0a2cb40758db8c1 (diff)
mu-cfind/contacts-cache: refactor matching in for_each
Move some of the code in from the command-line tool to contacts-cache, for possible re-use. Clean up a bit while doing so.
-rw-r--r--lib/mu-contacts-cache.cc44
-rw-r--r--lib/mu-contacts-cache.hh25
-rw-r--r--mu/mu-cmd-cfind.cc50
3 files changed, 75 insertions, 44 deletions
diff --git a/lib/mu-contacts-cache.cc b/lib/mu-contacts-cache.cc
index 017af20..330d625 100644
--- a/lib/mu-contacts-cache.cc
+++ b/lib/mu-contacts-cache.cc
@@ -285,7 +285,7 @@ struct ContactLessThan {
};
using ContactSet = std::set<std::reference_wrapper<const Contact>, ContactLessThan>;
-void
+Result<size_t>
ContactsCache::for_each(const EachContactFunc& each_contact) const
{
std::lock_guard<std::mutex> l_{priv_->mtx_};
@@ -296,10 +296,52 @@ ContactsCache::for_each(const EachContactFunc& each_contact) const
sorted.emplace(item.second);
// return in _reverse_ order, so we get the most relevant ones first.
+ size_t n{};
for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) {
+ ++n;
if (!each_contact(*it))
break;
}
+
+ return Ok(std::move(n));
+}
+
+Result<size_t>
+ContactsCache::for_each(const EachContactFunc& each_contact,
+ const std::string match_rx,
+ bool personal,
+ int64_t after,
+ size_t maxnum) const
+{
+ // get the pattern regex, if any.
+ const Result<Regex> rxres = match_rx.empty() ? Ok(Regex{}) :
+ Regex::make(match_rx, static_cast<GRegexCompileFlags>
+ (G_REGEX_OPTIMIZE|G_REGEX_CASELESS));
+ if (!rxres)
+ return Err(rxres.error());
+
+ const Regex rx{*rxres};
+
+ size_t n{};
+ const auto res = for_each([&](const Contact& contact)->bool {
+ // filter for personal & "after"
+ if ((personal && !contact.personal) ||
+ (after > contact.message_date))
+ return true; /* skip this contact */
+ // filter for regex, if any.
+ if (rx && !rx.matches(contact.name) && !rx.matches(contact.email))
+ return true; /* next */
+ // do we have enough matches?
+ if (maxnum != 0 && n >= maxnum)
+ return false; // terminate
+ ++n;
+ return each_contact(contact);
+ });
+
+ if (res)
+ return Ok(std::move(n));
+ else
+ return Err(res.error());
}
static bool
diff --git a/lib/mu-contacts-cache.hh b/lib/mu-contacts-cache.hh
index d31c9dc..a0a452b 100644
--- a/lib/mu-contacts-cache.hh
+++ b/lib/mu-contacts-cache.hh
@@ -1,5 +1,5 @@
/*
-** Copyright (C) 2020-2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
+** Copyright (C) 2020-2025 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
@@ -158,9 +158,30 @@ public:
* highest ranked contacts come first).
*
* @param each_contact function invoked for each contact
+ *
+ * @return The number of times the callback was invoked or some error
*/
- void for_each(const EachContactFunc& each_contact) const;
+ Result<size_t> for_each(const EachContactFunc& each_contact) const;
+ /**
+ * Invoke some callable for each contact, in _descending_ order of rank
+ * (i.e., the highest ranked contacts come first).
+ *
+ * @param each_contact function invoked for each contact
+ * @param match_rx string with regular expression or "" for none
+ * @param personal if true, only include personal contacts
+ * (i.e., contacts seen in message in which a personal address
+ * was involved)
+ * @param after only consider messages after this time_t (seconds since epoch)
+ * @param maxnum maximum number of times the callback invoked
+ *
+ * @return The number of times the callback was invoked or some error
+ */
+ Result<size_t> for_each(const EachContactFunc& each_contact,
+ const std::string match_rx,
+ bool personal = false,
+ int64_t after = 0,
+ size_t maxnum = 0) const;
private:
struct Private;
std::unique_ptr<Private> priv_;
diff --git a/mu/mu-cmd-cfind.cc b/mu/mu-cmd-cfind.cc
index 9c61595..0072790 100644
--- a/mu/mu-cmd-cfind.cc
+++ b/mu/mu-cmd-cfind.cc
@@ -1,5 +1,5 @@
/*
-** Copyright (C) 2022-2023 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
+** Copyright (C) 2022-2025 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
@@ -16,8 +16,6 @@
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**
*/
-
-#include "config.h"
#include "mu-cmd.hh"
#include <cstdint>
@@ -260,64 +258,34 @@ Mu::mu_cmd_cfind(const Mu::Store& store, const Mu::Options& opts)
if (!output)
return Err(Error::Code::Internal,
"missing output function");
-
- // get the pattern regex, if any.
- Regex rx{};
- if (!opts.cfind.rx_pattern.empty()) {
- if (auto&& res = Regex::make(opts.cfind.rx_pattern,
- static_cast<GRegexCompileFlags>
- (G_REGEX_OPTIMIZE|G_REGEX_CASELESS)); !res)
- return Err(std::move(res.error()));
- else
- rx = res.value();
- }
-
nicks.clear();
- store.contacts_cache().for_each([&](const Contact& contact)->bool {
-
- if (opts.cfind.maxnum && num > *opts.cfind.maxnum)
- return false; /* stop the loop */
-
- if (!store.contacts_cache().is_valid(contact.email))
- return true; /* next */
-
- // filter for maxnum, personal & "after"
- if ((opts.cfind.personal && !contact.personal) ||
- (opts.cfind.after.value_or(0) > contact.message_date))
- return true; /* next */
- // filter for regex, if any.
- if (rx) {
- if (!rx.matches(contact.name) && !rx.matches(contact.email))
- return true; /* next */
- }
-
- /* seems we have a match! display it. */
+ const auto res = store.contacts_cache().for_each([&](const Contact& contact)->bool {
const auto itype{num == 0 ? ItemType::Header : ItemType::Normal};
output(itype, contact, opts);
++num;
return true;
- });
+ }, opts.cfind.rx_pattern, opts.cfind.personal,
+ opts.cfind.after.value_or(0),
+ opts.cfind.maxnum.value_or(0U));
- if (num == 0)
+ if (!res)
+ return Err(res.error());
+ else if (*res == 0)
return Err(Error::Code::NoMatches, "no matching contacts found");
output(ItemType::Footer, Nothing, opts);
return Ok();
}
-
-
-
#ifdef BUILD_TESTS
/*
* Tests.
*
*/
-
+#include "config.h"
#include "utils/mu-test-utils.hh"
-
static std::string test_mu_home;
static void