mirror of https://github.com/djcb/mu.git
lib: update mu-contact-{fields,contact,flags,priority}
Improve cohesion and tests.
This commit is contained in:
parent
45499efef8
commit
c3ae3da0de
|
@ -92,7 +92,7 @@ libmu_la_SOURCES= \
|
||||||
mu-msg-doc.cc \
|
mu-msg-doc.cc \
|
||||||
mu-msg-doc.hh \
|
mu-msg-doc.hh \
|
||||||
mu-msg-fields.c \
|
mu-msg-fields.c \
|
||||||
mu-msg-fields.h \
|
mu-message-fields.hh \
|
||||||
mu-msg-file.cc \
|
mu-msg-file.cc \
|
||||||
mu-msg-file.hh \
|
mu-msg-file.hh \
|
||||||
mu-msg-part.cc \
|
mu-msg-part.cc \
|
||||||
|
|
|
@ -53,8 +53,7 @@ lib_mu=static_library(
|
||||||
'mu-msg-crypto.cc',
|
'mu-msg-crypto.cc',
|
||||||
'mu-msg-doc.cc',
|
'mu-msg-doc.cc',
|
||||||
'mu-msg-doc.hh',
|
'mu-msg-doc.hh',
|
||||||
'mu-msg-fields.c',
|
'mu-message-fields.hh',
|
||||||
'mu-msg-fields.h',
|
|
||||||
'mu-msg-file.cc',
|
'mu-msg-file.cc',
|
||||||
'mu-msg-file.hh',
|
'mu-msg-file.hh',
|
||||||
'mu-msg-part.cc',
|
'mu-msg-part.cc',
|
||||||
|
|
|
@ -26,10 +26,11 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
|
#include "mu-message-fields.hh"
|
||||||
|
|
||||||
struct _InternetAddressList;
|
struct _InternetAddressList;
|
||||||
|
|
||||||
namespace Mu {
|
namespace Mu {
|
||||||
|
@ -125,6 +126,27 @@ struct MessageContact {
|
||||||
return cached_hash;
|
return cached_hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the MessageField for this contact-type, if any.
|
||||||
|
*
|
||||||
|
* @return the message-field or nullopt.
|
||||||
|
*/
|
||||||
|
constexpr std::optional<MessageField> 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
|
* data members
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -21,23 +21,24 @@
|
||||||
|
|
||||||
using namespace Mu;
|
using namespace Mu;
|
||||||
|
|
||||||
std::optional<MessageField::Id>
|
std::string
|
||||||
message_field_id(const std::string& name_or_shortcut)
|
MessageField::xapian_term(const std::string& s) const
|
||||||
{
|
{
|
||||||
const auto it = std::find_if(
|
return std::string(1U, xapian_prefix()) + s;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
* compile-time checks
|
||||||
|
@ -65,9 +66,6 @@ validate_message_field_shortcuts()
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tests... also build as runtime-tests, so we can get coverage info
|
* tests... also build as runtime-tests, so we can get coverage info
|
||||||
*/
|
*/
|
||||||
|
@ -90,7 +88,29 @@ test_shortcuts()
|
||||||
static_assert(validate_message_field_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
|
#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
|
int
|
||||||
main(int argc, char* argv[])
|
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/ids", test_ids);
|
||||||
g_test_add_func("/message/fields/shortcuts", test_shortcuts);
|
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();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,11 @@ struct MessageField {
|
||||||
return static_cast<size_t>(Id::_count_);
|
return static_cast<size_t>(Id::_count_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr Xapian::valueno value_no() const {
|
||||||
|
return static_cast<Xapian::valueno>(id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field types
|
* Field types
|
||||||
*
|
*
|
||||||
|
@ -101,6 +106,13 @@ struct MessageField {
|
||||||
Integer, /**< An integer */
|
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
|
* Field flags
|
||||||
* note: the differences for our purposes between a xapian field and a
|
* 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 */
|
/**< Field-text is indexed in xapian (i.e., the text is processed */
|
||||||
Searchable = 1 << 2,
|
Searchable = 1 << 2,
|
||||||
/**< Field is a searchable term */
|
/**< Field is a searchable term */
|
||||||
Value = 1 << 3,
|
Value = 1 << 3,
|
||||||
/**< Field value is stored (so the literal value can be retrieved) */
|
/**< 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 */
|
/**< field contains one or more * e-mail-addresses */
|
||||||
XapianBoolean = 1 << 5,
|
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 */
|
* fields */
|
||||||
DoNotCache = 1 << 6,
|
DoNotCache = 1 << 6,
|
||||||
/**< don't cache this field in * the MuMsg cache */
|
/**< don't cache this field in * the MuMsg cache */
|
||||||
Range = 1 << 7 /**< whether this is a range field (e.g., date, size)*/
|
Range = 1 << 7 /**< whether this is a range field (e.g., date, size)*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr bool any_of(Flag some_flag) const{
|
||||||
|
return (static_cast<int>(some_flag) & static_cast<int>(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
|
* Field members
|
||||||
*
|
*
|
||||||
|
@ -152,16 +177,9 @@ struct MessageField {
|
||||||
return shortcut == 0 ? 0 : shortcut - ('a' - 'A');
|
return shortcut == 0 ? 0 : shortcut - ('a' - 'A');
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr Xapian::valueno value_no() const {
|
std::string xapian_term(const std::string& s="") const;
|
||||||
return static_cast<Xapian::valueno>(id);
|
std::string xapian_term(std::string_view sv) const;
|
||||||
}
|
std::string xapian_term(char c) const;
|
||||||
|
|
||||||
constexpr bool is_numeric() const
|
|
||||||
{
|
|
||||||
return type == Type::ByteSize ||
|
|
||||||
type == Type::TimeT ||
|
|
||||||
type == Type::Integer;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MU_ENABLE_BITOPS(MessageField::Flag);
|
MU_ENABLE_BITOPS(MessageField::Flag);
|
||||||
|
@ -282,7 +300,7 @@ static constexpr std::array<MessageField, MessageField::id_size()>
|
||||||
"path",
|
"path",
|
||||||
"File system path to message",
|
"File system path to message",
|
||||||
{},
|
{},
|
||||||
'i',
|
'p',
|
||||||
MessageField::Flag::GMime |
|
MessageField::Flag::GMime |
|
||||||
MessageField::Flag::XapianBoolean |
|
MessageField::Flag::XapianBoolean |
|
||||||
MessageField::Flag::Value},
|
MessageField::Flag::Value},
|
||||||
|
@ -304,7 +322,7 @@ static constexpr std::array<MessageField, MessageField::id_size()>
|
||||||
{
|
{
|
||||||
MessageField::Id::To,
|
MessageField::Id::To,
|
||||||
MessageField::Type::String,
|
MessageField::Type::String,
|
||||||
"To",
|
"to",
|
||||||
"Message recipient",
|
"Message recipient",
|
||||||
"to:flimflam@example.com",
|
"to:flimflam@example.com",
|
||||||
't',
|
't',
|
||||||
|
@ -428,6 +446,32 @@ message_field(MessageField::Id id)
|
||||||
return MessageFields.at(static_cast<size_t>(id));
|
return MessageFields.at(static_cast<size_t>(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke func for each message-field
|
||||||
|
*
|
||||||
|
* @param func some callable
|
||||||
|
*/
|
||||||
|
template <typename Func>
|
||||||
|
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 <typename Pred>
|
||||||
|
std::optional<MessageField::Id> 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
|
* 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.
|
* @return the message-field-id or nullopt.
|
||||||
*/
|
*/
|
||||||
std::optional<MessageField::Id> message_field_id(const std::string& name_or_shortcut);
|
static inline
|
||||||
|
std::optional<MessageField::Id> message_field_id(char shortcut) {
|
||||||
|
return message_field_find_if([&](auto&& field ){
|
||||||
|
return field.shortcut == shortcut;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static inline
|
||||||
|
std::optional<MessageField::Id> 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<MessageField::Id> message_field_id(size_t id)
|
||||||
|
{
|
||||||
|
if (id >= static_cast<size_t>(MessageField::Id::_count_))
|
||||||
|
return std::nullopt;
|
||||||
|
else
|
||||||
|
return static_cast<MessageField::Id>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Mu
|
} // namespace Mu
|
||||||
#endif /* MU_MESSAGE_FIELDS_HH__ */
|
#endif /* MU_MESSAGE_FIELDS_HH__ */
|
||||||
|
|
|
@ -93,9 +93,9 @@ struct MessageFlagInfo {
|
||||||
std::string_view name; /**< Name of the flag */
|
std::string_view name; /**< Name of the flag */
|
||||||
MessageFlagCategory category; /**< Flag category */
|
MessageFlagCategory category; /**< Flag category */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the lower-case version of shortcut
|
* Get the lower-case version of shortcut
|
||||||
*
|
*
|
||||||
* @return lower-case shortcut
|
* @return lower-case shortcut
|
||||||
*/
|
*/
|
||||||
constexpr char shortcut_lower() const {
|
constexpr char shortcut_lower() const {
|
||||||
|
@ -108,26 +108,39 @@ struct MessageFlagInfo {
|
||||||
* Array of all flag information.
|
* Array of all flag information.
|
||||||
*/
|
*/
|
||||||
constexpr std::array<MessageFlagInfo, 12> AllMessageFlagInfos = {{
|
constexpr std::array<MessageFlagInfo, 12> AllMessageFlagInfos = {{
|
||||||
MessageFlagInfo{MessageFlags::Draft, 'D', "draft", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{MessageFlags::Draft, 'D', "draft", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Flagged, 'F', "flagged", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{MessageFlags::Flagged, 'F', "flagged", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Passed, 'P', "passed", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{MessageFlags::Passed, 'P', "passed", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Replied, 'R', "replied", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{MessageFlags::Replied, 'R', "replied", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Seen, 'S', "seen", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{MessageFlags::Seen, 'S', "seen", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Trashed, 'T', "trashed", 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::Signed, 'z', "signed", MessageFlagCategory::Content},
|
||||||
MessageFlagInfo{MessageFlags::Encrypted, 'x', "encrypted",
|
MessageFlagInfo{MessageFlags::Encrypted, 'x', "encrypted",
|
||||||
MessageFlagCategory::Content},
|
MessageFlagCategory::Content},
|
||||||
MessageFlagInfo{MessageFlags::HasAttachment, 'a', "attach",
|
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<typename Func>
|
||||||
|
constexpr void message_flag_infos_for_each(Func&& func)
|
||||||
|
{
|
||||||
|
for (auto&& info: AllMessageFlagInfos)
|
||||||
|
func(info);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get flag info for some flag
|
* Get flag info for some flag
|
||||||
*
|
*
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include "mu-message-fields.hh"
|
||||||
|
|
||||||
namespace Mu {
|
namespace Mu {
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue