diff options
| author | Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> | 2025-06-05 19:31:53 +0300 |
|---|---|---|
| committer | Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> | 2025-06-10 10:04:53 +0300 |
| commit | 15279767293ce424f43fc5329dd9f1aaf8b7670d (patch) | |
| tree | ad8959b8a066fafb96d7cd163d869237455d8572 | |
| parent | 155725ff748ec05c19c088fec0a2cb40758db8c1 (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.cc | 44 | ||||
| -rw-r--r-- | lib/mu-contacts-cache.hh | 25 | ||||
| -rw-r--r-- | mu/mu-cmd-cfind.cc | 50 |
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 |
