mirror of https://github.com/djcb/mu.git
message: move to lib/message, update naming
Basically, move/rename lib/mu-message* to lib/mu-*. Add the beginnings of a Message class.
This commit is contained in:
parent
7774261526
commit
4c4fb1759f
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
#include "mu-guile-message.hh"
|
#include "mu-guile-message.hh"
|
||||||
#include "libguile/scm.h"
|
#include "libguile/scm.h"
|
||||||
#include "mu-message-flags.hh"
|
#include "mu-message.hh"
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
@ -213,12 +213,12 @@ SCM_DEFINE(get_field,
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
static SCM
|
static SCM
|
||||||
contacts_to_list(MuMsg *msg, Mu::MessageContact::Type mtype)
|
contacts_to_list(MuMsg *msg, std::optional<Field::Id> field_id)
|
||||||
{
|
{
|
||||||
SCM list{SCM_EOL};
|
SCM list{SCM_EOL};
|
||||||
|
|
||||||
const auto contacts{mu_msg_get_contacts(msg, mtype)};
|
const auto contacts{mu_msg_get_contacts(msg, field_id)};
|
||||||
for (auto&& contact: mu_msg_get_contacts(msg, mtype)) {
|
for (auto&& contact: mu_msg_get_contacts(msg, field_id)) {
|
||||||
SCM item{scm_list_1(
|
SCM item{scm_list_1(
|
||||||
scm_cons(mu_guile_scm_from_str(contact.name.c_str()),
|
scm_cons(mu_guile_scm_from_str(contact.name.c_str()),
|
||||||
mu_guile_scm_from_str(contact.email.c_str())))};
|
mu_guile_scm_from_str(contact.email.c_str())))};
|
||||||
|
@ -239,7 +239,7 @@ SCM_DEFINE(get_contacts,
|
||||||
{
|
{
|
||||||
MuMsgWrapper* msgwrap;
|
MuMsgWrapper* msgwrap;
|
||||||
SCM list;
|
SCM list;
|
||||||
Mu::MessageContact::Type mtype;
|
|
||||||
|
|
||||||
MU_GUILE_INITIALIZED_OR_ERROR;
|
MU_GUILE_INITIALIZED_OR_ERROR;
|
||||||
|
|
||||||
|
@ -252,17 +252,18 @@ SCM_DEFINE(get_contacts,
|
||||||
if (CONTACT_TYPE == SCM_BOOL_F)
|
if (CONTACT_TYPE == SCM_BOOL_F)
|
||||||
return SCM_UNSPECIFIED; /* nothing to do */
|
return SCM_UNSPECIFIED; /* nothing to do */
|
||||||
|
|
||||||
|
std::optional<Field::Id> field_id;
|
||||||
if (CONTACT_TYPE == SCM_BOOL_T)
|
if (CONTACT_TYPE == SCM_BOOL_T)
|
||||||
mtype = Mu::MessageContact::Type::Unknown; /* get all */
|
field_id = {}; /* get all */
|
||||||
else {
|
else {
|
||||||
if (scm_is_eq(CONTACT_TYPE, SYMB_CONTACT_TO))
|
if (scm_is_eq(CONTACT_TYPE, SYMB_CONTACT_TO))
|
||||||
mtype = Mu::MessageContact::Type::To;
|
field_id = Field::Id::To;
|
||||||
else if (scm_is_eq(CONTACT_TYPE, SYMB_CONTACT_CC))
|
else if (scm_is_eq(CONTACT_TYPE, SYMB_CONTACT_CC))
|
||||||
mtype = Mu::MessageContact::Type::Cc;
|
field_id = Field::Id::Cc;
|
||||||
else if (scm_is_eq(CONTACT_TYPE, SYMB_CONTACT_BCC))
|
else if (scm_is_eq(CONTACT_TYPE, SYMB_CONTACT_BCC))
|
||||||
mtype = Mu::MessageContact::Type::Bcc;
|
field_id = Field::Id::Bcc;
|
||||||
else if (scm_is_eq(CONTACT_TYPE, SYMB_CONTACT_FROM))
|
else if (scm_is_eq(CONTACT_TYPE, SYMB_CONTACT_FROM))
|
||||||
mtype = Mu::MessageContact::Type::From;
|
field_id = Field::Id::From;
|
||||||
else {
|
else {
|
||||||
mu_guile_error(FUNC_NAME, 0, "invalid contact type", SCM_UNDEFINED);
|
mu_guile_error(FUNC_NAME, 0, "invalid contact type", SCM_UNDEFINED);
|
||||||
return SCM_UNSPECIFIED;
|
return SCM_UNSPECIFIED;
|
||||||
|
@ -273,7 +274,7 @@ SCM_DEFINE(get_contacts,
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||||
list = contacts_to_list(msgwrap->_msg, mtype);
|
list = contacts_to_list(msgwrap->_msg, field_id);
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
/* explicitly close the file backend, so we won't run out of fds */
|
/* explicitly close the file backend, so we won't run out of fds */
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
|
|
||||||
subdir('utils')
|
subdir('utils')
|
||||||
|
subdir('message')
|
||||||
subdir('index')
|
subdir('index')
|
||||||
|
|
||||||
lib_mu=static_library(
|
lib_mu=static_library(
|
||||||
|
@ -61,16 +62,6 @@ lib_mu=static_library(
|
||||||
'mu-msg-sexp.cc',
|
'mu-msg-sexp.cc',
|
||||||
'mu-msg.cc',
|
'mu-msg.cc',
|
||||||
'mu-msg.hh',
|
'mu-msg.hh',
|
||||||
'mu-message-contact.hh',
|
|
||||||
'mu-message-contact.cc',
|
|
||||||
'mu-message-document.cc',
|
|
||||||
'mu-message-document.hh',
|
|
||||||
'mu-message-fields.hh',
|
|
||||||
'mu-message-fields.cc',
|
|
||||||
'mu-message-flags.hh',
|
|
||||||
'mu-message-flags.cc',
|
|
||||||
'mu-message-priority.hh',
|
|
||||||
'mu-message-priority.cc'
|
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
glib_dep,
|
glib_dep,
|
||||||
|
@ -80,6 +71,7 @@ lib_mu=static_library(
|
||||||
guile_dep,
|
guile_dep,
|
||||||
config_h_dep,
|
config_h_dep,
|
||||||
lib_mu_utils_dep,
|
lib_mu_utils_dep,
|
||||||
|
lib_mu_message_dep,
|
||||||
lib_mu_index_dep
|
lib_mu_index_dep
|
||||||
],
|
],
|
||||||
install: false)
|
install: false)
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
## Copyright (C) 2021 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 of the License, 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.
|
||||||
|
|
||||||
|
|
||||||
|
lib_mu_message=static_library(
|
||||||
|
'mu-message',
|
||||||
|
[
|
||||||
|
'mu-message.cc',
|
||||||
|
'mu-message.hh',
|
||||||
|
'mu-contact.hh',
|
||||||
|
'mu-contact.cc',
|
||||||
|
'mu-document.cc',
|
||||||
|
'mu-document.hh',
|
||||||
|
'mu-fields.hh',
|
||||||
|
'mu-fields.cc',
|
||||||
|
'mu-flags.hh',
|
||||||
|
'mu-flags.cc',
|
||||||
|
'mu-priority.hh',
|
||||||
|
'mu-priority.cc'
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
glib_dep,
|
||||||
|
gmime_dep,
|
||||||
|
xapian_dep,
|
||||||
|
config_h_dep,
|
||||||
|
lib_mu_utils_dep],
|
||||||
|
install: false)
|
||||||
|
|
||||||
|
# some of the libme headers include xapian
|
||||||
|
xapian_incs = xapian_dep.get_pkgconfig_variable('includedir')
|
||||||
|
lib_mu_message_dep = declare_dependency(
|
||||||
|
link_with: lib_mu_message,
|
||||||
|
include_directories:
|
||||||
|
include_directories(['.', '..', xapian_incs]))
|
||||||
|
|
||||||
|
#
|
||||||
|
# tests
|
||||||
|
#
|
||||||
|
|
||||||
|
test('test-contact',
|
||||||
|
executable('test-contact',
|
||||||
|
'mu-contact.cc',
|
||||||
|
install: false,
|
||||||
|
cpp_args: ['-DBUILD_TESTS'],
|
||||||
|
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||||
|
|
||||||
|
test('test-document',
|
||||||
|
executable('test-document',
|
||||||
|
'mu-document.cc',
|
||||||
|
install: false,
|
||||||
|
cpp_args: ['-DBUILD_TESTS'],
|
||||||
|
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||||
|
|
||||||
|
test('test-fields',
|
||||||
|
executable('test-fields',
|
||||||
|
'mu-fields.cc',
|
||||||
|
install: false,
|
||||||
|
cpp_args: ['-DBUILD_TESTS'],
|
||||||
|
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||||
|
|
||||||
|
test('test-flags',
|
||||||
|
executable('test-flags',
|
||||||
|
'mu-flags.cc',
|
||||||
|
install: false,
|
||||||
|
cpp_args: ['-DBUILD_TESTS'],
|
||||||
|
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
||||||
|
test('test-priority',
|
||||||
|
executable('test-priority',
|
||||||
|
'mu-priority.cc',
|
||||||
|
install: false,
|
||||||
|
cpp_args: ['-DBUILD_TESTS'],
|
||||||
|
dependencies: [glib_dep, gmime_dep, lib_mu_message_dep]))
|
|
@ -17,17 +17,16 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mu-message-contact.hh"
|
#include "mu-contact.hh"
|
||||||
#include "mu-message.hh"
|
#include "mu-message.hh"
|
||||||
|
|
||||||
#include <gmime/gmime.h>
|
#include <gmime/gmime.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
using namespace Mu;
|
using namespace Mu;
|
||||||
using namespace Mu::Message;
|
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
MessageContact::display_name() const
|
Contact::display_name() const
|
||||||
{
|
{
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
return email;
|
return email;
|
||||||
|
@ -35,11 +34,11 @@ MessageContact::display_name() const
|
||||||
return name + " <" + email + '>';
|
return name + " <" + email + '>';
|
||||||
}
|
}
|
||||||
|
|
||||||
Mu::MessageContacts
|
Mu::Contacts
|
||||||
Mu::make_message_contacts(InternetAddressList* addr_lst,
|
Mu::make_contacts(InternetAddressList* addr_lst,
|
||||||
Field::Id field_id, ::time_t message_date)
|
Field::Id field_id, ::time_t message_date)
|
||||||
{
|
{
|
||||||
MessageContacts contacts;
|
Contacts contacts;
|
||||||
size_t num{};
|
size_t num{};
|
||||||
|
|
||||||
g_return_val_if_fail(addr_lst, contacts);
|
g_return_val_if_fail(addr_lst, contacts);
|
||||||
|
@ -59,7 +58,7 @@ Mu::make_message_contacts(InternetAddressList* addr_lst,
|
||||||
if (G_UNLIKELY(!email))
|
if (G_UNLIKELY(!email))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
contacts.push_back(MessageContact{email, name ? name : "",
|
contacts.push_back(Contact{email, name ? name : "",
|
||||||
field_id, message_date});
|
field_id, message_date});
|
||||||
++num;
|
++num;
|
||||||
}
|
}
|
||||||
|
@ -68,8 +67,8 @@ Mu::make_message_contacts(InternetAddressList* addr_lst,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Mu::MessageContacts
|
Mu::Contacts
|
||||||
Mu::make_message_contacts(const std::string& addrs,
|
Mu::make_contacts(const std::string& addrs,
|
||||||
Field::Id field_id,
|
Field::Id field_id,
|
||||||
::time_t message_date)
|
::time_t message_date)
|
||||||
{
|
{
|
||||||
|
@ -79,7 +78,7 @@ Mu::make_message_contacts(const std::string& addrs,
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto contacts{make_message_contacts(addr_list, field_id, message_date)};
|
auto contacts{make_contacts(addr_list, field_id, message_date)};
|
||||||
g_object_unref(addr_list);
|
g_object_unref(addr_list);
|
||||||
|
|
||||||
return contacts;
|
return contacts;
|
||||||
|
@ -107,7 +106,7 @@ Mu::lowercase_hash(const std::string& s)
|
||||||
static void
|
static void
|
||||||
test_ctor_foo()
|
test_ctor_foo()
|
||||||
{
|
{
|
||||||
MessageContact c{
|
Contact c{
|
||||||
"foo@example.com",
|
"foo@example.com",
|
||||||
"Foo Bar",
|
"Foo Bar",
|
||||||
Field::Id::Bcc,
|
Field::Id::Bcc,
|
||||||
|
@ -126,7 +125,7 @@ test_ctor_foo()
|
||||||
static void
|
static void
|
||||||
test_ctor_blinky()
|
test_ctor_blinky()
|
||||||
{
|
{
|
||||||
MessageContact c{
|
Contact c{
|
||||||
"bar@example.com",
|
"bar@example.com",
|
||||||
"Blinky",
|
"Blinky",
|
||||||
1645215014,
|
1645215014,
|
||||||
|
@ -148,7 +147,7 @@ test_ctor_blinky()
|
||||||
static void
|
static void
|
||||||
test_ctor_cleanup()
|
test_ctor_cleanup()
|
||||||
{
|
{
|
||||||
MessageContact c{
|
Contact c{
|
||||||
"bar@example.com",
|
"bar@example.com",
|
||||||
"Bli\nky",
|
"Bli\nky",
|
||||||
1645215014,
|
1645215014,
|
||||||
|
@ -179,12 +178,12 @@ test_make_contacts()
|
||||||
internet_address_list_parse(NULL, str)};
|
internet_address_list_parse(NULL, str)};
|
||||||
|
|
||||||
g_assert_true(lst);
|
g_assert_true(lst);
|
||||||
const auto addrs{make_message_contacts(lst, Field::Id::Cc, 54321 )};
|
const auto addrs{make_contacts(lst, Field::Id::Cc, 54321 )};
|
||||||
g_object_unref(lst);
|
g_object_unref(lst);
|
||||||
|
|
||||||
g_assert_cmpuint(addrs.size(),==,3);
|
g_assert_cmpuint(addrs.size(),==,3);
|
||||||
|
|
||||||
const auto addrs2{make_message_contacts(str, Field::Id::To, 12345 )};
|
const auto addrs2{make_contacts(str, Field::Id::To, 12345 )};
|
||||||
g_assert_cmpuint(addrs2.size(),==,3);
|
g_assert_cmpuint(addrs2.size(),==,3);
|
||||||
|
|
||||||
assert_equal(addrs2[0].name, "Abc");
|
assert_equal(addrs2[0].name, "Abc");
|
||||||
|
@ -202,7 +201,7 @@ test_make_contacts_2()
|
||||||
"De\nf <baa@example.com>, "
|
"De\nf <baa@example.com>, "
|
||||||
"\tGhi <zzz@example.com>";
|
"\tGhi <zzz@example.com>";
|
||||||
|
|
||||||
const auto addrs2{make_message_contacts(str, Field::Id::Bcc, 12345 )};
|
const auto addrs2{make_contacts(str, Field::Id::Bcc, 12345 )};
|
||||||
g_assert_cmpuint(addrs2.size(),==,3);
|
g_assert_cmpuint(addrs2.size(),==,3);
|
||||||
|
|
||||||
assert_equal(addrs2[0].name, "Äbc");
|
assert_equal(addrs2[0].name, "Äbc");
|
||||||
|
@ -222,7 +221,7 @@ test_make_contacts_err()
|
||||||
InternetAddressList *lst{ internet_address_list_parse(NULL, "")};
|
InternetAddressList *lst{ internet_address_list_parse(NULL, "")};
|
||||||
g_assert_false(lst);
|
g_assert_false(lst);
|
||||||
|
|
||||||
const auto addrs{make_message_contacts("", Field::Id::To, 77777)};
|
const auto addrs{make_contacts("", Field::Id::To, 77777)};
|
||||||
g_assert_true(addrs.empty());
|
g_assert_true(addrs.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,12 +231,12 @@ main(int argc, char* argv[])
|
||||||
g_test_init(&argc, &argv, NULL);
|
g_test_init(&argc, &argv, NULL);
|
||||||
g_mime_init();
|
g_mime_init();
|
||||||
|
|
||||||
g_test_add_func("/message-contacts/ctor-foo", test_ctor_foo);
|
g_test_add_func("/message/contact/ctor-foo", test_ctor_foo);
|
||||||
g_test_add_func("/message-contacts/ctor-blinky", test_ctor_blinky);
|
g_test_add_func("/message/contact/ctor-blinky", test_ctor_blinky);
|
||||||
g_test_add_func("/message-contacts/ctor-cleanup", test_ctor_cleanup);
|
g_test_add_func("/message/contact/ctor-cleanup", test_ctor_cleanup);
|
||||||
g_test_add_func("/message-contacts/make-contacts", test_make_contacts);
|
g_test_add_func("/message/contact/make-contacts", test_make_contacts);
|
||||||
g_test_add_func("/message-contacts/make-contacts-2", test_make_contacts_2);
|
g_test_add_func("/message/contact/make-contacts-2", test_make_contacts_2);
|
||||||
g_test_add_func("/message-contacts/make-contacts-err", test_make_contacts_err);
|
g_test_add_func("/message/contact/make-contacts-err", test_make_contacts_err);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
|
@ -29,7 +29,7 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
#include "mu-message-fields.hh"
|
#include "mu-fields.hh"
|
||||||
|
|
||||||
struct _InternetAddressList;
|
struct _InternetAddressList;
|
||||||
|
|
||||||
|
@ -44,24 +44,24 @@ namespace Mu {
|
||||||
*/
|
*/
|
||||||
size_t lowercase_hash(const std::string& s);
|
size_t lowercase_hash(const std::string& s);
|
||||||
|
|
||||||
struct MessageContact {
|
struct Contact {
|
||||||
/**
|
/**
|
||||||
* Construct a new MessageContact
|
* Construct a new Contact
|
||||||
*
|
*
|
||||||
* @param email_ email address
|
* @param email_ email address
|
||||||
* @param name_ name or empty
|
* @param name_ name or empty
|
||||||
* @param field_id_ contact field id, or {}
|
* @param field_id_ contact field id, or {}
|
||||||
* @param message_date_ data for the message for this contact
|
* @param message_date_ data for the message for this contact
|
||||||
*/
|
*/
|
||||||
MessageContact(const std::string& email_, const std::string& name_ = "",
|
Contact(const std::string& email_, const std::string& name_ = "",
|
||||||
std::optional<MessageField::Id> field_id_ = {},
|
std::optional<Field::Id> field_id_ = {},
|
||||||
time_t message_date_ = 0)
|
time_t message_date_ = 0)
|
||||||
: email{email_}, name{name_}, field_id{field_id_},
|
: email{email_}, name{name_}, field_id{field_id_},
|
||||||
message_date{message_date_}, personal{}, frequency{1}, tstamp{}
|
message_date{message_date_}, personal{}, frequency{1}, tstamp{}
|
||||||
{ cleanup_name(); }
|
{ cleanup_name(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new MessageContact
|
* Construct a new Contact
|
||||||
*
|
*
|
||||||
* @param email_ email address
|
* @param email_ email address
|
||||||
* @param name_ name or empty
|
* @param name_ name or empty
|
||||||
|
@ -70,7 +70,7 @@ struct MessageContact {
|
||||||
* @param freq_ how often was this contact seen?
|
* @param freq_ how often was this contact seen?
|
||||||
* @param tstamp_ timestamp for last change
|
* @param tstamp_ timestamp for last change
|
||||||
*/
|
*/
|
||||||
MessageContact(const std::string& email_, const std::string& name_,
|
Contact(const std::string& email_, const std::string& name_,
|
||||||
time_t message_date_, bool personal_, size_t freq_,
|
time_t message_date_, bool personal_, size_t freq_,
|
||||||
int64_t tstamp_)
|
int64_t tstamp_)
|
||||||
: email{email_}, name{name_}, field_id{},
|
: email{email_}, name{name_}, field_id{},
|
||||||
|
@ -91,11 +91,11 @@ struct MessageContact {
|
||||||
/**
|
/**
|
||||||
* Operator==; based on the hash values (ie. lowercase e-mail address)
|
* Operator==; based on the hash values (ie. lowercase e-mail address)
|
||||||
*
|
*
|
||||||
* @param rhs some other MessageContact
|
* @param rhs some other Contact
|
||||||
*
|
*
|
||||||
* @return true orf false.
|
* @return true orf false.
|
||||||
*/
|
*/
|
||||||
bool operator== (const MessageContact& rhs) const noexcept {
|
bool operator== (const Contact& rhs) const noexcept {
|
||||||
return hash() == rhs.hash();
|
return hash() == rhs.hash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ struct MessageContact {
|
||||||
|
|
||||||
std::string email; /**< Email address for this contact.Not empty */
|
std::string email; /**< Email address for this contact.Not empty */
|
||||||
std::string name; /**< Name for this contact; can be empty. */
|
std::string name; /**< Name for this contact; can be empty. */
|
||||||
std::optional<MessageField::Id> field_id; /**< Field Id of contact or nullopt */
|
std::optional<Field::Id> field_id; /**< Field Id of contact or nullopt */
|
||||||
::time_t message_date; /**< date of the message from which the
|
::time_t message_date; /**< date of the message from which the
|
||||||
* contact originates */
|
* contact originates */
|
||||||
bool personal; /**< A personal message? */
|
bool personal; /**< A personal message? */
|
||||||
|
@ -135,43 +135,43 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using MessageContacts = std::vector<MessageContact>;
|
using Contacts = std::vector<Contact>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a sequence of MessageContact objects from an InternetAddressList
|
* Create a sequence of Contact objects from an InternetAddressList
|
||||||
*
|
*
|
||||||
* @param addr_lst an address list
|
* @param addr_lst an address list
|
||||||
* @param field_id the field_id for message field for these addresses
|
* @param field_id the field_id for message field for these addresses
|
||||||
* @param message_date the date of the message from which the InternetAddressList
|
* @param message_date the date of the message from which the InternetAddressList
|
||||||
* originates.
|
* originates.
|
||||||
*
|
*
|
||||||
* @return a sequence of MessageContact objects.
|
* @return a sequence of Contact objects.
|
||||||
*/
|
*/
|
||||||
MessageContacts
|
Contacts
|
||||||
make_message_contacts(/*const*/ struct _InternetAddressList* addr_lst,
|
make_contacts(/*const*/ struct _InternetAddressList* addr_lst,
|
||||||
MessageField::Id field_id, ::time_t message_date);
|
Field::Id field_id, ::time_t message_date);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a sequence of MessageContact objects from an InternetAddressList
|
* Create a sequence of Contact objects from an InternetAddressList
|
||||||
*
|
*
|
||||||
* @param addrs a string with one more valid addresses (as per internet_address_list_parse())
|
* @param addrs a string with one more valid addresses (as per internet_address_list_parse())
|
||||||
* @param field_id the field_id for message field for these addresses
|
* @param field_id the field_id for message field for these addresses
|
||||||
* @param message_date the date of the message from which the addresses originate
|
* @param message_date the date of the message from which the addresses originate
|
||||||
*
|
*
|
||||||
* @return a sequence of MessageContact objects.
|
* @return a sequence of Contact objects.
|
||||||
*/
|
*/
|
||||||
MessageContacts
|
Contacts
|
||||||
make_message_contacts(const std::string& addrs,
|
make_contacts(const std::string& addrs,
|
||||||
MessageField::Id field_id, ::time_t message_date);
|
Field::Id field_id, ::time_t message_date);
|
||||||
} // namespace Mu
|
} // namespace Mu
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement our hash int std::
|
* Implement our hash int std::
|
||||||
*/
|
*/
|
||||||
template<> struct std::hash<Mu::MessageContact> {
|
template<> struct std::hash<Mu::Contact> {
|
||||||
std::size_t operator()(const Mu::MessageContact& c) const noexcept {
|
std::size_t operator()(const Mu::Contact& c) const noexcept {
|
||||||
return c.hash();
|
return c.hash();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* MU_MESSAGE_CONTACT_HH__ */
|
#endif /* MU_CONTACT_HH__ */
|
|
@ -17,7 +17,7 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mu-message-document.hh"
|
#include "mu-document.hh"
|
||||||
#include "mu-message.hh"
|
#include "mu-message.hh"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
@ -31,12 +31,10 @@
|
||||||
|
|
||||||
|
|
||||||
using namespace Mu;
|
using namespace Mu;
|
||||||
using namespace Mu::Message;
|
|
||||||
|
|
||||||
constexpr char SepaChar1 = 0xfe;
|
constexpr char SepaChar1 = 0xfe;
|
||||||
constexpr char SepaChar2 = 0xff;
|
constexpr char SepaChar2 = 0xff;
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_index_term(Xapian::Document& doc, const Field& field, const std::string& val)
|
add_index_term(Xapian::Document& doc, const Field& field, const std::string& val)
|
||||||
{
|
{
|
||||||
|
@ -58,39 +56,39 @@ maybe_add_term(Xapian::Document& doc, const Field& field, const std::string& val
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MessageDocument::add(Field::Id id, const std::string& val)
|
Document::add(Field::Id id, const std::string& val)
|
||||||
{
|
{
|
||||||
const auto field{message_field(id)};
|
const auto field{field_from_id(id)};
|
||||||
|
|
||||||
if (field.is_value())
|
if (field.is_value())
|
||||||
doc_.add_value(field.value_no(), val);
|
xdoc_.add_value(field.value_no(), val);
|
||||||
|
|
||||||
maybe_add_term(doc_, field, val);
|
maybe_add_term(xdoc_, field, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MessageDocument::add(Field::Id id, const std::vector<std::string>& vals)
|
Document::add(Field::Id id, const std::vector<std::string>& vals)
|
||||||
{
|
{
|
||||||
const auto field{message_field(id)};
|
const auto field{field_from_id(id)};
|
||||||
|
|
||||||
if (field.is_value())
|
if (field.is_value())
|
||||||
doc_.add_value(field.value_no(), Mu::join(vals, SepaChar1));
|
xdoc_.add_value(field.value_no(), Mu::join(vals, SepaChar1));
|
||||||
|
|
||||||
std::for_each(vals.begin(), vals.end(),
|
std::for_each(vals.begin(), vals.end(),
|
||||||
[&](const auto& val) { maybe_add_term(doc_, field, val); });
|
[&](const auto& val) { maybe_add_term(xdoc_, field, val); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::string>
|
std::vector<std::string>
|
||||||
MessageDocument::string_vec_value(MessageField::Id field_id) const noexcept
|
Document::string_vec_value(Field::Id field_id) const noexcept
|
||||||
{
|
{
|
||||||
return Mu::split(string_value(field_id), SepaChar1);
|
return Mu::split(string_value(field_id), SepaChar1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MessageDocument::add(Field::Id id, const Contacts& contacts)
|
Document::add(Field::Id id, const Contacts& contacts)
|
||||||
{
|
{
|
||||||
const auto field{message_field(id)};
|
const auto field{field_from_id(id)};
|
||||||
std::vector<std::string> cvec;
|
std::vector<std::string> cvec;
|
||||||
|
|
||||||
const std::string sepa2(1, SepaChar2);
|
const std::string sepa2(1, SepaChar2);
|
||||||
|
@ -99,19 +97,19 @@ MessageDocument::add(Field::Id id, const Contacts& contacts)
|
||||||
if (!contact.field_id || *contact.field_id != id)
|
if (!contact.field_id || *contact.field_id != id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
doc_.add_term(contact.email);
|
xdoc_.add_term(contact.email);
|
||||||
if (!contact.name.empty())
|
if (!contact.name.empty())
|
||||||
add_index_term(doc_, field, contact.name);
|
add_index_term(xdoc_, field, contact.name);
|
||||||
|
|
||||||
cvec.emplace_back(contact.email + sepa2 + contact.name);
|
cvec.emplace_back(contact.email + sepa2 + contact.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cvec.empty())
|
if (!cvec.empty())
|
||||||
doc_.add_value(field.value_no(), join(cvec, SepaChar1));
|
xdoc_.add_value(field.value_no(), join(cvec, SepaChar1));
|
||||||
}
|
}
|
||||||
|
|
||||||
Contacts
|
Contacts
|
||||||
MessageDocument::contacts_value(Field::Id id) const noexcept
|
Document::contacts_value(Field::Id id) const noexcept
|
||||||
{
|
{
|
||||||
const auto vals{string_vec_value(id)};
|
const auto vals{string_vec_value(id)};
|
||||||
Contacts contacts;
|
Contacts contacts;
|
||||||
|
@ -152,7 +150,7 @@ string_to_integer(const std::string& str)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MessageDocument::add(Field::Id id, int64_t val)
|
Document::add(Field::Id id, int64_t val)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Xapian stores everything (incl. numbers) as strings.
|
* Xapian stores everything (incl. numbers) as strings.
|
||||||
|
@ -161,51 +159,51 @@ MessageDocument::add(Field::Id id, int64_t val)
|
||||||
* length; such that the strings are sorted in the numerical order.
|
* length; such that the strings are sorted in the numerical order.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const auto field{message_field(id)};
|
const auto field{field_from_id(id)};
|
||||||
|
|
||||||
if (field.is_value())
|
if (field.is_value())
|
||||||
doc_.add_value(field.value_no(), integer_to_string(val));
|
xdoc_.add_value(field.value_no(), integer_to_string(val));
|
||||||
|
|
||||||
/* terms are not supported for numerical fields */
|
/* terms are not supported for numerical fields */
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
MessageDocument::integer_value(MessageField::Id field_id) const noexcept
|
Document::integer_value(Field::Id field_id) const noexcept
|
||||||
{
|
{
|
||||||
return string_to_integer(string_value(field_id));
|
return string_to_integer(string_value(field_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MessageDocument::add(Priority prio)
|
Document::add(Priority prio)
|
||||||
{
|
{
|
||||||
constexpr auto field{message_field(Field::Id::Priority)};
|
constexpr auto field{field_from_id(Field::Id::Priority)};
|
||||||
|
|
||||||
doc_.add_value(field.value_no(), std::string(1, to_char(prio)));
|
xdoc_.add_value(field.value_no(), std::string(1, to_char(prio)));
|
||||||
doc_.add_boolean_term(field.xapian_term(to_char(prio)));
|
xdoc_.add_boolean_term(field.xapian_term(to_char(prio)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Priority
|
Priority
|
||||||
MessageDocument::priority_value() const noexcept
|
Document::priority_value() const noexcept
|
||||||
{
|
{
|
||||||
const auto val{string_value(Field::Id::Priority)};
|
const auto val{string_value(Field::Id::Priority)};
|
||||||
return message_priority_from_char(val.empty() ? 'n' : val[0]);
|
return priority_from_char(val.empty() ? 'n' : val[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MessageDocument::add(Flags flags)
|
Document::add(Flags flags)
|
||||||
{
|
{
|
||||||
constexpr auto field{message_field(Field::Id::Flags)};
|
constexpr auto field{field_from_id(Field::Id::Flags)};
|
||||||
|
|
||||||
doc_.add_value(field.value_no(), integer_to_string(static_cast<int64_t>(flags)));
|
xdoc_.add_value(field.value_no(), integer_to_string(static_cast<int64_t>(flags)));
|
||||||
message_flag_infos_for_each([&](auto&& flag_info) {
|
flag_infos_for_each([&](auto&& flag_info) {
|
||||||
if (any_of(flag_info.flag & flags))
|
if (any_of(flag_info.flag & flags))
|
||||||
doc_.add_boolean_term(field.xapian_term(flag_info.shortcut_lower()));
|
xdoc_.add_boolean_term(field.xapian_term(flag_info.shortcut_lower()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Flags
|
Flags
|
||||||
MessageDocument::flags_value() const noexcept
|
Document::flags_value() const noexcept
|
||||||
{
|
{
|
||||||
return static_cast<Flags>(integer_value(Field::Id::Flags));
|
return static_cast<Flags>(integer_value(Field::Id::Flags));
|
||||||
}
|
}
|
||||||
|
@ -227,37 +225,37 @@ MessageDocument::flags_value() const noexcept
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const MessageContacts test_contacts = {{
|
static const Contacts test_contacts = {{
|
||||||
MessageContact{"john@example.com", "John", Field::Id::Bcc},
|
Contact{"john@example.com", "John", Field::Id::Bcc},
|
||||||
MessageContact{"ringo@example.com", "Ringo", Field::Id::Bcc},
|
Contact{"ringo@example.com", "Ringo", Field::Id::Bcc},
|
||||||
MessageContact{"paul@example.com", "Paul", Field::Id::Cc},
|
Contact{"paul@example.com", "Paul", Field::Id::Cc},
|
||||||
MessageContact{"george@example.com", "George", Field::Id::Cc},
|
Contact{"george@example.com", "George", Field::Id::Cc},
|
||||||
MessageContact{"james@example.com", "James", Field::Id::From},
|
Contact{"james@example.com", "James", Field::Id::From},
|
||||||
MessageContact{"lars@example.com", "Lars", Field::Id::To},
|
Contact{"lars@example.com", "Lars", Field::Id::To},
|
||||||
MessageContact{"kirk@example.com", "Kirk", Field::Id::To},
|
Contact{"kirk@example.com", "Kirk", Field::Id::To},
|
||||||
MessageContact{"jason@example.com", "Jason", Field::Id::To}
|
Contact{"jason@example.com", "Jason", Field::Id::To}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_bcc()
|
test_bcc()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
MessageDocument doc;
|
Document doc;
|
||||||
doc.add(Field::Id::Bcc, test_contacts);
|
doc.add(Field::Id::Bcc, test_contacts);
|
||||||
|
|
||||||
MessageContacts expected_contacts = {{
|
Contacts expected_contacts = {{
|
||||||
MessageContact{"john@example.com", "John", Field::Id::Bcc},
|
Contact{"john@example.com", "John", Field::Id::Bcc},
|
||||||
MessageContact{"ringo@example.com", "Ringo", Field::Id::Bcc},
|
Contact{"ringo@example.com", "Ringo", Field::Id::Bcc},
|
||||||
}};
|
}};
|
||||||
const auto actual_contacts = doc.contacts_value(Field::Id::Bcc);
|
const auto actual_contacts = doc.contacts_value(Field::Id::Bcc);
|
||||||
assert_same_contacts(expected_contacts, actual_contacts);
|
assert_same_contacts(expected_contacts, actual_contacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
MessageDocument doc;
|
Document doc;
|
||||||
MessageContacts contacts = {{
|
Contacts contacts = {{
|
||||||
MessageContact{"john@example.com", "John Lennon", Field::Id::Bcc},
|
Contact{"john@example.com", "John Lennon", Field::Id::Bcc},
|
||||||
MessageContact{"ringo@example.com", "Ringo", Field::Id::Bcc},
|
Contact{"ringo@example.com", "Ringo", Field::Id::Bcc},
|
||||||
}};
|
}};
|
||||||
doc.add(Field::Id::Bcc, contacts);
|
doc.add(Field::Id::Bcc, contacts);
|
||||||
auto db = Xapian::InMemory::open();
|
auto db = Xapian::InMemory::open();
|
||||||
|
@ -271,12 +269,12 @@ test_bcc()
|
||||||
static void
|
static void
|
||||||
test_cc()
|
test_cc()
|
||||||
{
|
{
|
||||||
MessageDocument doc;
|
Document doc;
|
||||||
doc.add(Field::Id::Cc, test_contacts);
|
doc.add(Field::Id::Cc, test_contacts);
|
||||||
|
|
||||||
MessageContacts expected_contacts = {{
|
Contacts expected_contacts = {{
|
||||||
MessageContact{"paul@example.com", "Paul", Field::Id::Cc},
|
Contact{"paul@example.com", "Paul", Field::Id::Cc},
|
||||||
MessageContact{"george@example.com", "George", Field::Id::Cc}
|
Contact{"george@example.com", "George", Field::Id::Cc}
|
||||||
}};
|
}};
|
||||||
const auto actual_contacts = doc.contacts_value(Field::Id::Cc);
|
const auto actual_contacts = doc.contacts_value(Field::Id::Cc);
|
||||||
|
|
||||||
|
@ -287,11 +285,11 @@ test_cc()
|
||||||
static void
|
static void
|
||||||
test_from()
|
test_from()
|
||||||
{
|
{
|
||||||
MessageDocument doc;
|
Document doc;
|
||||||
doc.add(Field::Id::From, test_contacts);
|
doc.add(Field::Id::From, test_contacts);
|
||||||
|
|
||||||
MessageContacts expected_contacts = {{
|
Contacts expected_contacts = {{
|
||||||
MessageContact{"james@example.com", "James", Field::Id::From},
|
Contact{"james@example.com", "James", Field::Id::From},
|
||||||
}};
|
}};
|
||||||
const auto actual_contacts = doc.contacts_value(Field::Id::From);
|
const auto actual_contacts = doc.contacts_value(Field::Id::From);
|
||||||
|
|
||||||
|
@ -301,13 +299,13 @@ test_from()
|
||||||
static void
|
static void
|
||||||
test_to()
|
test_to()
|
||||||
{
|
{
|
||||||
MessageDocument doc;
|
Document doc;
|
||||||
doc.add(Field::Id::To, test_contacts);
|
doc.add(Field::Id::To, test_contacts);
|
||||||
|
|
||||||
MessageContacts expected_contacts = {{
|
Contacts expected_contacts = {{
|
||||||
MessageContact{"lars@example.com", "Lars", Field::Id::To},
|
Contact{"lars@example.com", "Lars", Field::Id::To},
|
||||||
MessageContact{"kirk@example.com", "Kirk", Field::Id::To},
|
Contact{"kirk@example.com", "Kirk", Field::Id::To},
|
||||||
MessageContact{"jason@example.com", "Jason", Field::Id::To}
|
Contact{"jason@example.com", "Jason", Field::Id::To}
|
||||||
}};
|
}};
|
||||||
const auto actual_contacts = doc.contacts_value(Field::Id::To);
|
const auto actual_contacts = doc.contacts_value(Field::Id::To);
|
||||||
|
|
||||||
|
@ -319,13 +317,13 @@ static void
|
||||||
test_size()
|
test_size()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
MessageDocument doc;
|
Document doc;
|
||||||
doc.add(Field::Id::Size, 12345);
|
doc.add(Field::Id::Size, 12345);
|
||||||
g_assert_cmpuint(doc.integer_value(Field::Id::Size),==,12345);
|
g_assert_cmpuint(doc.integer_value(Field::Id::Size),==,12345);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
MessageDocument doc;
|
Document doc;
|
||||||
g_assert_cmpuint(doc.integer_value(Field::Id::Size),==,0);
|
g_assert_cmpuint(doc.integer_value(Field::Id::Size),==,0);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
/*
|
/** Copyright (C) 2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||||
** Copyright (C) 2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
||||||
**
|
**
|
||||||
** This program is free software; you can redistribute it and/or modify it
|
** 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
|
** under the terms of the GNU General Public License as published by the
|
||||||
|
@ -17,8 +16,8 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MU_MESSAGE_DOCUMENT_HH__
|
#ifndef MU_DOCUMENT_HH__
|
||||||
#define MU_MESSAGE_DOCUMENT_HH__
|
#define MU_DOCUMENT_HH__
|
||||||
|
|
||||||
#include <xapian.h>
|
#include <xapian.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -26,49 +25,73 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "utils/mu-xapian-utils.hh"
|
#include "utils/mu-xapian-utils.hh"
|
||||||
|
|
||||||
#include "mu-message-fields.hh"
|
#include "mu-fields.hh"
|
||||||
#include "mu-message-priority.hh"
|
#include "mu-priority.hh"
|
||||||
#include "mu-message-flags.hh"
|
#include "mu-flags.hh"
|
||||||
#include "mu-message-contact.hh"
|
#include "mu-contact.hh"
|
||||||
|
|
||||||
namespace Mu {
|
namespace Mu {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A MessageDocument describes the information about a message that is
|
* A Document describes the information about a message that is
|
||||||
* or can be stored in the database.
|
* or can be stored in the database.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class MessageDocument {
|
class Document {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Construct a message for a new Xapian Document
|
* Construct a message for a new Xapian Document
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
MessageDocument() {}
|
Document() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a message document based on on existing Xapian document.
|
* Construct a message document based on on existing Xapian document.
|
||||||
*
|
*
|
||||||
* @param doc
|
* @param doc
|
||||||
*/
|
*/
|
||||||
MessageDocument(const Xapian::Document& doc): doc_{doc} {}
|
Document(const Xapian::Document& doc): xdoc_{doc} {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy CTOR
|
* Copy CTOR
|
||||||
*/
|
*/
|
||||||
MessageDocument(const MessageDocument&) = default;
|
Document(const Document& rhs) { *this = rhs; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move CTOR
|
* Move CTOR
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
MessageDocument(MessageDocument&&) = default;
|
Document(Document&& rhs) {*this = std::move(rhs); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a reference to the underlying Xapian document.
|
* Get a reference to the underlying Xapian document.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const Xapian::Document& xapian_document() const { return doc_; }
|
const Xapian::Document& xapian_document() const { return xdoc_; }
|
||||||
|
|
||||||
|
/* Copy assignment operator
|
||||||
|
*
|
||||||
|
* @param rhs some message
|
||||||
|
*
|
||||||
|
* @return a message ref
|
||||||
|
*/
|
||||||
|
Document& operator=(const Document& rhs) {
|
||||||
|
if (this != &rhs)
|
||||||
|
xdoc_ = rhs.xdoc_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move assignment operator
|
||||||
|
*
|
||||||
|
* @param rhs some message
|
||||||
|
*
|
||||||
|
* @return a message ref
|
||||||
|
*/
|
||||||
|
Document& operator=(Document&& rhs) {
|
||||||
|
if (this != &rhs)
|
||||||
|
xdoc_ = std::move(rhs.xdoc_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -81,7 +104,7 @@ public:
|
||||||
* @param field_id field id
|
* @param field_id field id
|
||||||
* @param val string value
|
* @param val string value
|
||||||
*/
|
*/
|
||||||
void add(MessageField::Id field_id, const std::string& val);
|
void add(Field::Id field_id, const std::string& val);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a string-vec value to the document
|
* Add a string-vec value to the document
|
||||||
|
@ -89,7 +112,7 @@ public:
|
||||||
* @param field_id field id
|
* @param field_id field id
|
||||||
* @param val string-vec value
|
* @param val string-vec value
|
||||||
*/
|
*/
|
||||||
void add(MessageField::Id field_id, const std::vector<std::string>& vals);
|
void add(Field::Id field_id, const std::vector<std::string>& vals);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +121,7 @@ public:
|
||||||
* @param field_id field id
|
* @param field_id field id
|
||||||
* @param contacts message contacts
|
* @param contacts message contacts
|
||||||
*/
|
*/
|
||||||
void add(MessageField::Id id, const MessageContacts& contacts);
|
void add(Field::Id id, const Contacts& contacts);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an integer value to the document
|
* Add an integer value to the document
|
||||||
|
@ -106,14 +129,14 @@ public:
|
||||||
* @param field_id field id
|
* @param field_id field id
|
||||||
* @param val integer value
|
* @param val integer value
|
||||||
*/
|
*/
|
||||||
void add(MessageField::Id field_id, int64_t val);
|
void add(Field::Id field_id, int64_t val);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a message priority to the document
|
* Add a message priority to the document
|
||||||
*
|
*
|
||||||
* @param prio priority
|
* @param prio priority
|
||||||
*/
|
*/
|
||||||
void add(MessagePriority prio);
|
void add(Priority prio);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,7 +144,7 @@ public:
|
||||||
*
|
*
|
||||||
* @param flags mesage flags.
|
* @param flags mesage flags.
|
||||||
*/
|
*/
|
||||||
void add(MessageFlags flags);
|
void add(Flags flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Retrieving values
|
* Retrieving values
|
||||||
|
@ -135,9 +158,9 @@ public:
|
||||||
*
|
*
|
||||||
* @return a string (empty if not found)
|
* @return a string (empty if not found)
|
||||||
*/
|
*/
|
||||||
std::string string_value(MessageField::Id field_id) const noexcept {
|
std::string string_value(Field::Id field_id) const noexcept {
|
||||||
return xapian_try([&]{
|
return xapian_try([&]{
|
||||||
return doc_.get_value(message_field(field_id).value_no());
|
return xdoc_.get_value(field_from_id(field_id).value_no());
|
||||||
}, std::string{});
|
}, std::string{});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -147,7 +170,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return a string list
|
* @return a string list
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> string_vec_value(MessageField::Id field_id) const noexcept;
|
std::vector<std::string> string_vec_value(Field::Id field_id) const noexcept;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,7 +180,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return an integer or 0 if not found.
|
* @return an integer or 0 if not found.
|
||||||
*/
|
*/
|
||||||
int64_t integer_value(MessageField::Id field_id) const noexcept;
|
int64_t integer_value(Field::Id field_id) const noexcept;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -167,15 +190,14 @@ public:
|
||||||
*
|
*
|
||||||
* @return an integer or 0 if not found.
|
* @return an integer or 0 if not found.
|
||||||
*/
|
*/
|
||||||
MessageContacts contacts_value(MessageField::Id id) const noexcept;
|
Contacts contacts_value(Field::Id id) const noexcept;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the priority
|
* Get the priority
|
||||||
*
|
*
|
||||||
* @return the message priority
|
* @return the message priority
|
||||||
*/
|
*/
|
||||||
MessagePriority priority_value() const noexcept;
|
Priority priority_value() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the message flags
|
* Get the message flags
|
||||||
|
@ -183,18 +205,12 @@ public:
|
||||||
*
|
*
|
||||||
* @return flags
|
* @return flags
|
||||||
*/
|
*/
|
||||||
MessageFlags flags_value() const noexcept;
|
Flags flags_value() const noexcept;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Xapian::Document doc_;
|
Xapian::Document xdoc_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namepace Mu
|
} // namepace Mu
|
||||||
|
|
||||||
|
#endif /* MU_DOCUMENT_HH__ */
|
||||||
|
|
||||||
|
|
||||||
#endif /* MU_MESSAGE_DOCUMENT_HH__ */
|
|
|
@ -17,24 +17,24 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mu-message-fields.hh"
|
#include "mu-fields.hh"
|
||||||
#include "mu-message-flags.hh"
|
#include "mu-flags.hh"
|
||||||
|
|
||||||
using namespace Mu;
|
using namespace Mu;
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
MessageField::xapian_term(const std::string& s) const
|
Field::xapian_term(const std::string& s) const
|
||||||
{
|
{
|
||||||
return std::string(1U, xapian_prefix()) + s;
|
return std::string(1U, xapian_prefix()) + s;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
MessageField::xapian_term(std::string_view sv) const
|
Field::xapian_term(std::string_view sv) const
|
||||||
{
|
{
|
||||||
return std::string(1U, xapian_prefix()) + std::string{sv};
|
return std::string(1U, xapian_prefix()) + std::string{sv};
|
||||||
}
|
}
|
||||||
std::string
|
std::string
|
||||||
MessageField::xapian_term(char c) const
|
Field::xapian_term(char c) const
|
||||||
{
|
{
|
||||||
return std::string(1U, xapian_prefix()) + c;
|
return std::string(1U, xapian_prefix()) + c;
|
||||||
}
|
}
|
||||||
|
@ -45,22 +45,22 @@ MessageField::xapian_term(char c) const
|
||||||
* compile-time checks
|
* compile-time checks
|
||||||
*/
|
*/
|
||||||
constexpr bool
|
constexpr bool
|
||||||
validate_message_field_ids()
|
validate_field_ids()
|
||||||
{
|
{
|
||||||
for (auto id = 0U; id != MessageField::id_size(); ++id) {
|
for (auto id = 0U; id != Field::id_size(); ++id) {
|
||||||
const auto field_id = static_cast<MessageField::Id>(id);
|
const auto field_id = static_cast<Field::Id>(id);
|
||||||
if (message_field(field_id).id != field_id)
|
if (field_from_id(field_id).id != field_id)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool
|
constexpr bool
|
||||||
validate_message_field_shortcuts()
|
validate_field_shortcuts()
|
||||||
{
|
{
|
||||||
for (auto id = 0U; id != MessageField::id_size(); ++id) {
|
for (auto id = 0U; id != Field::id_size(); ++id) {
|
||||||
const auto field_id = static_cast<MessageField::Id>(id);
|
const auto field_id = static_cast<Field::Id>(id);
|
||||||
const auto shortcut = message_field(field_id).shortcut;
|
const auto shortcut = field_from_id(field_id).shortcut;
|
||||||
if (shortcut != 0 &&
|
if (shortcut != 0 &&
|
||||||
(shortcut < 'a' || shortcut > 'z'))
|
(shortcut < 'a' || shortcut > 'z'))
|
||||||
return false;
|
return false;
|
||||||
|
@ -70,9 +70,9 @@ validate_message_field_shortcuts()
|
||||||
|
|
||||||
|
|
||||||
constexpr /*static*/ bool
|
constexpr /*static*/ bool
|
||||||
validate_message_field_flags()
|
validate_field_flags()
|
||||||
{
|
{
|
||||||
for (auto&& field: MessageFields) {
|
for (auto&& field: Fields) {
|
||||||
/* - A field has at most one of Indexable, HasTerms, IsXapianBoolean and
|
/* - A field has at most one of Indexable, HasTerms, IsXapianBoolean and
|
||||||
IsContact. */
|
IsContact. */
|
||||||
size_t flagnum{};
|
size_t flagnum{};
|
||||||
|
@ -108,29 +108,29 @@ validate_message_field_flags()
|
||||||
static void
|
static void
|
||||||
test_ids()
|
test_ids()
|
||||||
{
|
{
|
||||||
static_assert(validate_message_field_ids());
|
static_assert(validate_field_ids());
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
static void
|
static void
|
||||||
test_shortcuts()
|
test_shortcuts()
|
||||||
{
|
{
|
||||||
static_assert(validate_message_field_shortcuts());
|
static_assert(validate_field_shortcuts());
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
static void
|
static void
|
||||||
test_prefix()
|
test_prefix()
|
||||||
{
|
{
|
||||||
static_assert(message_field(MessageField::Id::Subject).xapian_prefix() == 'S');
|
static_assert(field_from_id(Field::Id::Subject).xapian_prefix() == 'S');
|
||||||
static_assert(message_field(MessageField::Id::BodyHtml).xapian_prefix() == 0);
|
static_assert(field_from_id(Field::Id::BodyHtml).xapian_prefix() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
static void
|
static void
|
||||||
test_field_flags()
|
test_field_flags()
|
||||||
{
|
{
|
||||||
static_assert(validate_message_field_flags());
|
static_assert(validate_field_flags());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILD_TESTS
|
#ifdef BUILD_TESTS
|
||||||
|
@ -141,11 +141,11 @@ test_xapian_term()
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
assert_equal(message_field(MessageField::Id::Subject).xapian_term(""s), "S");
|
assert_equal(field_from_id(Field::Id::Subject).xapian_term(""s), "S");
|
||||||
assert_equal(message_field(MessageField::Id::Subject).xapian_term("boo"s), "Sboo");
|
assert_equal(field_from_id(Field::Id::Subject).xapian_term("boo"s), "Sboo");
|
||||||
|
|
||||||
assert_equal(message_field(MessageField::Id::From).xapian_term('x'), "Fx");
|
assert_equal(field_from_id(Field::Id::From).xapian_term('x'), "Fx");
|
||||||
assert_equal(message_field(MessageField::Id::To).xapian_term("boo"sv), "Tboo");
|
assert_equal(field_from_id(Field::Id::To).xapian_term("boo"sv), "Tboo");
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
|
@ -17,8 +17,8 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MU_MESSAGE_FIELDS_HH__
|
#ifndef MU_FIELDS_HH__
|
||||||
#define MU_MESSAGE_FIELDS_HH__
|
#define MU_FIELDS_HH__
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
@ -30,11 +30,11 @@
|
||||||
|
|
||||||
namespace Mu {
|
namespace Mu {
|
||||||
|
|
||||||
struct MessageField {
|
struct Field {
|
||||||
/**
|
/**
|
||||||
* Field Ids.
|
* Field Ids.
|
||||||
*
|
*
|
||||||
* Note, the Ids are also used as indices in the MessageFields array,
|
* Note, the Ids are also used as indices in the Fields array,
|
||||||
* so their numerical values must be 0...Count.
|
* so their numerical values must be 0...Count.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -55,6 +55,7 @@ struct MessageField {
|
||||||
Path, /**< File-system Path */
|
Path, /**< File-system Path */
|
||||||
Subject, /**< Message subject */
|
Subject, /**< Message subject */
|
||||||
To, /**< To: recipient */
|
To, /**< To: recipient */
|
||||||
|
Uid, /**< Unique id for message (based on path) */
|
||||||
/*
|
/*
|
||||||
* string list items...
|
* string list items...
|
||||||
*/
|
*/
|
||||||
|
@ -75,7 +76,7 @@ struct MessageField {
|
||||||
/*
|
/*
|
||||||
* <private>
|
* <private>
|
||||||
*/
|
*/
|
||||||
_count_ /**< Number of MessageFieldIds */
|
_count_ /**< Number of FieldIds */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,6 +166,10 @@ struct MessageField {
|
||||||
constexpr bool is_boolean_term() const { return any_of(Flag::BooleanTerm); }
|
constexpr bool is_boolean_term() const { return any_of(Flag::BooleanTerm); }
|
||||||
constexpr bool is_normal_term() const { return any_of(Flag::NormalTerm); }
|
constexpr bool is_normal_term() const { return any_of(Flag::NormalTerm); }
|
||||||
|
|
||||||
|
constexpr bool is_searchable() const { return is_indexable_term() ||
|
||||||
|
is_boolean_term() ||
|
||||||
|
is_normal_term(); }
|
||||||
|
|
||||||
constexpr bool is_value() const { return any_of(Flag::Value); }
|
constexpr bool is_value() const { return any_of(Flag::Value); }
|
||||||
|
|
||||||
constexpr bool is_contact() const { return any_of(Flag::Contact); }
|
constexpr bool is_contact() const { return any_of(Flag::Contact); }
|
||||||
|
@ -172,6 +177,7 @@ struct MessageField {
|
||||||
constexpr bool do_not_cache() const { return any_of(Flag::DoNotCache); }
|
constexpr bool do_not_cache() const { return any_of(Flag::DoNotCache); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field members
|
* Field members
|
||||||
*
|
*
|
||||||
|
@ -199,254 +205,264 @@ struct MessageField {
|
||||||
std::string xapian_term(char c) const;
|
std::string xapian_term(char c) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
MU_ENABLE_BITOPS(MessageField::Flag);
|
MU_ENABLE_BITOPS(Field::Flag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sequence of _all_ message fields
|
* Sequence of _all_ message fields
|
||||||
*/
|
*/
|
||||||
static constexpr std::array<MessageField, MessageField::id_size()>
|
static constexpr std::array<Field, Field::id_size()>
|
||||||
MessageFields = {
|
Fields = {
|
||||||
{
|
{
|
||||||
// Bcc
|
// Bcc
|
||||||
{
|
{
|
||||||
MessageField::Id::Bcc,
|
Field::Id::Bcc,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"bcc",
|
"bcc",
|
||||||
"Blind carbon-copy recipient",
|
"Blind carbon-copy recipient",
|
||||||
"bcc:foo@example.com",
|
"bcc:foo@example.com",
|
||||||
'h',
|
'h',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::Contact |
|
Field::Flag::Contact |
|
||||||
MessageField::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
// HTML Body
|
// HTML Body
|
||||||
{
|
{
|
||||||
MessageField::Id::BodyHtml,
|
Field::Id::BodyHtml,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"body",
|
"body",
|
||||||
"Message html body",
|
"Message html body",
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::DoNotCache
|
Field::Flag::DoNotCache
|
||||||
},
|
},
|
||||||
// Body
|
// Body
|
||||||
{
|
{
|
||||||
MessageField::Id::BodyText,
|
Field::Id::BodyText,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"body",
|
"body",
|
||||||
"Message plain-text body",
|
"Message plain-text body",
|
||||||
"body:capybara", // example
|
"body:capybara", // example
|
||||||
'b',
|
'b',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::IndexableTerm |
|
Field::Flag::IndexableTerm |
|
||||||
MessageField::Flag::DoNotCache
|
Field::Flag::DoNotCache
|
||||||
},
|
},
|
||||||
// Cc
|
// Cc
|
||||||
{
|
{
|
||||||
MessageField::Id::Cc,
|
Field::Id::Cc,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"cc",
|
"cc",
|
||||||
"Carbon-copy recipient",
|
"Carbon-copy recipient",
|
||||||
"cc:quinn@example.com",
|
"cc:quinn@example.com",
|
||||||
'c',
|
'c',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::Contact |
|
Field::Flag::Contact |
|
||||||
MessageField::Flag::Value},
|
Field::Flag::Value},
|
||||||
|
|
||||||
// Embed
|
// Embed
|
||||||
{
|
{
|
||||||
MessageField::Id::EmbeddedText,
|
Field::Id::EmbeddedText,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"embed",
|
"embed",
|
||||||
"Embedded text",
|
"Embedded text",
|
||||||
"embed:war OR embed:peace",
|
"embed:war OR embed:peace",
|
||||||
'e',
|
'e',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::IndexableTerm |
|
Field::Flag::IndexableTerm |
|
||||||
MessageField::Flag::DoNotCache},
|
Field::Flag::DoNotCache},
|
||||||
// File
|
// File
|
||||||
{
|
{
|
||||||
MessageField::Id::File,
|
Field::Id::File,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"file",
|
"file",
|
||||||
"Attachment file name",
|
"Attachment file name",
|
||||||
"file:/image\\.*.jpg/",
|
"file:/image\\.*.jpg/",
|
||||||
'j',
|
'j',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::NormalTerm |
|
Field::Flag::NormalTerm |
|
||||||
MessageField::Flag::DoNotCache},
|
Field::Flag::DoNotCache},
|
||||||
|
|
||||||
// From
|
// From
|
||||||
{
|
{
|
||||||
MessageField::Id::From,
|
Field::Id::From,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"from",
|
"from",
|
||||||
"Message sender",
|
"Message sender",
|
||||||
"from:jimbo",
|
"from:jimbo",
|
||||||
'f',
|
'f',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::Contact |
|
Field::Flag::Contact |
|
||||||
MessageField::Flag::Value},
|
Field::Flag::Value},
|
||||||
// Maildir
|
// Maildir
|
||||||
{
|
{
|
||||||
MessageField::Id::Maildir,
|
Field::Id::Maildir,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"maildir",
|
"maildir",
|
||||||
"Maildir path for message",
|
"Maildir path for message",
|
||||||
"maildir:/private/archive",
|
"maildir:/private/archive",
|
||||||
'm',
|
'm',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::NormalTerm |
|
Field::Flag::NormalTerm |
|
||||||
MessageField::Flag::Value},
|
Field::Flag::Value},
|
||||||
// MIME
|
// MIME
|
||||||
{
|
{
|
||||||
MessageField::Id::Mime,
|
Field::Id::Mime,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"mime",
|
"mime",
|
||||||
"Attachment MIME-type",
|
"Attachment MIME-type",
|
||||||
"mime:image/jpeg",
|
"mime:image/jpeg",
|
||||||
'y',
|
'y',
|
||||||
MessageField::Flag::NormalTerm},
|
Field::Flag::NormalTerm},
|
||||||
// Message-ID
|
// Message-ID
|
||||||
{
|
{
|
||||||
MessageField::Id::MessageId,
|
Field::Id::MessageId,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"msgid",
|
"msgid",
|
||||||
"Attachment MIME-type",
|
"Attachment MIME-type",
|
||||||
"mime:image/jpeg",
|
"mime:image/jpeg",
|
||||||
'i',
|
'i',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::NormalTerm |
|
Field::Flag::NormalTerm |
|
||||||
MessageField::Flag::Value},
|
Field::Flag::Value},
|
||||||
// Path
|
// Path
|
||||||
{
|
{
|
||||||
MessageField::Id::Path,
|
Field::Id::Path,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"path",
|
"path",
|
||||||
"File system path to message",
|
"File system path to message",
|
||||||
{},
|
{},
|
||||||
'p',
|
'p',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::BooleanTerm |
|
Field::Flag::BooleanTerm |
|
||||||
MessageField::Flag::Value},
|
Field::Flag::Value},
|
||||||
|
|
||||||
// Subject
|
// Subject
|
||||||
{
|
{
|
||||||
MessageField::Id::Subject,
|
Field::Id::Subject,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"subject",
|
"subject",
|
||||||
"Message subject",
|
"Message subject",
|
||||||
"subject:wombat",
|
"subject:wombat",
|
||||||
's',
|
's',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::Value |
|
Field::Flag::Value |
|
||||||
MessageField::Flag::IndexableTerm},
|
Field::Flag::IndexableTerm},
|
||||||
|
|
||||||
// To
|
// To
|
||||||
{
|
{
|
||||||
MessageField::Id::To,
|
Field::Id::To,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"to",
|
"to",
|
||||||
"Message recipient",
|
"Message recipient",
|
||||||
"to:flimflam@example.com",
|
"to:flimflam@example.com",
|
||||||
't',
|
't',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::Contact |
|
Field::Flag::Contact |
|
||||||
MessageField::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
|
// UID (internal)
|
||||||
|
{
|
||||||
|
Field::Id::Uid,
|
||||||
|
Field::Type::String,
|
||||||
|
"uid",
|
||||||
|
"Message recipient",
|
||||||
|
{},
|
||||||
|
'u',
|
||||||
|
Field::Flag::NormalTerm},
|
||||||
|
|
||||||
// References
|
// References
|
||||||
{
|
{
|
||||||
MessageField::Id::References,
|
Field::Id::References,
|
||||||
MessageField::Type::StringList,
|
Field::Type::StringList,
|
||||||
"refs",
|
"refs",
|
||||||
"Message references to other messages",
|
"Message references to other messages",
|
||||||
{},
|
{},
|
||||||
'r',
|
'r',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
// Tags
|
// Tags
|
||||||
{
|
{
|
||||||
MessageField::Id::Tags,
|
Field::Id::Tags,
|
||||||
MessageField::Type::StringList,
|
Field::Type::StringList,
|
||||||
"tag",
|
"tag",
|
||||||
"Message tags",
|
"Message tags",
|
||||||
"tag:projectx",
|
"tag:projectx",
|
||||||
'x',
|
'x',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::NormalTerm |
|
Field::Flag::NormalTerm |
|
||||||
MessageField::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
// Date
|
// Date
|
||||||
{
|
{
|
||||||
MessageField::Id::Date,
|
Field::Id::Date,
|
||||||
MessageField::Type::TimeT,
|
Field::Type::TimeT,
|
||||||
"date",
|
"date",
|
||||||
"Message date",
|
"Message date",
|
||||||
"date:20220101..20220505",
|
"date:20220101..20220505",
|
||||||
'd',
|
'd',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::Value |
|
Field::Flag::Value |
|
||||||
MessageField::Flag::Range
|
Field::Flag::Range
|
||||||
},
|
},
|
||||||
// Flags
|
// Flags
|
||||||
{
|
{
|
||||||
MessageField::Id::Flags,
|
Field::Id::Flags,
|
||||||
MessageField::Type::Integer,
|
Field::Type::Integer,
|
||||||
"flag",
|
"flag",
|
||||||
"Message properties",
|
"Message properties",
|
||||||
"flag:unread",
|
"flag:unread",
|
||||||
'g',
|
'g',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::NormalTerm |
|
Field::Flag::NormalTerm |
|
||||||
MessageField::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
// Priority
|
// Priority
|
||||||
{
|
{
|
||||||
MessageField::Id::Priority,
|
Field::Id::Priority,
|
||||||
MessageField::Type::Integer,
|
Field::Type::Integer,
|
||||||
"prio",
|
"prio",
|
||||||
"Priority",
|
"Priority",
|
||||||
"prio:high",
|
"prio:high",
|
||||||
'p',
|
'p',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::NormalTerm |
|
Field::Flag::NormalTerm |
|
||||||
MessageField::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
// Size
|
// Size
|
||||||
{
|
{
|
||||||
MessageField::Id::Size,
|
Field::Id::Size,
|
||||||
MessageField::Type::ByteSize,
|
Field::Type::ByteSize,
|
||||||
"size",
|
"size",
|
||||||
"Message size in bytes",
|
"Message size in bytes",
|
||||||
"size:1M..5M",
|
"size:1M..5M",
|
||||||
'z',
|
'z',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::Value |
|
Field::Flag::Value |
|
||||||
MessageField::Flag::Range
|
Field::Flag::Range
|
||||||
},
|
},
|
||||||
// Mailing List
|
// Mailing List
|
||||||
{
|
{
|
||||||
MessageField::Id::MailingList,
|
Field::Id::MailingList,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"list",
|
"list",
|
||||||
"Mailing list (List-Id:)",
|
"Mailing list (List-Id:)",
|
||||||
"list:mu-discuss.googlegroups.com",
|
"list:mu-discuss.googlegroups.com",
|
||||||
'v',
|
'v',
|
||||||
MessageField::Flag::GMime |
|
Field::Flag::GMime |
|
||||||
MessageField::Flag::NormalTerm |
|
Field::Flag::NormalTerm |
|
||||||
MessageField::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
// ThreadId
|
// ThreadId
|
||||||
{
|
{
|
||||||
MessageField::Id::ThreadId,
|
Field::Id::ThreadId,
|
||||||
MessageField::Type::String,
|
Field::Type::String,
|
||||||
"thread",
|
"thread",
|
||||||
"Thread a message belongs to",
|
"Thread a message belongs to",
|
||||||
{},
|
{},
|
||||||
'w',
|
'w',
|
||||||
MessageField::Flag::NormalTerm
|
Field::Flag::NormalTerm
|
||||||
},
|
},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
@ -461,10 +477,10 @@ static constexpr std::array<MessageField, MessageField::id_size()>
|
||||||
*
|
*
|
||||||
* @return ref of the message field.
|
* @return ref of the message field.
|
||||||
*/
|
*/
|
||||||
constexpr const MessageField&
|
constexpr const Field&
|
||||||
message_field(MessageField::Id id)
|
field_from_id(Field::Id id)
|
||||||
{
|
{
|
||||||
return MessageFields.at(static_cast<size_t>(id));
|
return Fields.at(static_cast<size_t>(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -473,8 +489,8 @@ message_field(MessageField::Id id)
|
||||||
* @param func some callable
|
* @param func some callable
|
||||||
*/
|
*/
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
void message_field_for_each(Func&& func) {
|
void field_for_each(Func&& func) {
|
||||||
for (const auto& field: MessageFields)
|
for (const auto& field: Fields)
|
||||||
func(field);
|
func(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,10 +502,10 @@ void message_field_for_each(Func&& func) {
|
||||||
* @return a message-field id, or nullopt if not found.
|
* @return a message-field id, or nullopt if not found.
|
||||||
*/
|
*/
|
||||||
template <typename Pred>
|
template <typename Pred>
|
||||||
std::optional<MessageField::Id> message_field_find_if(Pred&& pred) {
|
std::optional<Field> field_find_if(Pred&& pred) {
|
||||||
for (auto&& field: MessageFields)
|
for (auto&& field: Fields)
|
||||||
if (pred(field))
|
if (pred(field))
|
||||||
return field.id;
|
return field;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,37 +517,37 @@ std::optional<MessageField::Id> message_field_find_if(Pred&& pred) {
|
||||||
* @return the message-field-id or nullopt.
|
* @return the message-field-id or nullopt.
|
||||||
*/
|
*/
|
||||||
static inline
|
static inline
|
||||||
std::optional<MessageField::Id> message_field_id(char shortcut) {
|
std::optional<Field> field_from_shortcut(char shortcut) {
|
||||||
return message_field_find_if([&](auto&& field ){
|
return field_find_if([&](auto&& field){
|
||||||
return field.shortcut == shortcut;
|
return field.shortcut == shortcut;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static inline
|
static inline
|
||||||
std::optional<MessageField::Id> message_field_id(const std::string& name) {
|
std::optional<Field> field_from_name(const std::string& name) {
|
||||||
if (name.length() == 1)
|
if (name.length() == 1)
|
||||||
return message_field_id(name[0]);
|
return field_from_shortcut(name[0]);
|
||||||
else
|
else
|
||||||
return message_field_find_if([&](auto&& field){
|
return field_find_if([&](auto&& field){
|
||||||
return field.name == name;
|
return field.name == name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the MessageField::Id for some number, or nullopt if it does not match
|
* Get the Field::Id for some number, or nullopt if it does not match
|
||||||
*
|
*
|
||||||
* @param id an id number
|
* @param id an id number
|
||||||
*
|
*
|
||||||
* @return MessageField::Id or nullopt
|
* @return Field::Id or nullopt
|
||||||
*/
|
*/
|
||||||
static inline
|
static inline
|
||||||
std::optional<MessageField::Id> message_field_id(size_t id)
|
std::optional<Field> field_from_number(size_t id)
|
||||||
{
|
{
|
||||||
if (id >= static_cast<size_t>(MessageField::Id::_count_))
|
if (id >= static_cast<size_t>(Field::Id::_count_))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
else
|
else
|
||||||
return static_cast<MessageField::Id>(id);
|
return field_from_id(static_cast<Field::Id>(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Mu
|
} // namespace Mu
|
||||||
#endif /* MU_MESSAGE_FIELDS_HH__ */
|
#endif /* MU_FIELDS_HH__ */
|
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
** 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-flags.hh"
|
||||||
|
|
||||||
|
using namespace Mu;
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Mu::flags_to_string(Flags 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<Flags>(1 << id);
|
||||||
|
if (flag != AllMessageFlagInfos[id].flag)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tests... also build as runtime-tests, so we can get coverage info
|
||||||
|
*/
|
||||||
|
#ifdef BUILD_TESTS
|
||||||
|
#define static_assert g_assert_true
|
||||||
|
#endif /*BUILD_TESTS*/
|
||||||
|
|
||||||
|
[[maybe_unused]] static void
|
||||||
|
test_basic()
|
||||||
|
{
|
||||||
|
static_assert(AllMessageFlagInfos.size() ==
|
||||||
|
__builtin_ctz(static_cast<unsigned>(Flags::_final_)));
|
||||||
|
static_assert(validate_message_info_flags());
|
||||||
|
|
||||||
|
static_assert(!!flag_info(Flags::Encrypted));
|
||||||
|
static_assert(!flag_info(Flags::None));
|
||||||
|
static_assert(!flag_info(static_cast<Flags>(0)));
|
||||||
|
static_assert(!flag_info(static_cast<Flags>(1<<AllMessageFlagInfos.size())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* flag_info
|
||||||
|
*/
|
||||||
|
[[maybe_unused]] static void
|
||||||
|
test_flag_info()
|
||||||
|
{
|
||||||
|
static_assert(flag_info('D')->flag == Flags::Draft);
|
||||||
|
static_assert(flag_info('l')->flag == Flags::MailingList);
|
||||||
|
static_assert(!flag_info('q'));
|
||||||
|
|
||||||
|
static_assert(flag_info("trashed")->flag == Flags::Trashed);
|
||||||
|
static_assert(flag_info("attach")->flag == Flags::HasAttachment);
|
||||||
|
static_assert(!flag_info("fnorb"));
|
||||||
|
|
||||||
|
|
||||||
|
static_assert(flag_info('D')->shortcut_lower() == 'd');
|
||||||
|
static_assert(flag_info('u')->shortcut_lower() == 'u');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* flags_from_expr
|
||||||
|
*/
|
||||||
|
[[maybe_unused]] static void
|
||||||
|
test_flags_from_expr()
|
||||||
|
{
|
||||||
|
static_assert(flags_from_absolute_expr("SRP").value() ==
|
||||||
|
(Flags::Seen | Flags::Replied | Flags::Passed));
|
||||||
|
static_assert(flags_from_absolute_expr("Faul").value() ==
|
||||||
|
(Flags::Flagged | Flags::Unread |
|
||||||
|
Flags::HasAttachment | Flags::MailingList));
|
||||||
|
|
||||||
|
static_assert(!flags_from_absolute_expr("DRT?"));
|
||||||
|
static_assert(flags_from_absolute_expr("DRT?", true/*ignore invalid*/).value() ==
|
||||||
|
(Flags::Draft | Flags::Replied |
|
||||||
|
Flags::Trashed));
|
||||||
|
static_assert(flags_from_absolute_expr("DFPNxulabcdef", true/*ignore invalid*/).value() ==
|
||||||
|
(Flags::Draft|Flags::Flagged|Flags::Passed|
|
||||||
|
Flags::New | Flags::Encrypted |
|
||||||
|
Flags::Unread | Flags::MailingList |
|
||||||
|
Flags::HasAttachment));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* flags_from_delta_expr
|
||||||
|
*/
|
||||||
|
[[maybe_unused]] static void
|
||||||
|
test_flags_from_delta_expr()
|
||||||
|
{
|
||||||
|
static_assert(flags_from_delta_expr(
|
||||||
|
"+S-u-N", Flags::New|Flags::Unread).value() ==
|
||||||
|
Flags::Seen);
|
||||||
|
static_assert(flags_from_delta_expr("+R+P-F", Flags::Seen).value() ==
|
||||||
|
(Flags::Seen|Flags::Passed|Flags::Replied));
|
||||||
|
/* '-B' is invalid */
|
||||||
|
static_assert(!flags_from_delta_expr("+R+P-B", Flags::Seen));
|
||||||
|
/* '-B' is invalid, but ignore invalid */
|
||||||
|
static_assert(flags_from_delta_expr("+R+P-B", Flags::Seen, true) ==
|
||||||
|
(Flags::Replied|Flags::Passed|Flags::Seen));
|
||||||
|
static_assert(flags_from_delta_expr("+F+T-S", Flags::None, true).value() ==
|
||||||
|
(Flags::Flagged|Flags::Trashed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* flags_filter
|
||||||
|
*/
|
||||||
|
[[maybe_unused]] static void
|
||||||
|
test_flags_filter()
|
||||||
|
{
|
||||||
|
static_assert(flags_filter(flags_from_absolute_expr(
|
||||||
|
"DFPNxulabcdef", true/*ignore invalid*/).value(),
|
||||||
|
MessageFlagCategory::Mailfile) ==
|
||||||
|
(Flags::Draft|Flags::Flagged|Flags::Passed));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef BUILD_TESTS
|
||||||
|
int
|
||||||
|
main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
g_test_init(&argc, &argv, NULL);
|
||||||
|
|
||||||
|
g_test_add_func("/message/flags/basic", test_basic);
|
||||||
|
g_test_add_func("/message/flags/flag-info", test_flag_info);
|
||||||
|
g_test_add_func("/message/flags/flags-from-absolute-expr",
|
||||||
|
test_flags_from_expr);
|
||||||
|
g_test_add_func("/message/flags/flags-from-delta-expr",
|
||||||
|
test_flags_from_delta_expr);
|
||||||
|
g_test_add_func("/message/flags/flags-filter",
|
||||||
|
test_flags_filter);
|
||||||
|
|
||||||
|
return g_test_run();
|
||||||
|
}
|
||||||
|
#endif /*BUILD_TESTS*/
|
|
@ -17,8 +17,8 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MU_MESSAGE_FLAGS_HH__
|
#ifndef MU_FLAGS_HH__
|
||||||
#define MU_MESSAGE_FLAGS_HH__
|
#define MU_FLAGS_HH__
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
namespace Mu {
|
namespace Mu {
|
||||||
|
|
||||||
enum struct MessageFlags {
|
enum struct Flags {
|
||||||
None = 0, /**< No flags */
|
None = 0, /**< No flags */
|
||||||
/**
|
/**
|
||||||
* next 6 are seen in the file-info part of maildir message file
|
* next 6 are seen in the file-info part of maildir message file
|
||||||
|
@ -69,7 +69,7 @@ enum struct MessageFlags {
|
||||||
*/
|
*/
|
||||||
_final_ = 1 << 12
|
_final_ = 1 << 12
|
||||||
};
|
};
|
||||||
MU_ENABLE_BITOPS(MessageFlags);
|
MU_ENABLE_BITOPS(Flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message flags category
|
* Message flags category
|
||||||
|
@ -88,7 +88,7 @@ enum struct MessageFlagCategory {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct MessageFlagInfo {
|
struct MessageFlagInfo {
|
||||||
MessageFlags flag; /**< The message flag */
|
Flags flag; /**< The message flag */
|
||||||
char shortcut; /**< Shortcut character */
|
char shortcut; /**< Shortcut character */
|
||||||
std::string_view name; /**< Name of the flag */
|
std::string_view name; /**< Name of the flag */
|
||||||
MessageFlagCategory category; /**< Flag category */
|
MessageFlagCategory category; /**< Flag category */
|
||||||
|
@ -108,24 +108,24 @@ struct MessageFlagInfo {
|
||||||
* Array of all flag information.
|
* Array of all flag information.
|
||||||
*/
|
*/
|
||||||
constexpr std::array<MessageFlagInfo, 12> AllMessageFlagInfos = {{
|
constexpr std::array<MessageFlagInfo, 12> AllMessageFlagInfos = {{
|
||||||
MessageFlagInfo{MessageFlags::Draft, 'D', "draft", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{Flags::Draft, 'D', "draft", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Flagged, 'F', "flagged", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{Flags::Flagged, 'F', "flagged", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Passed, 'P', "passed", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{Flags::Passed, 'P', "passed", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Replied, 'R', "replied", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{Flags::Replied, 'R', "replied", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Seen, 'S', "seen", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{Flags::Seen, 'S', "seen", MessageFlagCategory::Mailfile},
|
||||||
MessageFlagInfo{MessageFlags::Trashed, 'T', "trashed", MessageFlagCategory::Mailfile},
|
MessageFlagInfo{Flags::Trashed, 'T', "trashed", MessageFlagCategory::Mailfile},
|
||||||
|
|
||||||
MessageFlagInfo{MessageFlags::New, 'N', "new", MessageFlagCategory::Maildir},
|
MessageFlagInfo{Flags::New, 'N', "new", MessageFlagCategory::Maildir},
|
||||||
|
|
||||||
MessageFlagInfo{MessageFlags::Signed, 'z', "signed", MessageFlagCategory::Content},
|
MessageFlagInfo{Flags::Signed, 'z', "signed", MessageFlagCategory::Content},
|
||||||
MessageFlagInfo{MessageFlags::Encrypted, 'x', "encrypted",
|
MessageFlagInfo{Flags::Encrypted, 'x', "encrypted",
|
||||||
MessageFlagCategory::Content},
|
MessageFlagCategory::Content},
|
||||||
MessageFlagInfo{MessageFlags::HasAttachment, 'a', "attach",
|
MessageFlagInfo{Flags::HasAttachment, 'a', "attach",
|
||||||
MessageFlagCategory::Content},
|
MessageFlagCategory::Content},
|
||||||
|
|
||||||
MessageFlagInfo{MessageFlags::Unread, 'u', "unread", MessageFlagCategory::Pseudo},
|
MessageFlagInfo{Flags::Unread, 'u', "unread", MessageFlagCategory::Pseudo},
|
||||||
|
|
||||||
MessageFlagInfo{MessageFlags::MailingList, 'l', "list", MessageFlagCategory::Content},
|
MessageFlagInfo{Flags::MailingList, 'l', "list", MessageFlagCategory::Content},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ constexpr std::array<MessageFlagInfo, 12> AllMessageFlagInfos = {{
|
||||||
* @param func some callable
|
* @param func some callable
|
||||||
*/
|
*/
|
||||||
template<typename Func>
|
template<typename Func>
|
||||||
constexpr void message_flag_infos_for_each(Func&& func)
|
constexpr void flag_infos_for_each(Func&& func)
|
||||||
{
|
{
|
||||||
for (auto&& info: AllMessageFlagInfos)
|
for (auto&& info: AllMessageFlagInfos)
|
||||||
func(info);
|
func(info);
|
||||||
|
@ -149,9 +149,9 @@ constexpr void message_flag_infos_for_each(Func&& func)
|
||||||
* @return the MessageFlagInfo, or std::nullopt in case of error.
|
* @return the MessageFlagInfo, or std::nullopt in case of error.
|
||||||
*/
|
*/
|
||||||
constexpr const std::optional<MessageFlagInfo>
|
constexpr const std::optional<MessageFlagInfo>
|
||||||
message_flag_info(MessageFlags flag)
|
flag_info(Flags flag)
|
||||||
{
|
{
|
||||||
constexpr auto upper = static_cast<unsigned>(MessageFlags::_final_);
|
constexpr auto upper = static_cast<unsigned>(Flags::_final_);
|
||||||
const auto val = static_cast<unsigned>(flag);
|
const auto val = static_cast<unsigned>(flag);
|
||||||
|
|
||||||
if (__builtin_popcount(val) != 1 || val >= upper)
|
if (__builtin_popcount(val) != 1 || val >= upper)
|
||||||
|
@ -168,7 +168,7 @@ message_flag_info(MessageFlags flag)
|
||||||
* @return the MessageFlagInfo
|
* @return the MessageFlagInfo
|
||||||
*/
|
*/
|
||||||
constexpr const std::optional<MessageFlagInfo>
|
constexpr const std::optional<MessageFlagInfo>
|
||||||
message_flag_info(char shortcut)
|
flag_info(char shortcut)
|
||||||
{
|
{
|
||||||
for (auto&& info : AllMessageFlagInfos)
|
for (auto&& info : AllMessageFlagInfos)
|
||||||
if (info.shortcut == shortcut)
|
if (info.shortcut == shortcut)
|
||||||
|
@ -185,7 +185,7 @@ message_flag_info(char shortcut)
|
||||||
* @return the MessageFlagInfo
|
* @return the MessageFlagInfo
|
||||||
*/
|
*/
|
||||||
constexpr const std::optional<MessageFlagInfo>
|
constexpr const std::optional<MessageFlagInfo>
|
||||||
message_flag_info(std::string_view name)
|
flag_info(std::string_view name)
|
||||||
{
|
{
|
||||||
for (auto&& info : AllMessageFlagInfos)
|
for (auto&& info : AllMessageFlagInfos)
|
||||||
if (info.name == name)
|
if (info.name == name)
|
||||||
|
@ -207,15 +207,15 @@ message_flag_info(std::string_view name)
|
||||||
* @param ignore_invalid if @true, ignore invalid flags, otherwise return
|
* @param ignore_invalid if @true, ignore invalid flags, otherwise return
|
||||||
* nullopt if an invalid flag is encountered
|
* nullopt if an invalid flag is encountered
|
||||||
*
|
*
|
||||||
* @return the (OR'ed) flags or MessageFlags::None
|
* @return the (OR'ed) flags or Flags::None
|
||||||
*/
|
*/
|
||||||
constexpr std::optional<MessageFlags>
|
constexpr std::optional<Flags>
|
||||||
message_flags_from_absolute_expr(std::string_view expr, bool ignore_invalid = false)
|
flags_from_absolute_expr(std::string_view expr, bool ignore_invalid = false)
|
||||||
{
|
{
|
||||||
MessageFlags flags{MessageFlags::None};
|
Flags flags{Flags::None};
|
||||||
|
|
||||||
for (auto&& kar : expr) {
|
for (auto&& kar : expr) {
|
||||||
if (const auto& info{message_flag_info(kar)}; !info) {
|
if (const auto& info{flag_info(kar)}; !info) {
|
||||||
if (!ignore_invalid)
|
if (!ignore_invalid)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
} else
|
} else
|
||||||
|
@ -242,15 +242,15 @@ message_flags_from_absolute_expr(std::string_view expr, bool ignore_invalid = fa
|
||||||
*
|
*
|
||||||
* @return new flags, or nullopt in case of error
|
* @return new flags, or nullopt in case of error
|
||||||
*/
|
*/
|
||||||
constexpr std::optional<MessageFlags>
|
constexpr std::optional<Flags>
|
||||||
message_flags_from_delta_expr(std::string_view expr, MessageFlags flags,
|
flags_from_delta_expr(std::string_view expr, Flags flags,
|
||||||
bool ignore_invalid = false)
|
bool ignore_invalid = false)
|
||||||
{
|
{
|
||||||
if (expr.size() % 2 != 0)
|
if (expr.size() % 2 != 0)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
for (auto u = 0U; u != expr.size(); u += 2) {
|
for (auto u = 0U; u != expr.size(); u += 2) {
|
||||||
if (const auto& info{message_flag_info(expr[u + 1])}; !info) {
|
if (const auto& info{flag_info(expr[u + 1])}; !info) {
|
||||||
if (!ignore_invalid)
|
if (!ignore_invalid)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
} else {
|
} else {
|
||||||
|
@ -276,18 +276,18 @@ message_flags_from_delta_expr(std::string_view expr, MessageFlags flags,
|
||||||
*
|
*
|
||||||
* @return either messages flags or std::nullopt in case of error.
|
* @return either messages flags or std::nullopt in case of error.
|
||||||
*/
|
*/
|
||||||
constexpr std::optional<MessageFlags>
|
constexpr std::optional<Flags>
|
||||||
message_flags_from_expr(std::string_view expr,
|
flags_from_expr(std::string_view expr,
|
||||||
std::optional<MessageFlags> flags = std::nullopt)
|
std::optional<Flags> flags = std::nullopt)
|
||||||
{
|
{
|
||||||
if (expr.empty())
|
if (expr.empty())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if (expr[0] == '+' || expr[0] == '-')
|
if (expr[0] == '+' || expr[0] == '-')
|
||||||
return message_flags_from_delta_expr(
|
return flags_from_delta_expr(
|
||||||
expr, flags.value_or(MessageFlags::None), true);
|
expr, flags.value_or(Flags::None), true);
|
||||||
else
|
else
|
||||||
return message_flags_from_absolute_expr(expr, true);
|
return flags_from_absolute_expr(expr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -298,8 +298,8 @@ message_flags_from_expr(std::string_view expr,
|
||||||
*
|
*
|
||||||
* @return filter flags
|
* @return filter flags
|
||||||
*/
|
*/
|
||||||
constexpr MessageFlags
|
constexpr Flags
|
||||||
message_flags_filter(MessageFlags flags, MessageFlagCategory cat)
|
flags_filter(Flags flags, MessageFlagCategory cat)
|
||||||
{
|
{
|
||||||
for (auto&& info : AllMessageFlagInfos)
|
for (auto&& info : AllMessageFlagInfos)
|
||||||
if (info.category != cat)
|
if (info.category != cat)
|
||||||
|
@ -314,8 +314,8 @@ message_flags_filter(MessageFlags flags, MessageFlagCategory cat)
|
||||||
*
|
*
|
||||||
* @return string as a sequence of message-flag shortcuts
|
* @return string as a sequence of message-flag shortcuts
|
||||||
*/
|
*/
|
||||||
std::string message_flags_to_string(MessageFlags flags);
|
std::string flags_to_string(Flags flags);
|
||||||
|
|
||||||
} // namespace Mu
|
} // namespace Mu
|
||||||
|
|
||||||
#endif /* MU_MESSAGE_FLAGS_HH__ */
|
#endif /* MU_FLAGS_HH__ */
|
|
@ -0,0 +1,425 @@
|
||||||
|
/*
|
||||||
|
** 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.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "mu-message.hh"
|
||||||
|
#include "mu-maildir.hh"
|
||||||
|
|
||||||
|
#include <utils/mu-util.h>
|
||||||
|
#include <utils/mu-utils.hh>
|
||||||
|
#include <utils/mu-error.hh>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <gmime/gmime.h>
|
||||||
|
|
||||||
|
#include "gmime/gmime-message.h"
|
||||||
|
|
||||||
|
using namespace Mu;
|
||||||
|
|
||||||
|
/* note, we do the gmime initialization here rather than in mu-runtime, because this way
|
||||||
|
* we don't need mu-runtime for simple cases -- such as our unit tests. Also note that we
|
||||||
|
* need gmime init even for the doc backend, as we use the address parsing functions also
|
||||||
|
* there. */
|
||||||
|
static bool
|
||||||
|
gmime_maybe_init(void)
|
||||||
|
{
|
||||||
|
static std::atomic_bool gmime_initialized = false;
|
||||||
|
|
||||||
|
if (gmime_initialized)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
static std::mutex lock;
|
||||||
|
g_debug("initializing gmime %u.%u.%u",
|
||||||
|
gmime_major_version,
|
||||||
|
gmime_minor_version,
|
||||||
|
gmime_micro_version);
|
||||||
|
|
||||||
|
g_mime_init();
|
||||||
|
std::atexit([] {
|
||||||
|
g_debug("shutting down gmime");
|
||||||
|
g_mime_shutdown();
|
||||||
|
gmime_initialized = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GMimeMessage*
|
||||||
|
make_mime_message(const std::string& path, GError** err)
|
||||||
|
{
|
||||||
|
GMimeStream *stream{g_mime_stream_file_open(path.c_str(), "r", err)};
|
||||||
|
if (!stream)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
GMimeParser *parser{g_mime_parser_new_with_stream(stream)};
|
||||||
|
g_object_unref(stream);
|
||||||
|
if (!parser) {
|
||||||
|
g_set_error(err,MU_ERROR_DOMAIN, MU_ERROR_GMIME,
|
||||||
|
"cannot create mime parser for %s", path.c_str());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
GMimeMessage *mime_msg{g_mime_parser_construct_message(parser, NULL)};
|
||||||
|
g_object_unref(parser);
|
||||||
|
if (!mime_msg) {
|
||||||
|
g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR_GMIME,
|
||||||
|
"message seems invalid, ignoring (%s)", path.c_str());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return mime_msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fill_document(Document& doc, GMimeMessage *mime_msg);
|
||||||
|
|
||||||
|
|
||||||
|
Message::Message(const std::string& path, const std::string& mdir)
|
||||||
|
{
|
||||||
|
gmime_maybe_init();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sanity checks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!g_path_is_absolute(path.c_str()))
|
||||||
|
throw Error(Error::Code::File, "path '%s' is not absolute", path.c_str());
|
||||||
|
|
||||||
|
if (::access(path.c_str(), R_OK) != 0)
|
||||||
|
throw Error(Error::Code::File, "file @ '%s' is not readable", path.c_str());
|
||||||
|
|
||||||
|
struct stat statbuf{};
|
||||||
|
if (::stat(path.c_str(), &statbuf) < 0)
|
||||||
|
throw Error(Error::Code::File, "cannot stat %s: %s", path.c_str(),
|
||||||
|
g_strerror(errno));
|
||||||
|
|
||||||
|
if (!S_ISREG(statbuf.st_mode))
|
||||||
|
throw Error(Error::Code::File, "not a regular file: %s", path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* let's get the mime message
|
||||||
|
*/
|
||||||
|
GError *err{};
|
||||||
|
mime_msg_ = make_mime_message(path, &err);
|
||||||
|
if (!mime_msg_)
|
||||||
|
throw Error(Error::Code::File, &err, "invalid message");
|
||||||
|
|
||||||
|
doc_.add(Field::Id::Path,
|
||||||
|
Mu::from_gchars(g_canonicalize_filename(path.c_str(), NULL)));
|
||||||
|
doc_.add(Field::Id::Maildir, mdir);
|
||||||
|
doc_.add(Field::Id::Size, static_cast<int64_t>(statbuf.st_size));
|
||||||
|
|
||||||
|
// rest of the fields
|
||||||
|
//fill_fields(doc_, mime_msg_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::~Message()
|
||||||
|
{
|
||||||
|
g_clear_object(&mime_msg_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Message&
|
||||||
|
Message::operator=(const Message& rhs) {
|
||||||
|
|
||||||
|
if (this != &rhs) {
|
||||||
|
doc_ = rhs.doc_;
|
||||||
|
g_clear_object(&mime_msg_);
|
||||||
|
if (rhs.mime_msg_)
|
||||||
|
mime_msg_ = g_object_ref(rhs.mime_msg_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Message&
|
||||||
|
Message::operator=(Message&& rhs)
|
||||||
|
{
|
||||||
|
if (this != &rhs) {
|
||||||
|
doc_ = std::move(rhs.doc_);
|
||||||
|
rhs.doc_ = {};
|
||||||
|
|
||||||
|
g_clear_object(&mime_msg_);
|
||||||
|
mime_msg_ = rhs.mime_msg_;
|
||||||
|
rhs.mime_msg_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Priority
|
||||||
|
parse_prio_str(const char* priostr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct {
|
||||||
|
const char* _str;
|
||||||
|
Priority _prio;
|
||||||
|
} str_prio[] = {{"high", Priority::High},
|
||||||
|
{"1", Priority::High},
|
||||||
|
{"2", Priority::High},
|
||||||
|
|
||||||
|
{"normal", Priority::Normal},
|
||||||
|
{"3", Priority::Normal},
|
||||||
|
|
||||||
|
{"low", Priority::Low},
|
||||||
|
{"list", Priority::Low},
|
||||||
|
{"bulk", Priority::Low},
|
||||||
|
{"4", Priority::Low},
|
||||||
|
{"5", Priority::Low}};
|
||||||
|
|
||||||
|
for (i = 0; i != G_N_ELEMENTS(str_prio); ++i)
|
||||||
|
if (g_ascii_strcasecmp(priostr, str_prio[i]._str) == 0)
|
||||||
|
return str_prio[i]._prio;
|
||||||
|
|
||||||
|
/* e.g., last-fm uses 'fm-user'... as precedence */
|
||||||
|
return Priority::Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Priority
|
||||||
|
get_priority(GMimeMessage *mime_msg)
|
||||||
|
{
|
||||||
|
auto obj{GMIME_OBJECT(mime_msg)};
|
||||||
|
auto priostr = g_mime_object_get_header(obj, "Precedence");
|
||||||
|
if (!priostr)
|
||||||
|
priostr = g_mime_object_get_header(obj, "X-Priority");
|
||||||
|
if (!priostr)
|
||||||
|
priostr = g_mime_object_get_header(obj, "Importance");
|
||||||
|
if (!priostr)
|
||||||
|
return Priority::Normal;
|
||||||
|
else
|
||||||
|
return parse_prio_str(priostr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
looks_like_attachment(GMimeObject* part)
|
||||||
|
{
|
||||||
|
GMimeContentDisposition* disp;
|
||||||
|
GMimeContentType* ctype;
|
||||||
|
const char* dispstr;
|
||||||
|
guint u;
|
||||||
|
const struct {
|
||||||
|
const char* type;
|
||||||
|
const char* sub_type;
|
||||||
|
} att_types[] = {{"image", "*"},
|
||||||
|
{"audio", "*"},
|
||||||
|
{"application", "*"},
|
||||||
|
{"application", "x-patch"}};
|
||||||
|
|
||||||
|
disp = g_mime_object_get_content_disposition(part);
|
||||||
|
|
||||||
|
if (!GMIME_IS_CONTENT_DISPOSITION(disp))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
dispstr = g_mime_content_disposition_get_disposition(disp);
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp(dispstr, "attachment") == 0)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* we also consider patches, images, audio, and non-pgp-signature
|
||||||
|
* application attachments to be attachments... */
|
||||||
|
ctype = g_mime_object_get_content_type(part);
|
||||||
|
|
||||||
|
if (g_mime_content_type_is_type(ctype, "*", "pgp-signature"))
|
||||||
|
return FALSE; /* don't consider as a signature */
|
||||||
|
|
||||||
|
if (g_mime_content_type_is_type(ctype, "text", "*")) {
|
||||||
|
if (g_mime_content_type_is_type(ctype, "*", "plain") ||
|
||||||
|
g_mime_content_type_is_type(ctype, "*", "html"))
|
||||||
|
return FALSE;
|
||||||
|
else
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u = 0; u != G_N_ELEMENTS(att_types); ++u)
|
||||||
|
if (g_mime_content_type_is_type(ctype, att_types[u].type, att_types[u].sub_type))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
msg_cflags_cb(GMimeObject* parent, GMimeObject* part, Flags* flags)
|
||||||
|
{
|
||||||
|
if (GMIME_IS_MULTIPART_SIGNED(part))
|
||||||
|
*flags |= Flags::Signed;
|
||||||
|
|
||||||
|
/* FIXME: An encrypted part might be signed at the same time.
|
||||||
|
* In that case the signed flag is lost. */
|
||||||
|
if (GMIME_IS_MULTIPART_ENCRYPTED(part))
|
||||||
|
*flags |= Flags::Encrypted;
|
||||||
|
|
||||||
|
/* smime */
|
||||||
|
if (GMIME_IS_APPLICATION_PKCS7_MIME(part)) {
|
||||||
|
GMimeApplicationPkcs7Mime *pkcs7;
|
||||||
|
pkcs7 = GMIME_APPLICATION_PKCS7_MIME(part);
|
||||||
|
if (pkcs7) {
|
||||||
|
switch(pkcs7->smime_type) {
|
||||||
|
case GMIME_SECURE_MIME_TYPE_ENVELOPED_DATA:
|
||||||
|
*flags |= Flags::Encrypted;
|
||||||
|
break;
|
||||||
|
case GMIME_SECURE_MIME_TYPE_SIGNED_DATA:
|
||||||
|
*flags |= Flags::Signed;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any_of(*flags & Flags::HasAttachment))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!GMIME_IS_PART(part))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (looks_like_attachment(part))
|
||||||
|
*flags |= Flags::HasAttachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Flags
|
||||||
|
get_content_flags(GMimeMessage *mime_msg)
|
||||||
|
{
|
||||||
|
Flags flags{Flags::None};
|
||||||
|
|
||||||
|
/* toplevel */
|
||||||
|
msg_cflags_cb(NULL, GMIME_OBJECT(mime_msg), &flags);
|
||||||
|
/* parts */
|
||||||
|
// mu_mime_message_foreach(mime_msg,
|
||||||
|
// FALSE, /* never decrypt for this */
|
||||||
|
// (GMimeObjectForeachFunc)msg_cflags_cb,
|
||||||
|
// &flags);
|
||||||
|
|
||||||
|
|
||||||
|
// char *ml{get_mailing_list(self)};
|
||||||
|
// if (ml) {
|
||||||
|
// flags |= Flags::MailingList;
|
||||||
|
// g_free(ml);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Flags
|
||||||
|
get_flags(GMimeMessage *mime_msg, const std::string& path)
|
||||||
|
{
|
||||||
|
auto flags{mu_maildir_flags_from_path(path)
|
||||||
|
.value_or(Flags::None)};
|
||||||
|
flags |= get_content_flags(mime_msg);
|
||||||
|
|
||||||
|
/* pseudo-flag --> unread means either NEW or NOT SEEN, just
|
||||||
|
* for searching convenience */
|
||||||
|
if (any_of(flags & Flags::New) ||
|
||||||
|
none_of(flags & Flags::Seen))
|
||||||
|
flags |= Flags::Unread;
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fill_document(Document& doc, GMimeMessage *mime_msg)
|
||||||
|
{
|
||||||
|
|
||||||
|
//const auto contacts{mu_msg_get_contacts(msg)};
|
||||||
|
const auto path{doc.string_value(Field::Id::Path)};
|
||||||
|
|
||||||
|
// auto add_str=[&](Document& doc, Field::Id field_id, const char *str) {
|
||||||
|
// if (str)
|
||||||
|
// doc.add(field_id, std::string(str));
|
||||||
|
// };
|
||||||
|
|
||||||
|
field_for_each([&](auto&& field) {
|
||||||
|
|
||||||
|
if (!field.is_indexable_term() && !field.is_normal_term() && !field.is_value())
|
||||||
|
return;
|
||||||
|
// else if (field.is_contact())
|
||||||
|
// doc.add(field.id, contacts);
|
||||||
|
else if (field.id == Field::Id::Priority)
|
||||||
|
doc.add(get_priority(mime_msg));
|
||||||
|
// else if (field.id == Field::Id::Flags)
|
||||||
|
// doc.add(get_flags(mime_
|
||||||
|
else if (field.id == Field::Id::ThreadId) {
|
||||||
|
// refs contains a list of parent messages, with the
|
||||||
|
// oldest one first until the last one, which is the
|
||||||
|
// direct parent of the current message. of course, it
|
||||||
|
// may be empty.
|
||||||
|
//
|
||||||
|
// NOTE: there may be cases where the list is truncated;
|
||||||
|
// we happily ignore that case.
|
||||||
|
// const auto refs{mu_msg_get_references(msg)};
|
||||||
|
// const auto thread_id{refs ? (const char*)refs->data : mu_msg_get_msgid(msg)};
|
||||||
|
// doc.add(Field::Id::ThreadId, std::string(thread_id));
|
||||||
|
}
|
||||||
|
// else if (field.id == Field::Id::BodyText)
|
||||||
|
// add_str(doc, field.id, mu_msg_get_body_text(msg, MU_MSG_OPTION_NONE));
|
||||||
|
// else if (field.id == Field::Id::BodyHtml)
|
||||||
|
// add_str(doc, field.id, mu_msg_get_body_html(msg, MU_MSG_OPTION_NONE));
|
||||||
|
// else if (field.id == Field::Id::EmbeddedText || field.id == Field::Id::File) {
|
||||||
|
// /* handle with MIME */
|
||||||
|
// } else if (field.id == Field::Id::Mime)
|
||||||
|
// mu_msg_part_foreach(msg, MU_MSG_OPTION_RECURSE_RFC822,
|
||||||
|
// (MuMsgPartForeachFunc)each_part, &doc);
|
||||||
|
// else if (field.is_numerical())
|
||||||
|
// doc.add(field.id, mu_msg_get_field_numeric(msg, field.id));
|
||||||
|
// else if (field.is_string())
|
||||||
|
// add_str(doc, field.id, mu_msg_get_field_string(msg, field.id));
|
||||||
|
// else if (field.is_string_list()) {
|
||||||
|
// std::vector<std::string> vec;
|
||||||
|
// auto vals{mu_msg_get_field_string_list(msg, field.id)};
|
||||||
|
// while (vals) {
|
||||||
|
// vec.emplace_back ((const char*)vals->data);
|
||||||
|
// vals = g_slist_next((GList*)vals);
|
||||||
|
// }
|
||||||
|
// doc.add(field.id, vec);
|
||||||
|
else {
|
||||||
|
g_warning("unhandled field %*s", STR_V(field.name));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//contacts_cache_.add(std::move(contacts));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Message::header(const std::string& header_field) const
|
||||||
|
{
|
||||||
|
if (!mime_msg_)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const char *hdr = g_mime_object_get_header(GMIME_OBJECT(mime_msg_),
|
||||||
|
header_field.c_str());
|
||||||
|
if (!hdr)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (!g_utf8_validate(hdr, -1, {})) {
|
||||||
|
char *hdr_u{g_strdup(hdr)};
|
||||||
|
for (auto c = hdr_u; c && *c; ++c) {
|
||||||
|
if ((!isprint(*c) && !isspace (*c)) || !isascii(*c))
|
||||||
|
*c = '.';
|
||||||
|
}
|
||||||
|
return from_gchars(std::move(hdr_u));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hdr;
|
||||||
|
}
|
|
@ -0,0 +1,244 @@
|
||||||
|
/*
|
||||||
|
** 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_HH__
|
||||||
|
#define MU_MESSAGE_HH__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "mu-contact.hh"
|
||||||
|
#include "mu-priority.hh"
|
||||||
|
#include "mu-flags.hh"
|
||||||
|
#include "mu-fields.hh"
|
||||||
|
#include "mu-document.hh"
|
||||||
|
|
||||||
|
struct _GMimeMessage;
|
||||||
|
|
||||||
|
namespace Mu {
|
||||||
|
|
||||||
|
class Message {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Construct a message based on a path
|
||||||
|
*
|
||||||
|
* @param path path to message
|
||||||
|
* @param mdir the maildir for this message; ie, if the path is
|
||||||
|
* ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar; you can
|
||||||
|
* pass NULL for this parameter, in which case some maildir-specific
|
||||||
|
* information is not available.
|
||||||
|
*/
|
||||||
|
Message(const std::string& path, const std::string& mdir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a message based on a Message::Document
|
||||||
|
*
|
||||||
|
* @param doc
|
||||||
|
*/
|
||||||
|
Message(Document& doc): doc_{doc} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy CTOR
|
||||||
|
*
|
||||||
|
* @param rhs a Message
|
||||||
|
*/
|
||||||
|
Message(const Message& rhs) {
|
||||||
|
*this = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move CTOR
|
||||||
|
*
|
||||||
|
* @param rhs a Message
|
||||||
|
*/
|
||||||
|
|
||||||
|
Message(Message&& rhs) {
|
||||||
|
*this = std::move(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTOR
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
~Message();
|
||||||
|
/**
|
||||||
|
* Copy assignment operator
|
||||||
|
*
|
||||||
|
* @param rhs some message
|
||||||
|
*
|
||||||
|
* @return a message ref
|
||||||
|
*/
|
||||||
|
Message& operator=(const Message& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move assignment operator
|
||||||
|
*
|
||||||
|
* @param rhs some message
|
||||||
|
*
|
||||||
|
* @return a message ref
|
||||||
|
*/
|
||||||
|
Message& operator=(Message&& rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the document.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return document
|
||||||
|
*/
|
||||||
|
const Document& document() const { return doc_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file system path of this message
|
||||||
|
*
|
||||||
|
* @return the path of this Message or NULL in case of error.
|
||||||
|
* the returned string should *not* be modified or freed.
|
||||||
|
*/
|
||||||
|
std::string path() const { return doc_.string_value(Field::Id::Path); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the sender (From:) of this message
|
||||||
|
*
|
||||||
|
* @return the sender(s) of this Message
|
||||||
|
*/
|
||||||
|
Contacts from() const { return doc_.contacts_value(Field::Id::From); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the recipient(s) (To:) for this message
|
||||||
|
*
|
||||||
|
* @return recipients
|
||||||
|
*/
|
||||||
|
Contacts to() const { return doc_.contacts_value(Field::Id::To); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the recipient(s) (Cc:) for this message
|
||||||
|
*
|
||||||
|
* @return recipients
|
||||||
|
*/
|
||||||
|
Contacts cc() const { return doc_.contacts_value(Field::Id::Cc); }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the recipient(s) (Bcc:) for this message
|
||||||
|
*
|
||||||
|
* @return recipients
|
||||||
|
*/
|
||||||
|
Contacts bcc() const { return doc_.contacts_value(Field::Id::Bcc); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maildir this message lives in; ie, if the path is
|
||||||
|
* ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar
|
||||||
|
*
|
||||||
|
* @return the maildir requested or empty */
|
||||||
|
std::string maildir() const { return doc_.string_value(Field::Id::Maildir); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the subject of this message
|
||||||
|
*
|
||||||
|
* @return the subject of this Message
|
||||||
|
*/
|
||||||
|
std::string subject() const { return doc_.string_value(Field::Id::Subject); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Message-Id of this message
|
||||||
|
*
|
||||||
|
* @return the Message-Id of this message (without the enclosing <>), or
|
||||||
|
* a fake message-id for messages that don't have them
|
||||||
|
*/
|
||||||
|
std::string message_id() const { return doc_.string_value(Field::Id::MessageId);}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the mailing list for a message, i.e. the mailing-list
|
||||||
|
* identifier in the List-Id header.
|
||||||
|
*
|
||||||
|
* @return the mailing list id for this message (without the enclosing <>)
|
||||||
|
* or NULL in case of error or if there is none.
|
||||||
|
*/
|
||||||
|
std::string mailing_list() const { return doc_.string_value(Field::Id::MailingList);}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the message date/time (the Date: field) as time_t, using UTC
|
||||||
|
*
|
||||||
|
* @return message date/time or 0 in case of error or if there
|
||||||
|
* is no such header.
|
||||||
|
*/
|
||||||
|
time_t date() const { return static_cast<time_t>(doc_.integer_value(Field::Id::Date)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the flags for this message
|
||||||
|
*
|
||||||
|
* @return the file/content flags
|
||||||
|
*/
|
||||||
|
Flags flags() const { return doc_.flags_value(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the message priority for this message. The X-Priority, X-MSMailPriority,
|
||||||
|
* Importance and Precedence header are checked, in that order. if no known or
|
||||||
|
* explicit priority is set, Priority::Id::Normal is assumed
|
||||||
|
*
|
||||||
|
* @return the message priority
|
||||||
|
*/
|
||||||
|
Priority priority() const { return doc_.priority_value(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the file size in bytes of this message
|
||||||
|
*
|
||||||
|
* @return the filesize
|
||||||
|
*/
|
||||||
|
size_t size() const { return static_cast<size_t>(doc_.integer_value(Field::Id::Size)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the list of references (consisting of both the References and
|
||||||
|
* In-Reply-To fields), with the oldest first and the direct parent as
|
||||||
|
* the last one. Note, any reference (message-id) will appear at most
|
||||||
|
* once, duplicates are filtered out.
|
||||||
|
*
|
||||||
|
* @return a vec with the references for this msg.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> references() const {
|
||||||
|
return doc_.string_vec_value(Field::Id::References);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the list of tags (ie., X-Label)
|
||||||
|
*
|
||||||
|
* @param msg a valid MuMsg
|
||||||
|
*
|
||||||
|
* @return a list with the tags for this msg. Don't modify/free
|
||||||
|
*/
|
||||||
|
std::vector<std::string> tags() const {
|
||||||
|
return doc_.string_vec_value(Field::Id::References);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get some message-header
|
||||||
|
*
|
||||||
|
* @param header_field name of the header
|
||||||
|
*
|
||||||
|
* @return the value
|
||||||
|
*/
|
||||||
|
std::string header(const std::string& header_field) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Document doc_;
|
||||||
|
mutable struct _GMimeMessage *mime_msg_{};
|
||||||
|
|
||||||
|
}; // Message
|
||||||
|
} // Mu
|
||||||
|
#endif /* MU_MESSAGE_HH__ */
|
|
@ -17,17 +17,14 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MU_MESSAGE_PRIORITY_CC__
|
#include "mu-priority.hh"
|
||||||
#define MU_MESSAGE_PRIORITY_CC__
|
|
||||||
|
|
||||||
#include "mu-message-priority.hh"
|
|
||||||
|
|
||||||
using namespace Mu;
|
using namespace Mu;
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
Mu::to_string(MessagePriority prio)
|
Mu::to_string(Priority prio)
|
||||||
{
|
{
|
||||||
return std::string{message_priority_name(prio)};
|
return std::string{priority_name(prio)};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -41,26 +38,26 @@ Mu::to_string(MessagePriority prio)
|
||||||
[[maybe_unused]] static void
|
[[maybe_unused]] static void
|
||||||
test_priority_to_char()
|
test_priority_to_char()
|
||||||
{
|
{
|
||||||
static_assert(to_char(MessagePriority::Low) == 'l');
|
static_assert(to_char(Priority::Low) == 'l');
|
||||||
static_assert(to_char(MessagePriority::Normal) == 'n');
|
static_assert(to_char(Priority::Normal) == 'n');
|
||||||
static_assert(to_char(MessagePriority::High) == 'h');
|
static_assert(to_char(Priority::High) == 'h');
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] static void
|
[[maybe_unused]] static void
|
||||||
test_priority_from_char()
|
test_priority_from_char()
|
||||||
{
|
{
|
||||||
static_assert(message_priority_from_char('l') == MessagePriority::Low);
|
static_assert(priority_from_char('l') == Priority::Low);
|
||||||
static_assert(message_priority_from_char('n') == MessagePriority::Normal);
|
static_assert(priority_from_char('n') == Priority::Normal);
|
||||||
static_assert(message_priority_from_char('h') == MessagePriority::High);
|
static_assert(priority_from_char('h') == Priority::High);
|
||||||
static_assert(message_priority_from_char('x') == MessagePriority::Normal);
|
static_assert(priority_from_char('x') == Priority::Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] static void
|
[[maybe_unused]] static void
|
||||||
test_priority_name()
|
test_priority_name()
|
||||||
{
|
{
|
||||||
static_assert(message_priority_name(MessagePriority::Low) == "low");
|
static_assert(priority_name(Priority::Low) == "low");
|
||||||
static_assert(message_priority_name(MessagePriority::Normal) == "normal");
|
static_assert(priority_name(Priority::Normal) == "normal");
|
||||||
static_assert(message_priority_name(MessagePriority::High) == "high");
|
static_assert(priority_name(Priority::High) == "high");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,6 +74,3 @@ main(int argc, char* argv[])
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
#endif /*BUILD_TESTS*/
|
#endif /*BUILD_TESTS*/
|
||||||
|
|
||||||
|
|
||||||
#endif /* MU_MESSAGE_PRIORITY_CC__ */
|
|
|
@ -17,13 +17,13 @@
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MU_MESSAGE_PRIORITY_HH__
|
#ifndef MU_PRIORITY_HH__
|
||||||
#define MU_MESSAGE_PRIORITY_HH__
|
#define MU_PRIORITY_HH__
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "mu-message-fields.hh"
|
#include "mu-fields.hh"
|
||||||
|
|
||||||
namespace Mu {
|
namespace Mu {
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +35,7 @@ namespace Mu {
|
||||||
* The priority ids
|
* The priority ids
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
enum struct MessagePriority : char {
|
enum struct Priority : char {
|
||||||
Low = 'l', /**< Low priority */
|
Low = 'l', /**< Low priority */
|
||||||
Normal = 'n', /**< Normal priority */
|
Normal = 'n', /**< Normal priority */
|
||||||
High = 'h', /**< High priority */
|
High = 'h', /**< High priority */
|
||||||
|
@ -44,8 +44,8 @@ enum struct MessagePriority : char {
|
||||||
/**
|
/**
|
||||||
* Sequence of all message priorities.
|
* Sequence of all message priorities.
|
||||||
*/
|
*/
|
||||||
static constexpr std::array<MessagePriority, 3> AllMessagePriorities = {
|
static constexpr std::array<Priority, 3> AllMessagePriorities = {
|
||||||
MessagePriority::Low, MessagePriority::Normal, MessagePriority::High};
|
Priority::Low, Priority::Normal, Priority::High};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the char for some priority
|
* Get the char for some priority
|
||||||
|
@ -55,7 +55,7 @@ static constexpr std::array<MessagePriority, 3> AllMessagePriorities = {
|
||||||
* @return the char
|
* @return the char
|
||||||
*/
|
*/
|
||||||
constexpr char
|
constexpr char
|
||||||
to_char(MessagePriority prio)
|
to_char(Priority prio)
|
||||||
{
|
{
|
||||||
return static_cast<char>(prio);
|
return static_cast<char>(prio);
|
||||||
}
|
}
|
||||||
|
@ -66,17 +66,17 @@ to_char(MessagePriority prio)
|
||||||
*
|
*
|
||||||
* @param c some character
|
* @param c some character
|
||||||
*/
|
*/
|
||||||
constexpr MessagePriority
|
constexpr Priority
|
||||||
message_priority_from_char(char c)
|
priority_from_char(char c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'l':
|
case 'l':
|
||||||
return MessagePriority::Low;
|
return Priority::Low;
|
||||||
case 'h':
|
case 'h':
|
||||||
return MessagePriority::High;
|
return Priority::High;
|
||||||
case 'n':
|
case 'n':
|
||||||
default:
|
default:
|
||||||
return MessagePriority::Normal;
|
return Priority::Normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,14 +86,14 @@ message_priority_from_char(char c)
|
||||||
* @return the name
|
* @return the name
|
||||||
*/
|
*/
|
||||||
constexpr std::string_view
|
constexpr std::string_view
|
||||||
message_priority_name(MessagePriority prio)
|
priority_name(Priority prio)
|
||||||
{
|
{
|
||||||
switch (prio) {
|
switch (prio) {
|
||||||
case MessagePriority::Low:
|
case Priority::Low:
|
||||||
return "low";
|
return "low";
|
||||||
case MessagePriority::High:
|
case Priority::High:
|
||||||
return "high";
|
return "high";
|
||||||
case MessagePriority::Normal:
|
case Priority::Normal:
|
||||||
default:
|
default:
|
||||||
return "normal";
|
return "normal";
|
||||||
}
|
}
|
||||||
|
@ -105,12 +105,12 @@ message_priority_name(MessagePriority prio)
|
||||||
* @return the name
|
* @return the name
|
||||||
*/
|
*/
|
||||||
constexpr const char*
|
constexpr const char*
|
||||||
message_priority_name_c_str(MessagePriority prio)
|
priority_name_c_str(Priority prio)
|
||||||
{
|
{
|
||||||
switch (prio) {
|
switch (prio) {
|
||||||
case MessagePriority::Low: return "low";
|
case Priority::Low: return "low";
|
||||||
case MessagePriority::High: return "high";
|
case Priority::High: return "high";
|
||||||
case MessagePriority::Normal:
|
case Priority::Normal:
|
||||||
default: return "normal";
|
default: return "normal";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,8 +122,8 @@ message_priority_name_c_str(MessagePriority prio)
|
||||||
*
|
*
|
||||||
* @return a string
|
* @return a string
|
||||||
*/
|
*/
|
||||||
std::string to_string(MessagePriority prio);
|
std::string to_string(Priority prio);
|
||||||
|
|
||||||
} // namespace Mu
|
} // namespace Mu
|
||||||
|
|
||||||
#endif /*MU_MESSAGE_PRIORITY_HH_*/
|
#endif /*MU_PRIORITY_HH_*/
|
|
@ -1,170 +0,0 @@
|
||||||
/*
|
|
||||||
** 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* tests... also build as runtime-tests, so we can get coverage info
|
|
||||||
*/
|
|
||||||
#ifdef BUILD_TESTS
|
|
||||||
#define static_assert g_assert_true
|
|
||||||
#endif /*BUILD_TESTS*/
|
|
||||||
|
|
||||||
[[maybe_unused]] static void
|
|
||||||
test_basic()
|
|
||||||
{
|
|
||||||
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())));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* message_flag_info
|
|
||||||
*/
|
|
||||||
[[maybe_unused]] static void
|
|
||||||
test_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"));
|
|
||||||
|
|
||||||
|
|
||||||
static_assert(message_flag_info('D')->shortcut_lower() == 'd');
|
|
||||||
static_assert(message_flag_info('u')->shortcut_lower() == 'u');
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* message_flags_from_expr
|
|
||||||
*/
|
|
||||||
[[maybe_unused]] static void
|
|
||||||
test_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
|
|
||||||
*/
|
|
||||||
[[maybe_unused]] static void
|
|
||||||
test_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
|
|
||||||
*/
|
|
||||||
[[maybe_unused]] static void
|
|
||||||
test_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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef BUILD_TESTS
|
|
||||||
int
|
|
||||||
main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
g_test_init(&argc, &argv, NULL);
|
|
||||||
|
|
||||||
g_test_add_func("/message/flags/basic", test_basic);
|
|
||||||
g_test_add_func("/message/flags/flag-info", test_message_flag_info);
|
|
||||||
g_test_add_func("/message/flags/flags-from-absolute-expr",
|
|
||||||
test_message_flags_from_expr);
|
|
||||||
g_test_add_func("/message/flags/flags-from-delta-expr",
|
|
||||||
test_message_flags_from_delta_expr);
|
|
||||||
g_test_add_func("/message/flags/flags-filter",
|
|
||||||
test_message_flags_filter);
|
|
||||||
|
|
||||||
return g_test_run();
|
|
||||||
}
|
|
||||||
#endif /*BUILD_TESTS*/
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
** 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_HH__
|
|
||||||
#define MU_MESSAGE_HH__
|
|
||||||
|
|
||||||
#include "mu-message-contact.hh"
|
|
||||||
#include "mu-message-priority.hh"
|
|
||||||
#include "mu-message-flags.hh"
|
|
||||||
#include "mu-message-fields.hh"
|
|
||||||
|
|
||||||
namespace Mu {
|
|
||||||
|
|
||||||
namespace Message {
|
|
||||||
|
|
||||||
using Contact = MessageContact;
|
|
||||||
using Contacts = MessageContacts;
|
|
||||||
using Priority = MessagePriority;
|
|
||||||
using Flags = MessageFlags;
|
|
||||||
using Field = MessageField;
|
|
||||||
|
|
||||||
} // Message
|
|
||||||
} // Mu
|
|
||||||
#endif /* MU_MESSAGE_HH__ */
|
|
|
@ -18,17 +18,18 @@
|
||||||
# tests
|
# tests
|
||||||
#
|
#
|
||||||
|
|
||||||
|
test('test-message-contact',
|
||||||
test('test-message-flags',
|
executable('test-message-contact',
|
||||||
executable('test-message-flags',
|
'../mu-message-contact.cc',
|
||||||
'../mu-message-flags.cc',
|
|
||||||
install: false,
|
install: false,
|
||||||
cpp_args: ['-DBUILD_TESTS'],
|
cpp_args: ['-DBUILD_TESTS'],
|
||||||
dependencies: [glib_dep, gmime_dep, lib_mu_dep,
|
dependencies: [glib_dep, gmime_dep, lib_mu_dep,
|
||||||
lib_test_mu_common_dep]))
|
lib_test_mu_common_dep]))
|
||||||
test('test-message-priority',
|
|
||||||
executable('test-message-priority',
|
|
||||||
'../mu-message-priority.cc',
|
test('test-message-document',
|
||||||
|
executable('test-message-document',
|
||||||
|
'../mu-message-document.cc',
|
||||||
install: false,
|
install: false,
|
||||||
cpp_args: ['-DBUILD_TESTS'],
|
cpp_args: ['-DBUILD_TESTS'],
|
||||||
dependencies: [glib_dep, gmime_dep, lib_mu_dep,
|
dependencies: [glib_dep, gmime_dep, lib_mu_dep,
|
||||||
|
@ -42,9 +43,16 @@ test('test-message-fields',
|
||||||
dependencies: [glib_dep, gmime_dep, lib_mu_dep,
|
dependencies: [glib_dep, gmime_dep, lib_mu_dep,
|
||||||
lib_test_mu_common_dep]))
|
lib_test_mu_common_dep]))
|
||||||
|
|
||||||
test('test-message-contact',
|
test('test-message-flags',
|
||||||
executable('test-message-contact',
|
executable('test-message-flags',
|
||||||
'../mu-message-contact.cc',
|
'../mu-message-flags.cc',
|
||||||
|
install: false,
|
||||||
|
cpp_args: ['-DBUILD_TESTS'],
|
||||||
|
dependencies: [glib_dep, gmime_dep, lib_mu_dep,
|
||||||
|
lib_test_mu_common_dep]))
|
||||||
|
test('test-message-priority',
|
||||||
|
executable('test-message-priority',
|
||||||
|
'../mu-message-priority.cc',
|
||||||
install: false,
|
install: false,
|
||||||
cpp_args: ['-DBUILD_TESTS'],
|
cpp_args: ['-DBUILD_TESTS'],
|
||||||
dependencies: [glib_dep, gmime_dep, lib_mu_dep,
|
dependencies: [glib_dep, gmime_dep, lib_mu_dep,
|
||||||
|
|
Loading…
Reference in New Issue