From 58af12add65af7ef456b0cfd5ac5f19d4c62fa31 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 23 Feb 2022 09:29:37 +0200 Subject: [PATCH] mu-message-field: Implement Implement Mu::MessageField, which replace MuMsgField with something a bit more modern. --- lib/meson.build | 2 + lib/mu-message-fields.cc | 104 +++++++++ lib/mu-message-fields.hh | 441 +++++++++++++++++++++++++++++++++++++++ lib/mu-message.hh | 40 ++++ 4 files changed, 587 insertions(+) create mode 100644 lib/mu-message-fields.cc create mode 100644 lib/mu-message-fields.hh create mode 100644 lib/mu-message.hh diff --git a/lib/meson.build b/lib/meson.build index d4d085a1..fd9717fb 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -65,6 +65,8 @@ lib_mu=static_library( 'mu-msg.hh', 'mu-message-contact.hh', 'mu-message-contact.cc', + 'mu-message-fields.hh', + 'mu-message-fields.cc', 'mu-message-flags.hh', 'mu-message-flags.cc', 'mu-message-priority.hh', diff --git a/lib/mu-message-fields.cc b/lib/mu-message-fields.cc new file mode 100644 index 00000000..d9548ca7 --- /dev/null +++ b/lib/mu-message-fields.cc @@ -0,0 +1,104 @@ +/* +** Copyright (C) 2022 Dirk-Jan C. Binnema +** +** 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 +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#include "mu-message-fields.hh" + +using namespace Mu; + +std::optional +message_field_id(const std::string& name_or_shortcut) +{ + 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; +} + + +/** + * compile-time checks + */ +constexpr bool +validate_message_field_ids() +{ + for (auto id = 0U; id != MessageField::id_size(); ++id) { + const auto field_id = static_cast(id); + if (message_field(field_id).id != field_id) + return false; + } + return true; +} + +constexpr bool +validate_message_field_shortcuts() +{ + for (auto id = 0U; id != MessageField::id_size(); ++id) { + const auto field_id = static_cast(id); + const auto shortcut = message_field(field_id).shortcut; + if (shortcut != 0 && + (shortcut < 'a' || shortcut > 'z')) + return false; + } + return true; +} + + + +/* + * tests... also build as runtime-tests, so we can get coverage info + */ +#ifdef BUILD_TESTS +#define static_assert g_assert_true +#endif /*BUILD_TESTS*/ + + +[[maybe_unused]] +static void +test_ids() +{ + static_assert(validate_message_field_ids()); +} + +[[maybe_unused]] +static void +test_shortcuts() +{ + static_assert(validate_message_field_shortcuts()); +} + +#ifdef BUILD_TESTS +int +main(int argc, char* argv[]) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/message/fields/ids", test_ids); + g_test_add_func("/message/fields/shortcuts", test_shortcuts); + + return g_test_run(); +} +#endif /*BUILD_TESTS*/ diff --git a/lib/mu-message-fields.hh b/lib/mu-message-fields.hh new file mode 100644 index 00000000..36c34711 --- /dev/null +++ b/lib/mu-message-fields.hh @@ -0,0 +1,441 @@ +/* +** Copyright (C) 2022 Dirk-Jan C. Binnema +** +** 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 +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#ifndef MU_MESSAGE_FIELDS_HH__ +#define MU_MESSAGE_FIELDS_HH__ + +#include +#include +#include +#include +#include +#include +#include + +namespace Mu { + +struct MessageField { + /** + * Field Ids. + * + * Note, the Ids are also used as indices in the MessageFields array, + * so their numerical values must be 0...Count. + * + */ + enum struct Id { + /* + * first all the string-based ones + */ + Bcc = 0, /**< Blind Carbon-Copy */ + BodyHtml, /**< HTML Body */ + BodyText, /**< Text body */ + Cc, /**< Carbon-Copy */ + EmbeddedText, /**< Embedded text in message */ + File, /**< Filename */ + From, /**< Message sender */ + Maildir, /**< Maildir path */ + Mime, /**< MIME-Type */ + MessageId, /**< Message Id */ + Path, /**< File-system Path */ + Subject, /**< Message subject */ + To, /**< To: recipient */ + Uid, /**< Unique id for message (based on path) */ + /* + * string list items... + */ + References, /**< All references (incl. Reply-To:) */ + Tags, /**< Message Tags */ + /* + * then the numerical ones + */ + Date, /**< Message date */ + Flags, /**< Message flags */ + Priority, /**< Message priority */ + Size, /**< Message size (in bytes) */ + + /* add new ones here... */ + MailingList, /**< Mailing list */ + ThreadId, /**< Thread Id */ + + /* + * + */ + _count_ /**< Number of MessageFieldIds */ + }; + + /** + * Get the number of Id values. + * + * @return the number. + */ + static constexpr size_t id_size() + { + return static_cast(Id::_count_); + } + + /** + * Field types + * + */ + enum struct Type { + String, /**< String */ + StringList, /**< List of strings */ + ByteSize, /**< Size in bytes */ + TimeT, /**< A time_t value */ + Integer, /**< An integer */ + }; + + /** + * Field flags + * note: the differences for our purposes between a xapian field and a + * term: - there is only a single value for some item in per document + * (msg), ie. one value containing the list of To: addresses - there + * can be multiple terms, each containing e.g. one of the To: + * addresses - searching uses terms, but to display some field, it + * must be in the value (at least when using MuMsgIter) + */ + + enum struct Flag { + GMime = 1 << 0, + /**< Field retrieved through gmime */ + FullText = 1 << 1, + /**< Field-text is indexed in xapian (i.e., the text is processed */ + Searchable = 1 << 2, + /**< Field is a searchable term */ + Value = 1 << 3, + /**< Field value is stored (so the literal value can be retrieved) */ + 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 + * 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)*/ + }; + + /** + * Field members + * + */ + Id id; /**< Id of the message field */ + Type type; /**< Type of the message field */ + std::string_view name; /**< Name of the message field */ + std::string_view description; /**< Decription of the message field */ + std::string_view example_query; /**< Example query */ + char shortcut; /**< Shortcut for the message field; a..z */ + Flag flags; /**< Flags */ + + /** + * Convenience / helpers + * + */ + + constexpr char xapian_prefix() const + { /* xapian uses uppercase shortcuts; toupper is not constexpr */ + 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; + } +}; + +MU_ENABLE_BITOPS(MessageField::Flag); + +/** + * Sequence of _all_ message fields + */ +static constexpr std::array + MessageFields = { + { + + // Bcc + { + MessageField::Id::Bcc, + MessageField::Type::String, + "bcc", + "Blind carbon-copy recipient", + "bcc:foo@example.com", + 'h', + MessageField::Flag::GMime | MessageField::Flag::Contact | + MessageField::Flag::Value}, + // HTML Body + { + MessageField::Id::BodyHtml, + MessageField::Type::String, + "body", + "Message html body", + {}, + {}, + MessageField::Flag::GMime | MessageField::Flag::DoNotCache}, + // Body + { + MessageField::Id::BodyText, + MessageField::Type::String, + "body", + "Message plain-text body", + "body:capybara", // example + 'b', + MessageField::Flag::GMime | MessageField::Flag::FullText | + MessageField::Flag::DoNotCache}, + + // Cc + { + MessageField::Id::Cc, + MessageField::Type::String, + "cc", + "Carbon-copy recipient", + "cc:quinn@example.com", + 'c', + MessageField::Flag::GMime | MessageField::Flag::Contact | + MessageField::Flag::Value}, + + // Embed + { + MessageField::Id::EmbeddedText, + MessageField::Type::String, + "embed", + "Embedded text", + "embed:war OR embed:peace", + 'e', + MessageField::Flag::GMime | MessageField::Flag::FullText | + MessageField::Flag::DoNotCache}, + // File + { + MessageField::Id::File, + MessageField::Type::String, + "file", + "Attachment file name", + "file:/image\\.*.jpg/", + 'j', + MessageField::Flag::GMime | MessageField::Flag::Searchable | + MessageField::Flag::DoNotCache}, + + // From + { + MessageField::Id::From, + MessageField::Type::String, + "from", + "Message sender", + "from:jimbo", + 'f', + MessageField::Flag::GMime | MessageField::Flag::Contact | + MessageField::Flag::Value}, + // Maildir + { + MessageField::Id::Maildir, + MessageField::Type::String, + "maildir", + "Maildir path for message", + "maildir:/private/archive", + 'm', + MessageField::Flag::GMime | MessageField::Flag::Searchable | + MessageField::Flag::Value}, + // MIME + { + MessageField::Id::Mime, + MessageField::Type::String, + "mime", + "Attachment MIME-type", + "mime:image/jpeg", + 'y', + MessageField::Flag::Searchable}, + // Message-ID + { + MessageField::Id::MessageId, + MessageField::Type::String, + "msgid", + "Attachment MIME-type", + "mime:image/jpeg", + 'i', + MessageField::Flag::GMime | + MessageField::Flag::Searchable | + MessageField::Flag::Value}, + // Path + { + MessageField::Id::Path, + MessageField::Type::String, + "path", + "File system path to message", + {}, + 'i', + MessageField::Flag::GMime | + MessageField::Flag::XapianBoolean | + MessageField::Flag::Value}, + + // Subject + { + MessageField::Id::Subject, + MessageField::Type::String, + "subject", + "Message subject", + "subject:wombat", + 's', + MessageField::Flag::GMime | + MessageField::Flag::Searchable | + MessageField::Flag::Value | + MessageField::Flag::FullText}, + + // To + { + MessageField::Id::To, + MessageField::Type::String, + "To", + "Message recipient", + "to:flimflam@example.com", + 't', + MessageField::Flag::GMime | MessageField::Flag::Contact | + MessageField::Flag::Value}, + + // UID (internal) + { + MessageField::Id::Uid, + MessageField::Type::String, + "uid", + "Message recipient", + {}, + 'u', + MessageField::Flag::Searchable}, + + // References + { + MessageField::Id::References, + MessageField::Type::StringList, + "refs", + "Message references to other messages", + {}, + 'r', + MessageField::Flag::GMime | MessageField::Flag::Value}, + + // Tags + { + MessageField::Id::Tags, + MessageField::Type::StringList, + "tag", + "Message tags", + "tag:projectx", + 'x', + MessageField::Flag::GMime | MessageField::Flag::Searchable | + MessageField::Flag::Value}, + + // Date + { + MessageField::Id::Date, + MessageField::Type::TimeT, + "date", + "Message date", + "date:20220101..20220505", + 'd', + MessageField::Flag::GMime | MessageField::Flag::Searchable | + MessageField::Flag::Value | MessageField::Flag::XapianBoolean | + MessageField::Flag::Range}, + + // Flags + { + MessageField::Id::Flags, + MessageField::Type::Integer, + "flag", + "Message properties", + "flag:unread", + 'g', + MessageField::Flag::GMime | MessageField::Flag::Searchable | + MessageField::Flag::Value}, + // Priority + { + MessageField::Id::Priority, + MessageField::Type::Integer, + "prio", + "Priority", + "prio:high", + 'p', + MessageField::Flag::GMime | + MessageField::Flag::Searchable | + MessageField::Flag::Value}, + + // Size + { + MessageField::Id::Size, + MessageField::Type::ByteSize, + "size", + "Message size in bytes", + "size:1M..5M", + 'z', + MessageField::Flag::GMime | + MessageField::Flag::Searchable | + MessageField::Flag::Value | + MessageField::Flag::Range}, + + // Mailing List + { + MessageField::Id::MailingList, + MessageField::Type::String, + "list", + "Mailing list (List-Id:)", + "list:mu-discuss.googlegroups.com", + 'v', + MessageField::Flag::GMime | MessageField::Flag::Searchable | + MessageField::Flag::Value}, + + // ThreadId + { + MessageField::Id::ThreadId, + MessageField::Type::String, + "thread", + "Thread a message belongs to", + {}, + 'w', + MessageField::Flag::Searchable}, + }}; + +/* + * Convenience + */ + +/** + * Get the message field for the given Id. + * + * @param id of the message field + * + * @return ref of the message field. + */ +constexpr const MessageField& +message_field(MessageField::Id id) +{ + return MessageFields.at(static_cast(id)); +} + +/** + * Get the the message-field id for the given name or shortcut + * + * @param name_or_shortcut + * + * @return the message-field-id or nullopt. + */ +std::optional message_field_id(const std::string& name_or_shortcut); + +} // namespace Mu +#endif /* MU_MESSAGE_FIELDS_HH__ */ diff --git a/lib/mu-message.hh b/lib/mu-message.hh new file mode 100644 index 00000000..cea716ba --- /dev/null +++ b/lib/mu-message.hh @@ -0,0 +1,40 @@ +/* +** Copyright (C) 2022 Dirk-Jan C. Binnema +** +** 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 +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#ifndef MU_MESSAGE_HH__ +#define MU_MESSAGE_HH__ + +#include "mu-message-contact.hh" +#include "mu-message-priority.hh" +#include "mu-message-flags.hh" +#include "mu-message-fields.hh" + +namespace Mu { + +namespace Message { + +using Contact = MessageContact; +using Contacts = MessageContacts; +using Priority = MessagePriority; +using Flags = MessageFlags; +using Field = MessageField; + +} // Message +} // Mu +#endif /* MU_MESSAGE_HH__ */