mirror of https://github.com/djcb/mu.git
lib: replace MuFlags with Mu::MessageFlags
Modernize the ancient MuFlags code to C++17
This commit is contained in:
parent
c5538d5b14
commit
473134a7b1
|
@ -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'
|
||||
],
|
||||
|
|
220
lib/mu-flags.cc
220
lib/mu-flags.cc
|
@ -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;
|
||||
}
|
171
lib/mu-flags.hh
171
lib/mu-flags.hh
|
@ -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__*/
|
|
@ -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));
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue