diff --git a/lib/mu-contacts-cache.cc b/lib/mu-contacts-cache.cc index fc3a22ed..366d0451 100644 --- a/lib/mu-contacts-cache.cc +++ b/lib/mu-contacts-cache.cc @@ -304,7 +304,7 @@ using ContactSet = std::set, ContactLessThan>; void -ContactsCache::for_each(const EachContactFunc& each_contact, size_t max_num) const +ContactsCache::for_each(const EachContactFunc& each_contact) const { std::lock_guard l_{priv_->mtx_}; @@ -316,12 +316,10 @@ ContactsCache::for_each(const EachContactFunc& each_contact, size_t max_num) con for (const auto& item : priv_->contacts_) sorted.emplace(item.second); - // sadly, there's no set::resize - size_t n{}; - for (const auto& ci : sorted) { - if (max_num > 0 && n++ >= max_num) + // return in _reverse_ order, so we get the most relevant ones first. + for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) { + if (!each_contact(*it)) break; - each_contact(ci); } } @@ -424,6 +422,7 @@ test_mu_contacts_cache_sort() if (g_test_verbose()) g_print("\t- %s\n", contact.display_name().c_str()); str += contact.name; + return true; }); return str; }; @@ -431,38 +430,38 @@ test_mu_contacts_cache_sort() const auto now{std::time({})}; - // "first" means less relevant + // "first" means more relevant - { /* recent messages, older comes first */ + { /* recent messages, newer comes first */ Mu::ContactsCache ccache(""); ccache.add(Mu::Contact{"a@example.com", "a", now, true, 1000, 0}); ccache.add(Mu::Contact{"b@example.com", "b", now-1, true, 1000, 0}); - assert_equal(result_chars(ccache), "ba"); + assert_equal(result_chars(ccache), "ab"); } - { /* non-recent messages, less frequent comes first */ + { /* non-recent messages, more frequent comes first */ Mu::ContactsCache ccache(""); ccache.add(Mu::Contact{"a@example.com", "a", now-2*RecentOffset, true, 1000, 0}); ccache.add(Mu::Contact{"b@example.com", "b", now-3*RecentOffset, true, 2000, 0}); - assert_equal(result_chars(ccache), "ab"); + assert_equal(result_chars(ccache), "ba"); } - { /* non-personal comes first */ + { /* personal comes first */ Mu::ContactsCache ccache(""); ccache.add(Mu::Contact{"a@example.com", "a", now-5*RecentOffset, true, 1000, 0}); ccache.add(Mu::Contact{"b@example.com", "b", now, false, 8000, 0}); - assert_equal(result_chars(ccache), "ba"); + assert_equal(result_chars(ccache), "ab"); } - { /* if all else fails, alphabetically */ + { /* if all else fails, reverse-alphabetically */ Mu::ContactsCache ccache(""); ccache.add(Mu::Contact{"a@example.com", "a", now, false, 1000, 0}); ccache.add(Mu::Contact{"b@example.com", "b", now, false, 1000, 0}); g_assert_cmpuint(ccache.size(),==,2); - assert_equal(result_chars(ccache), "ab"); + assert_equal(result_chars(ccache), "ba"); } } diff --git a/lib/mu-contacts-cache.hh b/lib/mu-contacts-cache.hh index 5b2e2b49..7c871d75 100644 --- a/lib/mu-contacts-cache.hh +++ b/lib/mu-contacts-cache.hh @@ -136,16 +136,18 @@ public: * Prototype for a callable that receives a contact * * @param contact some contact + * + * @return to get more contacts; false otherwise */ - using EachContactFunc = std::function; + using EachContactFunc = std::function; /** - * Invoke some callable for each contact, in order of rank. + * 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 max_num stop after at most so many contacts, or 0 for no limit */ - void for_each(const EachContactFunc& each_contact, size_t max_num=0) const; + void for_each(const EachContactFunc& each_contact) const; private: struct Private; diff --git a/lib/mu-server.cc b/lib/mu-server.cc index 71cf41a1..8ee380eb 100644 --- a/lib/mu-server.cc +++ b/lib/mu-server.cc @@ -532,30 +532,25 @@ Server::Private::contacts_handler(const Parameters& params) time_to_string("%c", after).c_str(), static_cast(tstamp)); - auto rank{0}; + auto n{0}; Sexp::List contacts; store().contacts_cache().for_each([&](const Contact& ci) { /* since the last time we got some contacts */ if (tstamp > ci.tstamp) - return; + return true; /* (maybe) only include 'personal' contacts */ if (personal && !ci.personal) - return; + return true; /* only include newer-than-x contacts */ if (after > ci.message_date) - return; + return true; - rank++; + n++; - Sexp::List contact; - contact.add_prop(":address", - Sexp::make_string(ci.display_name(true/*encode-if-needed*/))); - contact.add_prop(":rank", Sexp::make_number(rank)); - auto contacts_sexp{Sexp::make_list(std::move(contact))}; - contacts_sexp.formatting_opts |= Sexp::FormattingOptions::SplitList; - contacts.add(std::move(contacts_sexp)); - }, static_cast(maxnum)); + contacts.add(Sexp::make_string(ci.display_name(true/*encode-if-needed*/))); + return maxnum == 0 || n < maxnum; + }); Sexp::List seq; seq.add_prop(":contacts", Sexp::make_list(std::move(contacts))); @@ -564,7 +559,7 @@ Server::Private::contacts_handler(const Parameters& params) g_get_monotonic_time()))); /* dump the contacts cache as a giant sexp */ - g_debug("sending %d of %zu contact(s)", rank, store().contacts_cache().size()); + g_debug("sending %d of %zu contact(s)", n, store().contacts_cache().size()); output_sexp(std::move(seq), Server::OutputFlags::SplitList); } diff --git a/mu/mu-cmd-cfind.cc b/mu/mu-cmd-cfind.cc index 9e5cce00..8d4938ae 100644 --- a/mu/mu-cmd-cfind.cc +++ b/mu/mu-cmd-cfind.cc @@ -265,18 +265,19 @@ print_plain(const std::string& email, const std::string& name, bool color) } struct ECData { - MuConfigFormat format; - gboolean color, personal; - time_t after; - GRegex* rx; - GHashTable* nicks; - size_t n; + MuConfigFormat format; + gboolean color, personal; + time_t after; + GRegex* rx; + GHashTable* nicks; + size_t maxnum; + size_t n; }; static void each_contact(const Mu::Contact& ci, ECData& ecdata) { - if (ecdata.personal && ci.personal) + if (ecdata.personal && !ci.personal) return; if (ci.message_date < ecdata.after) @@ -334,13 +335,14 @@ each_contact(const Mu::Contact& ci, ECData& ecdata) } static MuError -run_cmd_cfind(const Mu::Store& store, - const char* pattern, - gboolean personal, - time_t after, - const MuConfigFormat format, - gboolean color, - GError** err) +run_cmd_cfind(const Mu::Store& store, + const char* pattern, + gboolean personal, + time_t after, + int maxnum, + const MuConfigFormat format, + gboolean color, + GError** err) { ECData ecdata{}; @@ -360,14 +362,17 @@ run_cmd_cfind(const Mu::Store& store, ecdata.personal = personal; ecdata.n = 0; ecdata.after = after; + ecdata.maxnum = maxnum; ecdata.format = format; ecdata.color = color; ecdata.nicks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); print_header(format); - store.contacts_cache().for_each([&](const auto& ci) { each_contact(ci, ecdata); }); - + store.contacts_cache().for_each([&](const auto& ci) { + each_contact(ci, ecdata); + return ecdata.maxnum == 0 || ecdata.n < ecdata.maxnum; + }); g_hash_table_unref(ecdata.nicks); if (ecdata.rx) @@ -422,6 +427,7 @@ Mu::mu_cmd_cfind(const Mu::Store& store, const MuConfig* opts, GError** err) opts->params[1], opts->personal, opts->after, + opts->maxnum, opts->format, !opts->nocolor, err); diff --git a/mu/mu-config.cc b/mu/mu-config.cc index f8db3af3..348c5eeb 100644 --- a/mu/mu-config.cc +++ b/mu/mu-config.cc @@ -296,6 +296,8 @@ config_options_group_cfind() "whether to only get 'personal' contacts", NULL}, {"after", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.after, "only get addresses last seen after T", ""}, + {"maxnum", 'n', 0, G_OPTION_ARG_INT, &MU_CONFIG.maxnum, + "maximum number of contacts", ""}, {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}}; og = g_option_group_new("cfind", "Options for the 'cfind' command", "", NULL,