diff --git a/lib/meson.build b/lib/meson.build index 4a50ab52..d9c31207 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -50,8 +50,6 @@ lib_mu=static_library( 'mu-xapian.hh', 'mu-maildir.cc', 'mu-maildir.hh', - 'mu-flags.cc', - 'mu-flags.hh', 'mu-msg-crypto.cc', 'mu-msg-doc.cc', 'mu-msg-doc.hh', @@ -65,7 +63,8 @@ lib_mu=static_library( 'mu-msg-sexp.cc', 'mu-msg.cc', 'mu-msg.hh', - 'mu-message-priority.hh' + 'mu-message-flags.hh', + 'mu-message-flags.cc', 'mu-message-priority.hh', 'mu-message-priority.cc' ], diff --git a/lib/mu-flags.cc b/lib/mu-flags.cc deleted file mode 100644 index 00d80998..00000000 --- a/lib/mu-flags.cc +++ /dev/null @@ -1,220 +0,0 @@ -/* -** Copyright (C) 2011-2020 -** -** 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 -#include "mu-flags.hh" - -using namespace Mu; - -struct FlagInfo { - MuFlags flag; - char kar; - const char* name; - MuFlagType flag_type; -}; - -static const FlagInfo FLAG_INFO[] = { - - /* NOTE: order of this is significant, due to optimizations - * below */ - - {MU_FLAG_DRAFT, 'D', "draft", MU_FLAG_TYPE_MAILFILE}, - {MU_FLAG_FLAGGED, 'F', "flagged", MU_FLAG_TYPE_MAILFILE}, - {MU_FLAG_PASSED, 'P', "passed", MU_FLAG_TYPE_MAILFILE}, - {MU_FLAG_REPLIED, 'R', "replied", MU_FLAG_TYPE_MAILFILE}, - {MU_FLAG_SEEN, 'S', "seen", MU_FLAG_TYPE_MAILFILE}, - {MU_FLAG_TRASHED, 'T', "trashed", MU_FLAG_TYPE_MAILFILE}, - - {MU_FLAG_NEW, 'N', "new", MU_FLAG_TYPE_MAILDIR}, - - {MU_FLAG_SIGNED, 'z', "signed", MU_FLAG_TYPE_CONTENT}, - {MU_FLAG_ENCRYPTED, 'x', "encrypted", MU_FLAG_TYPE_CONTENT}, - {MU_FLAG_HAS_ATTACH, 'a', "attach", MU_FLAG_TYPE_CONTENT}, - {MU_FLAG_LIST, 'l', "list", MU_FLAG_TYPE_CONTENT}, - - {MU_FLAG_UNREAD, 'u', "unread", MU_FLAG_TYPE_PSEUDO}}; - -MuFlagType -Mu::mu_flag_type(MuFlags flag) -{ - unsigned u; - - for (u = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) - if (FLAG_INFO[u].flag == flag) - return FLAG_INFO[u].flag_type; - - return MU_FLAG_TYPE_INVALID; -} - -char -Mu::mu_flag_char(MuFlags flag) -{ - unsigned u; - - for (u = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) - if (FLAG_INFO[u].flag == flag) - return FLAG_INFO[u].kar; - return 0; -} - -MuFlags -Mu::mu_flag_char_from_name(const char* str) -{ - unsigned u; - - g_return_val_if_fail(str, MU_FLAG_INVALID); - - for (u = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) - if (g_strcmp0(FLAG_INFO[u].name, str) == 0) - return (MuFlags)FLAG_INFO[u].kar; - - return (MuFlags)0; -} - -static MuFlags -mu_flag_from_char(char kar) -{ - unsigned u; - - for (u = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) - if (FLAG_INFO[u].kar == kar) - return FLAG_INFO[u].flag; - - return MU_FLAG_INVALID; -} - -const char* -Mu::mu_flag_name(MuFlags flag) -{ - unsigned u; - - for (u = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) - if (FLAG_INFO[u].flag == flag) - return FLAG_INFO[u].name; - - return NULL; -} - -const char* -Mu::mu_flags_to_str_s(MuFlags flags, MuFlagType types) -{ - unsigned u, v; - static char str[sizeof(FLAG_INFO) + 1]; - - for (u = 0, v = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) - if (flags & FLAG_INFO[u].flag && types & FLAG_INFO[u].flag_type) - str[v++] = FLAG_INFO[u].kar; - str[v] = '\0'; - - return str; -} - -MuFlags -Mu::mu_flags_from_str(const char* str, MuFlagType types, gboolean ignore_invalid) -{ - const char* cur; - MuFlags flag; - - g_return_val_if_fail(str, MU_FLAG_INVALID); - - for (cur = str, flag = MU_FLAG_NONE; *cur; ++cur) { - MuFlags f; - - f = mu_flag_from_char(*cur); - - if (f == MU_FLAG_INVALID) { - if (ignore_invalid) - continue; - return MU_FLAG_INVALID; - } - - if (mu_flag_type(f) & types) - flag |= f; - } - - return flag; -} - -char* -Mu::mu_flags_custom_from_str(const char* str) -{ - char* custom; - const char* cur; - unsigned u; - - g_return_val_if_fail(str, NULL); - - for (cur = str, u = 0, custom = NULL; *cur; ++cur) { - MuFlags flag; - flag = mu_flag_from_char(*cur); - - /* if it's a valid file flag, ignore it */ - if (flag != MU_FLAG_INVALID && mu_flag_type(flag) == MU_FLAG_TYPE_MAILFILE) - continue; - - /* otherwise, add it to our custom string */ - if (!custom) - custom = g_new0(char, strlen(str) + 1); - custom[u++] = *cur; - } - - return custom; -} - -void -Mu::mu_flags_foreach(MuFlagsForeachFunc func, gpointer user_data) -{ - unsigned u; - - g_return_if_fail(func); - - for (u = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u) - func(FLAG_INFO[u].flag, user_data); -} - -MuFlags -Mu::mu_flags_from_str_delta(const char* str, MuFlags oldflags, MuFlagType types) -{ - const char* cur; - MuFlags newflags; - - g_return_val_if_fail(str, MU_FLAG_INVALID); - - for (cur = str, newflags = oldflags; *cur; ++cur) { - MuFlags f; - if (*cur == '+' || *cur == '-') { - f = mu_flag_from_char(cur[1]); - if (f == 0) - goto error; - if (*cur == '+') - newflags |= f; - else - newflags &= ~f; - ++cur; - continue; - } - - goto error; - } - - return newflags; -error: - g_warning("invalid flag string"); - return MU_FLAG_INVALID; -} diff --git a/lib/mu-flags.hh b/lib/mu-flags.hh deleted file mode 100644 index 6e2046aa..00000000 --- a/lib/mu-flags.hh +++ /dev/null @@ -1,171 +0,0 @@ -/* -** Copyright (C) 2011-2020 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_FLAGS_HH__ -#define MU_FLAGS_HH__ - -#include -#include - -namespace Mu { - -enum MuFlags { - MU_FLAG_NONE = 0, - - /* next 6 are seen in the file-info part of maildir message - * file names, ie., in a name like "1234345346:2,", - * consists of zero or more of the following - * characters (in ascii order) */ - MU_FLAG_DRAFT = 1 << 0, - MU_FLAG_FLAGGED = 1 << 1, - MU_FLAG_PASSED = 1 << 2, - MU_FLAG_REPLIED = 1 << 3, - MU_FLAG_SEEN = 1 << 4, - MU_FLAG_TRASHED = 1 << 5, - - /* decides on cur/ or new/ in the maildir */ - MU_FLAG_NEW = 1 << 6, - - /* content flags -- not visible in the filename, but used for - * searching */ - MU_FLAG_SIGNED = 1 << 7, - MU_FLAG_ENCRYPTED = 1 << 8, - MU_FLAG_HAS_ATTACH = 1 << 9, - - /* pseudo-flag, only for queries, so we can search for - * flag:unread, which is equivalent to 'flag:new OR NOT - * flag:seen' */ - MU_FLAG_UNREAD = 1 << 10, - - /* other content flags */ - MU_FLAG_LIST = 1 << 11 -}; -MU_ENABLE_BITOPS(MuFlags); - -#define MU_FLAG_INVALID ((MuFlags)-1) - -enum MuFlagType { - MU_FLAG_TYPE_MAILFILE = 1 << 0, - MU_FLAG_TYPE_MAILDIR = 1 << 1, - MU_FLAG_TYPE_CONTENT = 1 << 2, - MU_FLAG_TYPE_PSEUDO = 1 << 3 -}; -MU_ENABLE_BITOPS(MuFlagType); - -#define MU_FLAG_TYPE_ANY ((MuFlagType)-1) -#define MU_FLAG_TYPE_INVALID ((MuFlagType)-1) - -/** - * Get the type of flag (mailfile, maildir, pseudo or content) - * - * @param flag a MuFlag - * - * @return the flag type or MU_FLAG_TYPE_INVALID in case of error - */ -MuFlagType mu_flag_type(MuFlags flag) G_GNUC_CONST; - -/** - * Get the flag character - * - * @param flag a MuFlag (single) - * - * @return the character, or 0 if it's not a valid flag - */ -char mu_flag_char(MuFlags flag) G_GNUC_CONST; - -/** - * Get the flag name - * - * @param flag a single MuFlag - * - * @return the name (don't free) as string or NULL in case of error - */ -const char* mu_flag_name(MuFlags flag) G_GNUC_CONST; - -/** - * Get the string representation of an OR'ed set of flags - * - * @param flags MuFlag (OR'ed) - * @param types allowable types (OR'ed) for the result; the rest is ignored - * - * @return The string representation (static, don't free), or NULL in - * case of error - */ -const char* mu_flags_to_str_s(MuFlags flags, MuFlagType types); - -/** - * Get the (OR'ed) flags corresponding to a string representation - * - * @param str the file info string - * @param types the flag types to accept (other will be ignored) - * @param ignore invalid if TRUE, ignore invalid flags, otherwise return - * MU_FLAG_INVALID if an invalid flag is encountered - * - * @return the (OR'ed) flags - */ -MuFlags mu_flags_from_str(const char* str, MuFlagType types, gboolean ignore_invalid); - -/** - * Get the MuFlag char for some flag name - * - * @param str a flag name - * - * @return a flag character, or 0 - */ -MuFlags mu_flag_char_from_name(const char* str); - -/** - * return the concatenation of all non-standard file flags in str - * (ie., characters other than DFPRST) as a newly allocated string. - * - * @param str the file info string - * - * @return concatenation of all non-standard flags, as a string; free - * with g_free when done. If there are no such flags, return NULL. - */ -char* mu_flags_custom_from_str(const char* str) G_GNUC_WARN_UNUSED_RESULT; - -/** - * Update #oldflags with the flags in #str, where #str consists of the - * the normal flag characters, but prefixed with either '+' or '-', - * which means resp. "add this flag" or "remove this flag" from - * oldflags. So, e.g. "-N+S" would unset the NEW flag and set the - * SEEN flag, without affecting other flags. - * - * @param str the string representation - * @param old flags to update - * @param types the flag types to accept (other will be ignored) - * - * @return - */ -MuFlags mu_flags_from_str_delta(const char* str, MuFlags oldflags, MuFlagType types); - -typedef void (*MuFlagsForeachFunc)(MuFlags flag, gpointer user_data); - -/** - * call a function for each available flag - * - * @param func a function to call - * @param user_data a user pointer to pass to the function - */ -void mu_flags_foreach(MuFlagsForeachFunc func, gpointer user_data); - -} // namespace Mu - -#endif /*__MU_FLAGS_H__*/ diff --git a/lib/mu-message-flags.cc b/lib/mu-message-flags.cc new file mode 100644 index 00000000..0abe675a --- /dev/null +++ b/lib/mu-message-flags.cc @@ -0,0 +1,119 @@ +/* +** 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. +** +*/ + +/* + * implementation is almost completely in the header; here we just add some + * compile-time tests. + */ + +#include "mu-message-flags.hh" + +using namespace Mu; + +std::string +Mu::message_flags_to_string(MessageFlags flags) +{ + std::string str; + + for (auto&& info: AllMessageFlagInfos) + if (any_of(info.flag & flags)) + str+=info.shortcut; + + return str; +} + + +/* + * flags & flag-info + */ +constexpr bool +validate_message_info_flags() +{ + for (auto id = 0U; id != AllMessageFlagInfos.size(); ++id) { + const auto flag = static_cast(1 << id); + if (flag != AllMessageFlagInfos[id].flag) + return false; + } + return true; +} + +static_assert(AllMessageFlagInfos.size() == + __builtin_ctz(static_cast(MessageFlags::_final_))); +static_assert(validate_message_info_flags()); + +static_assert(!!message_flag_info(MessageFlags::Encrypted)); +static_assert(!message_flag_info(MessageFlags::None)); +static_assert(!message_flag_info(static_cast(0))); +static_assert(!message_flag_info(static_cast(1<flag == MessageFlags::Draft); +static_assert(message_flag_info('l')->flag == MessageFlags::MailingList); +static_assert(!message_flag_info('q')); + +static_assert(message_flag_info("trashed")->flag == MessageFlags::Trashed); +static_assert(message_flag_info("attach")->flag == MessageFlags::HasAttachment); +static_assert(!message_flag_info("fnorb")); + + +/* + * message_flags_from_expr + */ +static_assert(message_flags_from_absolute_expr("SRP").value() == + (MessageFlags::Seen | MessageFlags::Replied | MessageFlags::Passed)); +static_assert(message_flags_from_absolute_expr("Faul").value() == + (MessageFlags::Flagged | MessageFlags::Unread | + MessageFlags::HasAttachment | MessageFlags::MailingList)); + +static_assert(!message_flags_from_absolute_expr("DRT?")); +static_assert(message_flags_from_absolute_expr("DRT?", true/*ignore invalid*/).value() == + (MessageFlags::Draft | MessageFlags::Replied | + MessageFlags::Trashed)); +static_assert(message_flags_from_absolute_expr("DFPNxulabcdef", true/*ignore invalid*/).value() == + (MessageFlags::Draft|MessageFlags::Flagged|MessageFlags::Passed| + MessageFlags::New | MessageFlags::Encrypted | + MessageFlags::Unread | MessageFlags::MailingList | + MessageFlags::HasAttachment)); + +/* + * message_flags_from_delta_expr + */ +static_assert(message_flags_from_delta_expr( + "+S-u-N", MessageFlags::New|MessageFlags::Unread).value() == + MessageFlags::Seen); +static_assert(message_flags_from_delta_expr("+R+P-F", MessageFlags::Seen).value() == + (MessageFlags::Seen|MessageFlags::Passed|MessageFlags::Replied)); +/* '-B' is invalid */ +static_assert(!message_flags_from_delta_expr("+R+P-B", MessageFlags::Seen)); +/* '-B' is invalid, but ignore invalid */ +static_assert(message_flags_from_delta_expr("+R+P-B", MessageFlags::Seen, true) == + (MessageFlags::Replied|MessageFlags::Passed|MessageFlags::Seen)); +static_assert(message_flags_from_delta_expr("+F+T-S", MessageFlags::None, true).value() == + (MessageFlags::Flagged|MessageFlags::Trashed)); + +/* + * message_flags_filter + */ +static_assert(message_flags_filter(message_flags_from_absolute_expr( + "DFPNxulabcdef", true/*ignore invalid*/).value(), + MessageFlagCategory::Mailfile) == + (MessageFlags::Draft|MessageFlags::Flagged|MessageFlags::Passed)); diff --git a/lib/mu-message-flags.hh b/lib/mu-message-flags.hh new file mode 100644 index 00000000..95a08dc3 --- /dev/null +++ b/lib/mu-message-flags.hh @@ -0,0 +1,308 @@ +/* +** 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_FLAGS_HH__ +#define MU_MESSAGE_FLAGS_HH__ + +#include +#include +#include +#include "utils/mu-utils.hh" + +namespace Mu { + +enum struct MessageFlags { + None = 0, /**< No flags */ + /** + * next 6 are seen in the file-info part of maildir message file + * names, ie., in a name like "1234345346:2,", + * consists of zero or more of the following + * characters (in ascii order) + */ + Draft = 1 << 0, /**< A draft message */ + Flagged = 1 << 1, /**< A flagged message */ + Passed = 1 << 2, /**< A passed (forwarded) message */ + Replied = 1 << 3, /**< A replied message */ + Seen = 1 << 4, /**< A seen (read) message */ + Trashed = 1 << 5, /**< A trashed message */ + + /** + * decides on cur/ or new/ in the maildir + */ + New = 1 << 6, /**< A new message */ + + /** + * content flags -- not visible in the filename, but used for + * searching + */ + Signed = 1 << 7, /**< Cryptographically signed */ + Encrypted = 1 << 8, /**< Encrypted */ + HasAttachment = 1 << 9, /**< Has an attachment */ + + Unread = 1 << 10, /**< Unread; pseudo-flag, only for queries, so + * we can search for flag:unread, which is + * equivalent to 'flag:new OR NOT + * flag:seen' */ + /** + * other content flags + */ + MailingList = 1 << 11,/**< A mailing-list message */ + + /* + * + */ + _final_ = 1 << 12 +}; +MU_ENABLE_BITOPS(MessageFlags); + +/** + * Message flags category + * + */ +enum struct MessageFlagCategory { + None, /**< Nothing */ + Mailfile, /**< Flag for a message file */ + Maildir, /**< Flag for message file's location */ + Content, /**< Message content flag */ + Pseudo /**< Pseuodo flag */ +}; + + +/** + * Info about invidual message flags + * + */ +struct MessageFlagInfo { + MessageFlags flag; /**< The message flag */ + char shortcut; /**< Shortcut character */ + std::string_view name; /**< Name of the flag */ + MessageFlagCategory category; /**< Flag category */ +}; + +/** + * Array of all flag information. + */ +constexpr std::array +AllMessageFlagInfos = {{ + {MessageFlags::Draft, 'D', "draft", MessageFlagCategory::Mailfile}, + {MessageFlags::Flagged, 'F', "flagged", MessageFlagCategory::Mailfile}, + {MessageFlags::Passed, 'P', "passed", MessageFlagCategory::Mailfile}, + {MessageFlags::Replied, 'R', "replied", MessageFlagCategory::Mailfile}, + {MessageFlags::Seen, 'S', "seen", MessageFlagCategory::Mailfile}, + {MessageFlags::Trashed, 'T', "trashed", MessageFlagCategory::Mailfile}, + + {MessageFlags::New, 'N', "new", MessageFlagCategory::Maildir}, + + {MessageFlags::Signed, 'z', "signed", MessageFlagCategory::Content}, + {MessageFlags::Encrypted, 'x', "encrypted", MessageFlagCategory::Content}, + {MessageFlags::HasAttachment, 'a', "attach", MessageFlagCategory::Content}, + + {MessageFlags::Unread, 'u', "unread", MessageFlagCategory::Pseudo}, + + {MessageFlags::MailingList, 'l', "list", MessageFlagCategory::Content}, +}}; + +/** + * Get flag info for some flag + * + * @param flag a singular flag + * + * @return the MessageFlagInfo, or std::nullopt in case of error. + */ +constexpr const std::optional +message_flag_info(MessageFlags flag) +{ + constexpr auto upper = static_cast(MessageFlags::_final_); + const auto val = static_cast(flag); + + if (__builtin_popcount(val) != 1 || val >= upper) + return std::nullopt; + + return AllMessageFlagInfos[static_cast(__builtin_ctz(val))]; +} + + +/** + * Get flag info for some flag + * + * @param shortcut shortcut character + * + * @return the MessageFlagInfo + */ +constexpr const std::optional +message_flag_info(char shortcut) +{ + for (auto&& info: AllMessageFlagInfos) + if (info.shortcut == shortcut) + return info; + + return std::nullopt; +} + +/** + * Get flag info for some flag + * + * @param name of the message-flag. + * + * @return the MessageFlagInfo + */ +constexpr const std::optional +message_flag_info(std::string_view name) +{ + for (auto&& info: AllMessageFlagInfos) + if (info.name == name) + return info; + + return std::nullopt; +} + + +/** + * There are two string-based expression types for flags: + * 1) 'absolute': replace the existing flags + * 2_ 'delta' : flags as a delta of existing flags. + */ + +/** + * Get the (OR'ed) flags corresponding to an expression. + * + * @param expr the expression (a sequence of flag shortcut characters) + * @param ignore_invalid if @true, ignore invalid flags, otherwise return + * nullopt if an invalid flag is encountered + * + * @return the (OR'ed) flags or MessageFlags::None + */ +constexpr std::optional +message_flags_from_absolute_expr(std::string_view expr, + bool ignore_invalid=false) +{ + MessageFlags flags{MessageFlags::None}; + + for (auto&& kar: expr) { + if (const auto& info{message_flag_info(kar)}; !info) { + if (!ignore_invalid) + return std::nullopt; + } else + flags |= info->flag; + } + + return flags; +} + +/** + * Calculate flags from existing flags and a delta expression + * + * Update @p flags with the flags in @p expr, where @p exprt consists of the the + * normal flag shortcut characters, prefixed with either '+' or '-', which means + * resp. "add this flag" or "remove this flag". + * + * So, e.g. "-N+S" would unset the NEW flag and set the SEEN flag, without + * affecting other flags. + * + * @param expr delta expression + * @param flags existing flags + * @param ignore_invalid if @true, ignore invalid flags, otherwise return + * nullopt if an invalid flag is encountered + * + * @return new flags, or nullopt in case of error + */ +constexpr std::optional +message_flags_from_delta_expr(std::string_view expr, MessageFlags flags, + bool ignore_invalid=false) +{ + if (expr.size() % 2 != 0) + return std::nullopt; + + for (auto u = 0U; u != expr.size(); u+=2) { + + if (const auto& info{message_flag_info(expr[u+1])}; !info) { + if (!ignore_invalid) + return std::nullopt; + } else { + switch(expr[u]) { + case '+': + flags |= info->flag; + break; + case '-': + flags &= ~info->flag; + break; + default: + if (!ignore_invalid) + return std::nullopt; + break; + } + } + } + + return flags; +} + + + +/** + * Calculate the flags from either 'absolute' or 'delta' expressions + * + * @param expr a flag expression, either 'delta' or 'absolute' + * @param flags optional: existing flags or none. Required for delta. + * + * @return either messages flags or std::nullopt in case of error. + */ +constexpr std::optional +message_flags_from_expr(std::string_view expr, + std::optional flags=std::nullopt) +{ + if (expr.empty()) + return std::nullopt; + + if (expr[0] == '+' || expr[0] == '-') + return message_flags_from_delta_expr( + expr, flags.value_or(MessageFlags::None), true); + else + return message_flags_from_absolute_expr(expr, true); +} + +/** + * Filter out flags which are not in the given category + * + * @param flags flags + * @param cat category + * + * @return filter flags + */ +constexpr MessageFlags +message_flags_filter(MessageFlags flags, MessageFlagCategory cat) +{ + for (auto&& info: AllMessageFlagInfos) + if (info.category != cat) + flags &= ~info.flag; + return flags; +} + +/** + * Get a string representation of flags + * + * @param flags flags + * + * @return string as a sequence of message-flag shortcuts + */ +std::string message_flags_to_string(MessageFlags flags); + +} // namepace Mu + +#endif /* MU_MESSAGE_FLAGS_HH__ */ diff --git a/lib/tests/test-mu-flags.cc b/lib/tests/test-mu-flags.cc deleted file mode 100644 index df1e0bed..00000000 --- a/lib/tests/test-mu-flags.cc +++ /dev/null @@ -1,193 +0,0 @@ -/* -** Copyright (C) 2008-2020 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. -** -*/ - -#if HAVE_CONFIG_H -#include "config.h" -#endif /*HAVE_CONFIG_H*/ - -#include -#include "mu-flags.hh" -#include "test-mu-common.hh" - -using namespace Mu; - -static void -test_mu_flag_char(void) -{ - g_assert_cmpuint(mu_flag_char(MU_FLAG_DRAFT), ==, 'D'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_FLAGGED), ==, 'F'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_PASSED), ==, 'P'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_REPLIED), ==, 'R'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_SEEN), ==, 'S'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_TRASHED), ==, 'T'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_NEW), ==, 'N'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_SIGNED), ==, 'z'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_ENCRYPTED), ==, 'x'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_HAS_ATTACH), ==, 'a'); - g_assert_cmpuint(mu_flag_char(MU_FLAG_UNREAD), ==, 'u'); - g_assert_cmpuint(mu_flag_char((MuFlags)12345), ==, 0); -} - -static void -test_mu_flag_name(void) -{ - g_assert_cmpstr(mu_flag_name(MU_FLAG_DRAFT), ==, "draft"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_FLAGGED), ==, "flagged"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_PASSED), ==, "passed"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_REPLIED), ==, "replied"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_SEEN), ==, "seen"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_TRASHED), ==, "trashed"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_NEW), ==, "new"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_SIGNED), ==, "signed"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_ENCRYPTED), ==, "encrypted"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_HAS_ATTACH), ==, "attach"); - g_assert_cmpstr(mu_flag_name(MU_FLAG_UNREAD), ==, "unread"); - g_assert_cmpstr(mu_flag_name((MuFlags)12345), ==, NULL); -} - -static void -test_mu_flags_to_str_s(void) -{ - g_assert_cmpstr( - mu_flags_to_str_s((MuFlags)(MU_FLAG_PASSED | MU_FLAG_SIGNED), MU_FLAG_TYPE_ANY), - ==, - "Pz"); - g_assert_cmpstr(mu_flags_to_str_s(MU_FLAG_NEW, MU_FLAG_TYPE_ANY), ==, "N"); - g_assert_cmpstr( - mu_flags_to_str_s((MuFlags)(MU_FLAG_HAS_ATTACH | MU_FLAG_TRASHED), MU_FLAG_TYPE_ANY), - ==, - "Ta"); - g_assert_cmpstr(mu_flags_to_str_s(MU_FLAG_NONE, MU_FLAG_TYPE_ANY), ==, ""); - - g_assert_cmpstr( - mu_flags_to_str_s((MuFlags)(MU_FLAG_PASSED | MU_FLAG_SIGNED), MU_FLAG_TYPE_CONTENT), - ==, - "z"); - - g_assert_cmpstr(mu_flags_to_str_s(MU_FLAG_NEW, MU_FLAG_TYPE_MAILDIR), ==, "N"); - g_assert_cmpstr(mu_flags_to_str_s((MuFlags)(MU_FLAG_HAS_ATTACH | MU_FLAG_TRASHED), - MU_FLAG_TYPE_MAILFILE), - ==, - "T"); - - g_assert_cmpstr(mu_flags_to_str_s(MU_FLAG_NONE, MU_FLAG_TYPE_PSEUDO), ==, ""); -} - -static void -test_mu_flags_from_str(void) -{ - /* note, the 3rd arg to mu_flags_from_str determines whether - * invalid flags will be ignored (if TRUE) or MU_FLAG_INVALID (if FALSE) - */ - - g_assert_cmpuint(mu_flags_from_str("RP", MU_FLAG_TYPE_ANY, TRUE), - ==, - (MuFlags)(MU_FLAG_REPLIED | MU_FLAG_PASSED)); - g_assert_cmpuint(mu_flags_from_str("Nz", MU_FLAG_TYPE_ANY, TRUE), - ==, - MU_FLAG_NEW | MU_FLAG_SIGNED); - g_assert_cmpuint(mu_flags_from_str("axD", MU_FLAG_TYPE_ANY, TRUE), - ==, - (MuFlags)(MU_FLAG_HAS_ATTACH | MU_FLAG_ENCRYPTED | MU_FLAG_DRAFT)); - - g_assert_cmpuint(mu_flags_from_str("RP", MU_FLAG_TYPE_MAILFILE, TRUE), - ==, - MU_FLAG_REPLIED | MU_FLAG_PASSED); - g_assert_cmpuint(mu_flags_from_str("Nz", MU_FLAG_TYPE_MAILFILE, TRUE), ==, MU_FLAG_NONE); - - /* ignore errors or not */ - g_assert_cmpuint(mu_flags_from_str("qwi", MU_FLAG_TYPE_MAILFILE, FALSE), - ==, - MU_FLAG_INVALID); - g_assert_cmpuint(mu_flags_from_str("qwi", MU_FLAG_TYPE_MAILFILE, TRUE), ==, 0); -} - -static void -test_mu_flags_from_str_delta(void) -{ - g_assert_cmpuint(mu_flags_from_str_delta("+S-R", - (MuFlags)(MU_FLAG_REPLIED | MU_FLAG_DRAFT), - MU_FLAG_TYPE_ANY), - ==, - (MuFlags)(MU_FLAG_SEEN | MU_FLAG_DRAFT)); - - g_assert_cmpuint(mu_flags_from_str_delta("", - (MuFlags)(MU_FLAG_REPLIED | MU_FLAG_DRAFT), - MU_FLAG_TYPE_ANY), - ==, - (MuFlags)(MU_FLAG_REPLIED | MU_FLAG_DRAFT)); - - g_assert_cmpuint(mu_flags_from_str_delta("-N+P+S-D", - (MuFlags)(MU_FLAG_SIGNED | MU_FLAG_DRAFT), - MU_FLAG_TYPE_ANY), - ==, - (MuFlags)(MU_FLAG_PASSED | MU_FLAG_SEEN | MU_FLAG_SIGNED)); -} - -static void -test_mu_flags_custom_from_str(void) -{ - unsigned u; - - struct { - const char* str; - const char* expected; - } cases[] = {{"ABC", "ABC"}, - {"PAF", "A"}, - {"ShelloPwoFrDldR123", "helloworld123"}, - {"SPD", NULL}}; - - for (u = 0; u != G_N_ELEMENTS(cases); ++u) { - char* cust; - cust = mu_flags_custom_from_str(cases[u].str); - if (g_test_verbose()) - g_print("%s: str:%s; got:%s; expected:%s\n", - __func__, - cases[u].str, - cust, - cases[u].expected); - g_assert_cmpstr(cust, ==, cases[u].expected); - g_free(cust); - } -} - -int -main(int argc, char* argv[]) -{ - int rv; - g_test_init(&argc, &argv, NULL); - - /* mu_msg_str_date */ - g_test_add_func("/mu-flags/test-mu-flag-char", test_mu_flag_char); - g_test_add_func("/mu-flags/test-mu-flag-name", test_mu_flag_name); - g_test_add_func("/mu-flags/test-mu-flags-to-str-s", test_mu_flags_to_str_s); - g_test_add_func("/mu-flags/test-mu-flags-from-str", test_mu_flags_from_str); - g_test_add_func("/mu-flags/test-mu-flags-from-str-delta", test_mu_flags_from_str_delta); - g_test_add_func("/mu-flags/test-mu-flags-custom-from-str", test_mu_flags_custom_from_str); - - g_log_set_handler( - NULL, - (GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), - (GLogFunc)black_hole, - NULL); - - rv = g_test_run(); - - return rv; -}