From c3ae3da0de5d84b9a7a03f41cd9ba01f8a53db10 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Fri, 4 Mar 2022 00:02:52 +0200 Subject: [PATCH] lib: update mu-contact-{fields,contact,flags,priority} Improve cohesion and tests. --- lib/Makefile.am | 2 +- lib/meson.build | 3 +- lib/mu-message-contact.hh | 24 ++++++++- lib/mu-message-fields.cc | 56 +++++++++++++------ lib/mu-message-fields.hh | 107 +++++++++++++++++++++++++++++++------ lib/mu-message-flags.hh | 43 +++++++++------ lib/mu-message-priority.hh | 1 + 7 files changed, 184 insertions(+), 52 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index a018c650..410d58f9 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -92,7 +92,7 @@ libmu_la_SOURCES= \ mu-msg-doc.cc \ mu-msg-doc.hh \ mu-msg-fields.c \ - mu-msg-fields.h \ + mu-message-fields.hh \ mu-msg-file.cc \ mu-msg-file.hh \ mu-msg-part.cc \ diff --git a/lib/meson.build b/lib/meson.build index fd9717fb..d8011a03 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -53,8 +53,7 @@ lib_mu=static_library( 'mu-msg-crypto.cc', 'mu-msg-doc.cc', 'mu-msg-doc.hh', - 'mu-msg-fields.c', - 'mu-msg-fields.h', + 'mu-message-fields.hh', 'mu-msg-file.cc', 'mu-msg-file.hh', 'mu-msg-part.cc', diff --git a/lib/mu-message-contact.hh b/lib/mu-message-contact.hh index 6318867d..298bc680 100644 --- a/lib/mu-message-contact.hh +++ b/lib/mu-message-contact.hh @@ -26,10 +26,11 @@ #include #include #include - #include #include +#include "mu-message-fields.hh" + struct _InternetAddressList; namespace Mu { @@ -125,6 +126,27 @@ struct MessageContact { return cached_hash; } + + /** + * Get the MessageField for this contact-type, if any. + * + * @return the message-field or nullopt. + */ + constexpr std::optional const field() { + switch(type){ + case Type::From: + return message_field(MessageField::Id::From); + case Type::To: + return message_field(MessageField::Id::To); + case Type::Cc: + return message_field(MessageField::Id::Cc); + case Type::Bcc: + return message_field(MessageField::Id::Bcc); + default: + return std::nullopt; + } + } + /* * data members */ diff --git a/lib/mu-message-fields.cc b/lib/mu-message-fields.cc index d9548ca7..5931dd35 100644 --- a/lib/mu-message-fields.cc +++ b/lib/mu-message-fields.cc @@ -21,23 +21,24 @@ using namespace Mu; -std::optional -message_field_id(const std::string& name_or_shortcut) +std::string +MessageField::xapian_term(const std::string& s) const { - const auto it = std::find_if( - MessageFields.begin(), MessageFields.end(), - [&](auto&& field)->bool { - return field.name == name_or_shortcut || - (name_or_shortcut.size() == 1 && - name_or_shortcut[0] == field.shortcut); - }); - - if (it == MessageFields.end()) - return std::nullopt; - else - return it->id; + return std::string(1U, xapian_prefix()) + s; } +std::string +MessageField::xapian_term(std::string_view sv) const +{ + return std::string(1U, xapian_prefix()) + std::string{sv}; +} +std::string +MessageField::xapian_term(char c) const +{ + return std::string(1U, xapian_prefix()) + c; +} + + /** * compile-time checks @@ -65,9 +66,6 @@ validate_message_field_shortcuts() } return true; } - - - /* * tests... also build as runtime-tests, so we can get coverage info */ @@ -90,7 +88,29 @@ test_shortcuts() static_assert(validate_message_field_shortcuts()); } +[[maybe_unused]] +static void +test_prefix() +{ + static_assert(message_field(MessageField::Id::Subject).xapian_prefix() == 'S'); + static_assert(message_field(MessageField::Id::BodyHtml).xapian_prefix() == 0); +} + #ifdef BUILD_TESTS + +static void +test_xapian_term() +{ + using namespace std::string_literals; + using namespace std::literals; + + assert_equal(message_field(MessageField::Id::Subject).xapian_term(""s), "S"); + assert_equal(message_field(MessageField::Id::Subject).xapian_term("boo"s), "Sboo"); + + assert_equal(message_field(MessageField::Id::From).xapian_term('x'), "Fx"); + assert_equal(message_field(MessageField::Id::To).xapian_term("boo"sv), "Tboo"); +} + int main(int argc, char* argv[]) { @@ -98,6 +118,8 @@ main(int argc, char* argv[]) g_test_add_func("/message/fields/ids", test_ids); g_test_add_func("/message/fields/shortcuts", test_shortcuts); + g_test_add_func("/message/fields/prefix", test_prefix); + g_test_add_func("/message/fields/xapian-term", test_xapian_term); return g_test_run(); } diff --git a/lib/mu-message-fields.hh b/lib/mu-message-fields.hh index 36c34711..b08b7dfe 100644 --- a/lib/mu-message-fields.hh +++ b/lib/mu-message-fields.hh @@ -89,6 +89,11 @@ struct MessageField { return static_cast(Id::_count_); } + + constexpr Xapian::valueno value_no() const { + return static_cast(id); + } + /** * Field types * @@ -101,6 +106,13 @@ struct MessageField { Integer, /**< An integer */ }; + constexpr bool is_string() const { return type == Type::String; } + constexpr bool is_string_list() const { return type == Type::StringList; } + constexpr bool is_byte_size() const { return type == Type::ByteSize; } + constexpr bool is_time_t() const { return type == Type::TimeT; } + constexpr bool is_integer() const { return type == Type::Integer; } + constexpr bool is_numerical() const { return is_byte_size() || is_time_t() || is_integer(); } + /** * Field flags * note: the differences for our purposes between a xapian field and a @@ -118,18 +130,31 @@ struct MessageField { /**< Field-text is indexed in xapian (i.e., the text is processed */ Searchable = 1 << 2, /**< Field is a searchable term */ - Value = 1 << 3, + Value = 1 << 3, /**< Field value is stored (so the literal value can be retrieved) */ - Contact = 1 << 4, + Contact = 1 << 4, /**< field contains one or more * e-mail-addresses */ XapianBoolean = 1 << 5, - /**< use 'add_boolean_prefix' for Xapian queries; wildcards do NOT WORK for such + /**< use 'add_boolean_prefix' for Xapian queries; wildcards do NOT WORK for such * fields */ DoNotCache = 1 << 6, /**< don't cache this field in * the MuMsg cache */ Range = 1 << 7 /**< whether this is a range field (e.g., date, size)*/ }; + constexpr bool any_of(Flag some_flag) const{ + return (static_cast(some_flag) & static_cast(flags)) != 0; + } + + constexpr bool is_gmime() const { return any_of(Flag::Value); } + constexpr bool is_full_text() const { return any_of(Flag::FullText); } + constexpr bool is_searchable() const { return any_of(Flag::Searchable); } + constexpr bool is_value() const { return any_of(Flag::Value); } + constexpr bool is_contact() const { return any_of(Flag::Contact); } + constexpr bool is_xapian_boolean() const { return any_of(Flag::XapianBoolean); } + constexpr bool is_range() const { return any_of(Flag::Range); } + constexpr bool do_not_cache() const { return any_of(Flag::DoNotCache); } + /** * Field members * @@ -152,16 +177,9 @@ struct MessageField { return shortcut == 0 ? 0 : shortcut - ('a' - 'A'); } - constexpr Xapian::valueno value_no() const { - return static_cast(id); - } - - constexpr bool is_numeric() const - { - return type == Type::ByteSize || - type == Type::TimeT || - type == Type::Integer; - } + std::string xapian_term(const std::string& s="") const; + std::string xapian_term(std::string_view sv) const; + std::string xapian_term(char c) const; }; MU_ENABLE_BITOPS(MessageField::Flag); @@ -282,7 +300,7 @@ static constexpr std::array "path", "File system path to message", {}, - 'i', + 'p', MessageField::Flag::GMime | MessageField::Flag::XapianBoolean | MessageField::Flag::Value}, @@ -304,7 +322,7 @@ static constexpr std::array { MessageField::Id::To, MessageField::Type::String, - "To", + "to", "Message recipient", "to:flimflam@example.com", 't', @@ -428,6 +446,32 @@ message_field(MessageField::Id id) return MessageFields.at(static_cast(id)); } +/** + * Invoke func for each message-field + * + * @param func some callable + */ +template +void message_field_for_each(Func&& func) { + for (const auto& field: MessageFields) + func(field); +} + +/** + * Find a message field that satisfies some predicate + * + * @param pred the predicate (a callable) + * + * @return a message-field id, or nullopt if not found. + */ +template +std::optional message_field_find_if(Pred&& pred) { + for (auto&& field: MessageFields) + if (pred(field)) + return field.id; + return std::nullopt; +} + /** * Get the the message-field id for the given name or shortcut * @@ -435,7 +479,38 @@ message_field(MessageField::Id id) * * @return the message-field-id or nullopt. */ -std::optional message_field_id(const std::string& name_or_shortcut); +static inline +std::optional message_field_id(char shortcut) { + return message_field_find_if([&](auto&& field ){ + return field.shortcut == shortcut; + }); +} +static inline +std::optional message_field_id(const std::string& name) { + if (name.length() == 1) + return message_field_id(name[0]); + else + return message_field_find_if([&](auto&& field){ + return field.name == name; + }); +} + +/** + * Get the MessageField::Id for some number, or nullopt if it does not match + * + * @param id an id number + * + * @return MessageField::Id or nullopt + */ +static inline +std::optional message_field_id(size_t id) +{ + if (id >= static_cast(MessageField::Id::_count_)) + return std::nullopt; + else + return static_cast(id); +} + } // namespace Mu #endif /* MU_MESSAGE_FIELDS_HH__ */ diff --git a/lib/mu-message-flags.hh b/lib/mu-message-flags.hh index 07c6e0d2..6ce5b289 100644 --- a/lib/mu-message-flags.hh +++ b/lib/mu-message-flags.hh @@ -93,9 +93,9 @@ struct MessageFlagInfo { std::string_view name; /**< Name of the flag */ MessageFlagCategory category; /**< Flag category */ - /** + /** * Get the lower-case version of shortcut - * + * * @return lower-case shortcut */ constexpr char shortcut_lower() const { @@ -108,26 +108,39 @@ struct MessageFlagInfo { * Array of all flag information. */ constexpr std::array AllMessageFlagInfos = {{ - MessageFlagInfo{MessageFlags::Draft, 'D', "draft", MessageFlagCategory::Mailfile}, - MessageFlagInfo{MessageFlags::Flagged, 'F', "flagged", MessageFlagCategory::Mailfile}, - MessageFlagInfo{MessageFlags::Passed, 'P', "passed", MessageFlagCategory::Mailfile}, - MessageFlagInfo{MessageFlags::Replied, 'R', "replied", MessageFlagCategory::Mailfile}, - MessageFlagInfo{MessageFlags::Seen, 'S', "seen", MessageFlagCategory::Mailfile}, - MessageFlagInfo{MessageFlags::Trashed, 'T', "trashed", MessageFlagCategory::Mailfile}, + MessageFlagInfo{MessageFlags::Draft, 'D', "draft", MessageFlagCategory::Mailfile}, + MessageFlagInfo{MessageFlags::Flagged, 'F', "flagged", MessageFlagCategory::Mailfile}, + MessageFlagInfo{MessageFlags::Passed, 'P', "passed", MessageFlagCategory::Mailfile}, + MessageFlagInfo{MessageFlags::Replied, 'R', "replied", MessageFlagCategory::Mailfile}, + MessageFlagInfo{MessageFlags::Seen, 'S', "seen", MessageFlagCategory::Mailfile}, + MessageFlagInfo{MessageFlags::Trashed, 'T', "trashed", MessageFlagCategory::Mailfile}, - MessageFlagInfo{MessageFlags::New, 'N', "new", MessageFlagCategory::Maildir}, + MessageFlagInfo{MessageFlags::New, 'N', "new", MessageFlagCategory::Maildir}, - MessageFlagInfo{MessageFlags::Signed, 'z', "signed", MessageFlagCategory::Content}, - MessageFlagInfo{MessageFlags::Encrypted, 'x', "encrypted", - MessageFlagCategory::Content}, + MessageFlagInfo{MessageFlags::Signed, 'z', "signed", MessageFlagCategory::Content}, + MessageFlagInfo{MessageFlags::Encrypted, 'x', "encrypted", + MessageFlagCategory::Content}, MessageFlagInfo{MessageFlags::HasAttachment, 'a', "attach", - MessageFlagCategory::Content}, + MessageFlagCategory::Content}, - MessageFlagInfo{MessageFlags::Unread, 'u', "unread", MessageFlagCategory::Pseudo}, + MessageFlagInfo{MessageFlags::Unread, 'u', "unread", MessageFlagCategory::Pseudo}, - MessageFlagInfo{MessageFlags::MailingList, 'l', "list", MessageFlagCategory::Content}, + MessageFlagInfo{MessageFlags::MailingList, 'l', "list", MessageFlagCategory::Content}, }}; + +/** + * Invoke some callable Func for each flag info + * + * @param func some callable + */ +template +constexpr void message_flag_infos_for_each(Func&& func) +{ + for (auto&& info: AllMessageFlagInfos) + func(info); +} + /** * Get flag info for some flag * diff --git a/lib/mu-message-priority.hh b/lib/mu-message-priority.hh index 4c035642..5ede4779 100644 --- a/lib/mu-message-priority.hh +++ b/lib/mu-message-priority.hh @@ -23,6 +23,7 @@ #include #include #include +#include "mu-message-fields.hh" namespace Mu { /**