lib: replace MuFlags with Mu::MessageFlags

Modernize the ancient MuFlags code to C++17
This commit is contained in:
Dirk-Jan C. Binnema 2022-02-16 21:56:02 +02:00
parent c5538d5b14
commit 473134a7b1
6 changed files with 429 additions and 587 deletions

View File

@ -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'
],

View File

@ -1,220 +0,0 @@
/*
** Copyright (C) 2011-2020 <djcb@djcbsoftware.nl>
**
** 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 <string.h>
#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;
}

View File

@ -1,171 +0,0 @@
/*
** Copyright (C) 2011-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <glib.h>
#include <utils/mu-utils.hh>
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,<fileinfo>",
* <fileinfo> 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__*/

119
lib/mu-message-flags.cc Normal file
View File

@ -0,0 +1,119 @@
/*
** Copyright (C) 2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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<MessageFlags>(1 << id);
if (flag != AllMessageFlagInfos[id].flag)
return false;
}
return true;
}
static_assert(AllMessageFlagInfos.size() ==
__builtin_ctz(static_cast<unsigned>(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<MessageFlags>(0)));
static_assert(!message_flag_info(static_cast<MessageFlags>(1<<AllMessageFlagInfos.size())),
"should be invalid");
/*
* message_flag_info
*/
static_assert(message_flag_info('D')->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));

308
lib/mu-message-flags.hh Normal file
View File

@ -0,0 +1,308 @@
/*
** Copyright (C) 2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <algorithm>
#include <optional>
#include <string_view>
#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,<fileinfo>",
* <fileinfo> 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 */
/*
* <private>
*/
_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<MessageFlagInfo, 12>
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<MessageFlagInfo>
message_flag_info(MessageFlags flag)
{
constexpr auto upper = static_cast<unsigned>(MessageFlags::_final_);
const auto val = static_cast<unsigned>(flag);
if (__builtin_popcount(val) != 1 || val >= upper)
return std::nullopt;
return AllMessageFlagInfos[static_cast<unsigned>(__builtin_ctz(val))];
}
/**
* Get flag info for some flag
*
* @param shortcut shortcut character
*
* @return the MessageFlagInfo
*/
constexpr const std::optional<MessageFlagInfo>
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<MessageFlagInfo>
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<MessageFlags>
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<MessageFlags>
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<MessageFlags>
message_flags_from_expr(std::string_view expr,
std::optional<MessageFlags> 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__ */

View File

@ -1,193 +0,0 @@
/*
** Copyright (C) 2008-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <glib.h>
#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;
}