lib: Implement Mu::MessageContact
Implement a new struct Mu::MessageContact to usurps some of the different types for contact information. Sprinkle some "modern C++" on it for convenience.
This commit is contained in:
@ -63,6 +63,8 @@ lib_mu=static_library(
|
||||
'mu-msg-sexp.cc',
|
||||
'mu-msg.cc',
|
||||
'mu-msg.hh',
|
||||
'mu-message-contact.hh',
|
||||
'mu-message-contact.cc',
|
||||
'mu-message-flags.hh',
|
||||
'mu-message-flags.cc',
|
||||
'mu-message-priority.hh',
|
||||
|
||||
180
lib/mu-message-contact.cc
Normal file
180
lib/mu-message-contact.cc
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
** 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-contact.hh"
|
||||
#include <gmime/gmime.h>
|
||||
#include <glib.h>
|
||||
|
||||
using namespace Mu;
|
||||
|
||||
|
||||
std::string
|
||||
MessageContact::display_name() const
|
||||
{
|
||||
if (name.empty())
|
||||
return email;
|
||||
else
|
||||
return name + " <" + email + '>';
|
||||
}
|
||||
|
||||
Mu::MessageContacts
|
||||
Mu::make_message_contacts(InternetAddressList* addr_lst,
|
||||
MessageContact::Type type,
|
||||
::time_t message_date)
|
||||
{
|
||||
MessageContacts contacts;
|
||||
size_t num{};
|
||||
|
||||
g_return_val_if_fail(addr_lst, contacts);
|
||||
|
||||
auto lst_len{internet_address_list_length(addr_lst)};
|
||||
contacts.reserve(lst_len);
|
||||
for (auto i = 0; i != lst_len; ++i) {
|
||||
|
||||
auto&& addr{internet_address_list_get_address(addr_lst, i)};
|
||||
const auto name{internet_address_get_name(addr)};
|
||||
|
||||
if (G_UNLIKELY(!INTERNET_ADDRESS_IS_MAILBOX(addr)))
|
||||
continue;
|
||||
|
||||
const auto email{internet_address_mailbox_get_addr (
|
||||
INTERNET_ADDRESS_MAILBOX(addr))};
|
||||
if (G_UNLIKELY(!email))
|
||||
continue;
|
||||
|
||||
contacts.push_back(MessageContact{email, name ? name : "",
|
||||
type, message_date});
|
||||
++num;
|
||||
}
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
|
||||
Mu::MessageContacts
|
||||
Mu::make_message_contacts(const std::string& addrs,
|
||||
MessageContact::Type type,
|
||||
::time_t message_date)
|
||||
{
|
||||
auto addr_list = internet_address_list_parse(NULL, addrs.c_str());
|
||||
if (!addr_list) {
|
||||
g_warning("no addresses found in '%s'", addrs.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto contacts{make_message_contacts(addr_list, type, message_date)};
|
||||
g_object_unref(addr_list);
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
Mu::lowercase_hash(const std::string& s)
|
||||
{
|
||||
std::size_t djb = 5381; // djb hash
|
||||
for (const auto c : s)
|
||||
djb = ((djb << 5) + djb) +
|
||||
static_cast<size_t>(g_ascii_tolower(c));
|
||||
return djb;
|
||||
}
|
||||
|
||||
#ifdef BUILD_TESTS
|
||||
/*
|
||||
* Tests.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "utils/mu-utils.hh"
|
||||
|
||||
static void
|
||||
test_ctor_01()
|
||||
{
|
||||
MessageContact c{
|
||||
"foo@example.com",
|
||||
"Foo Bar",
|
||||
MessageContact::Type::Bcc,
|
||||
1645214647
|
||||
};
|
||||
|
||||
assert_equal(c.email, "foo@example.com");
|
||||
assert_equal(c.name, "Foo Bar");
|
||||
g_assert_true(c.type == MessageContact::Type::Bcc);
|
||||
g_assert_cmpuint(c.message_date,==,1645214647);
|
||||
|
||||
assert_equal(c.display_name(), "Foo Bar <foo@example.com>");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_ctor_02()
|
||||
{
|
||||
MessageContact c{
|
||||
"bar@example.com",
|
||||
"Blinky",
|
||||
1645215014,
|
||||
true, /* personal */
|
||||
13, /*freq*/
|
||||
12345 /* tstamp */
|
||||
};
|
||||
|
||||
assert_equal(c.email, "bar@example.com");
|
||||
assert_equal(c.name, "Blinky");
|
||||
g_assert_true(c.personal);
|
||||
g_assert_cmpuint(c.frequency,==,13);
|
||||
g_assert_cmpuint(c.tstamp,==,12345);
|
||||
assert_equal(c.name, "Blinky");
|
||||
g_assert_cmpuint(c.message_date,==,1645215014);
|
||||
|
||||
assert_equal(c.display_name(), "Blinky <bar@example.com>");
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_make_contacts()
|
||||
{
|
||||
const auto str = "Abc <boo@example.com>, "
|
||||
"Def <baa@example.com>, "
|
||||
"Ghi <zzz@example.com>";
|
||||
InternetAddressList *lst{
|
||||
internet_address_list_parse(NULL, str)};
|
||||
|
||||
g_assert_true(lst);
|
||||
const auto addrs{make_message_contacts(lst, MessageContact::Type::Cc, 54321 )};
|
||||
g_object_unref(lst);
|
||||
|
||||
g_assert_cmpuint(addrs.size(),==,3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_mime_init();
|
||||
|
||||
g_test_add_func("/lib/message-contacts/ctor-01", test_ctor_01);
|
||||
g_test_add_func("/lib/message-contacts/ctor-02", test_ctor_02);
|
||||
g_test_add_func("/lib/message-contacts/make-contacts", test_make_contacts);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
#endif /*BUILD_TESTS*/
|
||||
186
lib/mu-message-contact.hh
Normal file
186
lib/mu-message-contact.hh
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
** 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_CONTACT_HH__
|
||||
#define MU_MESSAGE_CONTACT_HH__
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
struct _InternetAddressList;
|
||||
|
||||
namespace Mu {
|
||||
|
||||
/**
|
||||
* Get the hash value for a lowercase value of s; useful for email-addresses
|
||||
*
|
||||
* @param s a string
|
||||
*
|
||||
* @return a hash value.
|
||||
*/
|
||||
size_t lowercase_hash(const std::string& s);
|
||||
|
||||
|
||||
struct MessageContact {
|
||||
/**
|
||||
* Contact types
|
||||
*/
|
||||
enum struct Type {
|
||||
To, /**< To recipient */
|
||||
From, /**< Sender */
|
||||
Cc, /**< Cc recipient */
|
||||
Bcc, /**< Bcc recipient */
|
||||
ReplyTo, /**< Reply-To wannabe recipient */
|
||||
Unknown, /**< Unknown type */
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a new MessageContact
|
||||
*
|
||||
* @param email_ email address
|
||||
* @param name_ name or empty
|
||||
* @param type_ contact type
|
||||
* @param message_date_ data for the message for this contact
|
||||
*/
|
||||
MessageContact(const std::string& email_, const std::string& name_ = "",
|
||||
Type type_ = Type::Unknown, time_t message_date_ = 0)
|
||||
: email{email_}, name{name_}, type{type_},
|
||||
message_date{message_date_}, personal{}, frequency{1}, tstamp{}
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new MessageContact
|
||||
*
|
||||
* @param email_ email address
|
||||
* @param name_ name or empty
|
||||
* @param personal_ is this a personal contact?
|
||||
* @param last_seen_ when was this contact last seen?
|
||||
* @param freq_ how often was this contact seen?
|
||||
*/
|
||||
MessageContact(const std::string& email_, const std::string& name_,
|
||||
time_t message_date_, bool personal_, size_t freq_,
|
||||
int64_t tstamp_)
|
||||
: email{email_}, name{name_}, type{Type::Unknown},
|
||||
message_date{message_date_}, personal{personal_}, frequency{freq_},
|
||||
tstamp{tstamp_}
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "display name" for this contact; basically, if there's a
|
||||
* non-empty name, it's
|
||||
* Jane Doe <email@example.com>
|
||||
* otherwise it's just the e-mail address.
|
||||
*
|
||||
* @return the display name
|
||||
*/
|
||||
std::string display_name() const;
|
||||
|
||||
/**
|
||||
* Operator==; based on the hash values (ie. lowercase e-mail address)
|
||||
*
|
||||
* @param rhs some other MessageContact
|
||||
*
|
||||
* @return true orf false.
|
||||
*/
|
||||
bool operator== (const MessageContact& rhs) const noexcept {
|
||||
return hash() == rhs.hash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a hash-value for this contact, which gets lazily calculated. This
|
||||
* is for use with container classes. This uses the _lowercase_ email
|
||||
* address.
|
||||
*
|
||||
* @return the hash
|
||||
*/
|
||||
size_t hash() const {
|
||||
static size_t cached_hash;
|
||||
if (cached_hash == 0) {
|
||||
cached_hash = lowercase_hash(email);
|
||||
}
|
||||
return cached_hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* data members
|
||||
*/
|
||||
|
||||
std::string email; /**< Email address for this contact.Not empty */
|
||||
std::string name; /**< Name for this contact; can be empty. */
|
||||
Type type{Type::Unknown}; /**< Type of contact */
|
||||
::time_t message_date; /**< date of the message from which the
|
||||
* contact originates */
|
||||
bool personal; /**< A personal message? */
|
||||
size_t frequency; /**< Frequency of this contact */
|
||||
int64_t tstamp; /**< Timestamp for this contact */
|
||||
|
||||
};
|
||||
|
||||
using MessageContacts = std::vector<MessageContact>;
|
||||
|
||||
/**
|
||||
* Create a sequence of MessageContact objects from an InternetAddressList
|
||||
*
|
||||
* @param addr_lst an address list
|
||||
* @param type the type of addresses
|
||||
* @param message_date the date of the message from which the InternetAddressList
|
||||
* originates.
|
||||
*
|
||||
* @return a sequence of MessageContact objects.
|
||||
*/
|
||||
MessageContacts
|
||||
make_message_contacts(/*const*/ struct _InternetAddressList* addr_lst,
|
||||
MessageContact::Type type, ::time_t message_date);
|
||||
|
||||
/**
|
||||
* Create a sequence of MessageContact objects from an InternetAddressList
|
||||
*
|
||||
* @param addrs a string with one more valid addresses (as per internet_address_list_parse())
|
||||
* @param type the type of addresses
|
||||
* @param message_date the date of the message from which the addresses originate
|
||||
*
|
||||
* @return a sequence of MessageContact objects.
|
||||
*/
|
||||
MessageContacts
|
||||
make_message_contacts(const std::string& addrs,
|
||||
MessageContact::Type type, ::time_t message_date);
|
||||
} // namespace Mu
|
||||
|
||||
/**
|
||||
* Implement our hash int std::
|
||||
*/
|
||||
template<> struct std::hash<Mu::MessageContact> {
|
||||
std::size_t operator()(const Mu::MessageContact& c) const noexcept {
|
||||
return c.hash();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* MU_MESSAGE_CONTACT_HH__ */
|
||||
@ -61,6 +61,14 @@ test('test_contacts',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, lib_mu_dep, lib_test_mu_common_dep]))
|
||||
|
||||
test('test_message_contact',
|
||||
executable('test-message-contact',
|
||||
'../mu-message-contact.cc',
|
||||
install: false,
|
||||
cpp_args: ['-DBUILD_TESTS'],
|
||||
dependencies: [glib_dep, gmime_dep, lib_mu_dep, lib_test_mu_common_dep]))
|
||||
|
||||
test('test_parser',
|
||||
executable('test-parser',
|
||||
'test-parser.cc',
|
||||
|
||||
Reference in New Issue
Block a user