mirror of https://github.com/djcb/mu.git
message/field: cache the message's sexp
Keep it in the store; much faster than generating on the fly.
This commit is contained in:
parent
263e122a13
commit
fea596ae3b
|
@ -142,7 +142,7 @@ static void
|
||||||
test_prefix()
|
test_prefix()
|
||||||
{
|
{
|
||||||
static_assert(field_from_id(Field::Id::Subject).xapian_prefix() == 'S');
|
static_assert(field_from_id(Field::Id::Subject).xapian_prefix() == 'S');
|
||||||
static_assert(field_from_id(Field::Id::BodyHtml).xapian_prefix() == 0);
|
static_assert(field_from_id(Field::Id::XBodyHtml).xapian_prefix() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
|
|
|
@ -40,7 +40,6 @@ struct Field {
|
||||||
*/
|
*/
|
||||||
enum struct Id {
|
enum struct Id {
|
||||||
Bcc = 0, /**< Blind Carbon-Copy */
|
Bcc = 0, /**< Blind Carbon-Copy */
|
||||||
BodyHtml, /**< HTML Body */
|
|
||||||
BodyText, /**< Text body */
|
BodyText, /**< Text body */
|
||||||
Cc, /**< Carbon-Copy */
|
Cc, /**< Carbon-Copy */
|
||||||
Date, /**< Message date */
|
Date, /**< Message date */
|
||||||
|
@ -64,6 +63,9 @@ struct Field {
|
||||||
/*
|
/*
|
||||||
* <private>
|
* <private>
|
||||||
*/
|
*/
|
||||||
|
XBodyHtml, /**< HTML Body */
|
||||||
|
XCachedSexp, /**< Cached message s-expression */
|
||||||
|
|
||||||
_count_ /**< Number of FieldIds */
|
_count_ /**< Number of FieldIds */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -214,15 +216,6 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Flag::Contact |
|
Field::Flag::Contact |
|
||||||
Field::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Field::Id::BodyHtml,
|
|
||||||
Field::Type::String,
|
|
||||||
"body",
|
|
||||||
"Message html body",
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
Field::Flag::Internal
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Field::Id::BodyText,
|
Field::Id::BodyText,
|
||||||
Field::Type::String,
|
Field::Type::String,
|
||||||
|
@ -314,7 +307,7 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Id::MessageId,
|
Field::Id::MessageId,
|
||||||
Field::Type::String,
|
Field::Type::String,
|
||||||
"msgid",
|
"msgid",
|
||||||
"Attachment MIME-type",
|
"Message-Id",
|
||||||
"msgid:abc@123",
|
"msgid:abc@123",
|
||||||
'i',
|
'i',
|
||||||
Field::Flag::BooleanTerm |
|
Field::Flag::BooleanTerm |
|
||||||
|
@ -418,6 +411,30 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Flag::Contact |
|
Field::Flag::Contact |
|
||||||
Field::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* internal */
|
||||||
|
{
|
||||||
|
Field::Id::XBodyHtml,
|
||||||
|
Field::Type::String,
|
||||||
|
"htmlbody",
|
||||||
|
"Message html body",
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
Field::Flag::Internal
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Field::Id::XCachedSexp,
|
||||||
|
Field::Type::String,
|
||||||
|
"sexp",
|
||||||
|
"Cached message s-expression",
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
Field::Flag::Internal |
|
||||||
|
Field::Flag::Value
|
||||||
|
},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -140,13 +140,10 @@ add_date_and_size(Sexp::List& items, const Message& message)
|
||||||
}
|
}
|
||||||
|
|
||||||
Mu::Sexp::List
|
Mu::Sexp::List
|
||||||
Message::to_sexp_list(unsigned docid) const
|
Message::to_sexp_list() const
|
||||||
{
|
{
|
||||||
Sexp::List items;
|
Sexp::List items;
|
||||||
|
|
||||||
if (docid != 0)
|
|
||||||
items.add_prop(":docid", Sexp::make_number(docid));
|
|
||||||
|
|
||||||
add_prop_nonempty(items, ":subject", subject());
|
add_prop_nonempty(items, ":subject", subject());
|
||||||
add_prop_nonempty(items, ":message-id", message_id());
|
add_prop_nonempty(items, ":message-id", message_id());
|
||||||
add_prop_nonempty(items, ":mailing-list", mailing_list());
|
add_prop_nonempty(items, ":mailing-list", mailing_list());
|
||||||
|
@ -169,7 +166,7 @@ Message::to_sexp_list(unsigned docid) const
|
||||||
}
|
}
|
||||||
|
|
||||||
Mu::Sexp
|
Mu::Sexp
|
||||||
Message::to_sexp(unsigned docid) const
|
Message::to_sexp() const
|
||||||
{
|
{
|
||||||
return Sexp::make_list(to_sexp_list());
|
return Sexp::make_list(to_sexp_list());
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,9 @@ Message::Message(const std::string& text, const std::string& path,
|
||||||
priv_->mime_msg = std::move(msg.value());
|
priv_->mime_msg = std::move(msg.value());
|
||||||
|
|
||||||
fill_document(*priv_);
|
fill_document(*priv_);
|
||||||
|
|
||||||
|
/* cache the sexp */
|
||||||
|
priv_->doc.add(Field::Id::XCachedSexp, to_sexp().to_sexp_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -166,6 +169,13 @@ Message::document() const
|
||||||
return priv_->doc;
|
return priv_->doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Message::update_cached_sexp()
|
||||||
|
{
|
||||||
|
priv_->doc.add(Field::Id::XCachedSexp, to_sexp().to_sexp_string());
|
||||||
|
}
|
||||||
|
|
||||||
Result<void>
|
Result<void>
|
||||||
Message::set_maildir(const std::string& maildir)
|
Message::set_maildir(const std::string& maildir)
|
||||||
{
|
{
|
||||||
|
@ -175,7 +185,7 @@ Message::set_maildir(const std::string& maildir)
|
||||||
maildir.at(0) != '/' ||
|
maildir.at(0) != '/' ||
|
||||||
(maildir.size() > 1 && maildir.at(maildir.length()-1) == '/'))
|
(maildir.size() > 1 && maildir.at(maildir.length()-1) == '/'))
|
||||||
return Err(Error::Code::Message,
|
return Err(Error::Code::Message,
|
||||||
"'%s' is not a valid maildir", maildir.c_str());
|
"'%s' is not a valid maildir", maildir.c_str());
|
||||||
|
|
||||||
const auto path{document().string_value(Field::Id::Path)};
|
const auto path{document().string_value(Field::Id::Path)};
|
||||||
if (path == maildir || path.find(maildir) == std::string::npos)
|
if (path == maildir || path.find(maildir) == std::string::npos)
|
||||||
|
@ -554,19 +564,15 @@ fill_document(Message::Private& priv)
|
||||||
/* insist on expliclity handling each */
|
/* insist on expliclity handling each */
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic error "-Wswitch"
|
#pragma GCC diagnostic error "-Wswitch"
|
||||||
using AddrType = MimeMessage::AddressType;
|
|
||||||
switch(field.id) {
|
switch(field.id) {
|
||||||
case Field::Id::Bcc:
|
case Field::Id::Bcc:
|
||||||
doc.add(field.id, mime_msg.addresses(AddrType::Bcc));
|
doc.add(field.id, mime_msg.contacts(Contact::Type::Bcc));
|
||||||
break;
|
|
||||||
case Field::Id::BodyHtml:
|
|
||||||
doc.add(field.id, priv.body_html);
|
|
||||||
break;
|
break;
|
||||||
case Field::Id::BodyText:
|
case Field::Id::BodyText:
|
||||||
doc.add(field.id, priv.body_txt);
|
doc.add(field.id, priv.body_txt);
|
||||||
break;
|
break;
|
||||||
case Field::Id::Cc:
|
case Field::Id::Cc:
|
||||||
doc.add(field.id, mime_msg.addresses(AddrType::Cc));
|
doc.add(field.id, mime_msg.contacts(Contact::Type::Cc));
|
||||||
break;
|
break;
|
||||||
case Field::Id::Date:
|
case Field::Id::Date:
|
||||||
doc.add(field.id, mime_msg.date());
|
doc.add(field.id, mime_msg.date());
|
||||||
|
@ -582,7 +588,7 @@ fill_document(Message::Private& priv)
|
||||||
doc.add(priv.flags);
|
doc.add(priv.flags);
|
||||||
break;
|
break;
|
||||||
case Field::Id::From:
|
case Field::Id::From:
|
||||||
doc.add(field.id, mime_msg.addresses(AddrType::From));
|
doc.add(field.id, mime_msg.contacts(Contact::Type::From));
|
||||||
break;
|
break;
|
||||||
case Field::Id::Maildir: /* already */
|
case Field::Id::Maildir: /* already */
|
||||||
break;
|
break;
|
||||||
|
@ -622,8 +628,16 @@ fill_document(Message::Private& priv)
|
||||||
doc.add(field.id, refs.empty() ? message_id : refs.at(0));
|
doc.add(field.id, refs.empty() ? message_id : refs.at(0));
|
||||||
break;
|
break;
|
||||||
case Field::Id::To:
|
case Field::Id::To:
|
||||||
doc.add(field.id, mime_msg.addresses(AddrType::To));
|
doc.add(field.id, mime_msg.contacts(Contact::Type::To));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* internal fields */
|
||||||
|
case Field::Id::XBodyHtml:
|
||||||
|
doc.add(field.id, priv.body_html);
|
||||||
|
break;
|
||||||
|
case Field::Id::XCachedSexp:
|
||||||
|
break;
|
||||||
|
/* ignore */
|
||||||
case Field::Id::_count_:
|
case Field::Id::_count_:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -667,18 +681,7 @@ Message::all_contacts() const
|
||||||
if (!load_mime_message())
|
if (!load_mime_message())
|
||||||
return contacts; /* empty */
|
return contacts; /* empty */
|
||||||
|
|
||||||
for (auto&& ctype: {
|
return priv_->mime_msg->contacts(Contact::Type::None); /* get all types */
|
||||||
MimeMessage::AddressType::Sender,
|
|
||||||
MimeMessage::AddressType::From,
|
|
||||||
MimeMessage::AddressType::ReplyTo,
|
|
||||||
MimeMessage::AddressType::To,
|
|
||||||
MimeMessage::AddressType::Cc,
|
|
||||||
MimeMessage::AddressType::Bcc}) {
|
|
||||||
auto addrs{priv_->mime_msg->addresses(ctype)};
|
|
||||||
std::move(addrs.begin(), addrs.end(), std::back_inserter(contacts));
|
|
||||||
}
|
|
||||||
|
|
||||||
return contacts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<Message::Part>&
|
const std::vector<Message::Part>&
|
||||||
|
@ -714,9 +717,10 @@ Message::update_after_move(const std::string& new_path,
|
||||||
priv_->doc.add(Field::Id::Modified, statbuf->st_mtime);
|
priv_->doc.add(Field::Id::Modified, statbuf->st_mtime);
|
||||||
priv_->doc.add(new_flags);
|
priv_->doc.add(new_flags);
|
||||||
|
|
||||||
|
|
||||||
if (const auto res = set_maildir(new_maildir); !res)
|
if (const auto res = set_maildir(new_maildir); !res)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
priv_->doc.add(Field::Id::XCachedSexp, to_sexp().to_sexp_string());
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,12 +177,11 @@ public:
|
||||||
Contacts bcc() const { return document().contacts_value(Field::Id::Bcc); }
|
Contacts bcc() const { return document().contacts_value(Field::Id::Bcc); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the maildir this message lives in; i.e., if the path is
|
* Get the maildir this message resides in; i.e., if the path is
|
||||||
* ~/Maildir/foo/bar/cur/msg, the maildir would typically be foo/bar
|
* ~/Maildir/foo/bar/cur/msg, the maildir would typically be foo/bar
|
||||||
*
|
*
|
||||||
* Note that that only messages that live in the store (i.e., are
|
* This is determined when _storing_ the message (which uses
|
||||||
* constructed use make_from_document() have a non-empty value for
|
* set_maildir())
|
||||||
* this.) until set_maildir() is used.
|
|
||||||
*
|
*
|
||||||
* @return the maildir requested or empty */
|
* @return the maildir requested or empty */
|
||||||
std::string maildir() const { return document().string_value(Field::Id::Maildir); }
|
std::string maildir() const { return document().string_value(Field::Id::Maildir); }
|
||||||
|
@ -245,7 +244,6 @@ public:
|
||||||
return static_cast<::time_t>(document().integer_value(Field::Id::Modified));
|
return static_cast<::time_t>(document().integer_value(Field::Id::Modified));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the flags for this message.
|
* get the flags for this message.
|
||||||
*
|
*
|
||||||
|
@ -291,6 +289,16 @@ public:
|
||||||
std::vector<std::string> tags() const {
|
std::vector<std::string> tags() const {
|
||||||
return document().string_vec_value(Field::Id::Tags);
|
return document().string_vec_value(Field::Id::Tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cached s-expression for this message, or {} if not available.
|
||||||
|
*
|
||||||
|
* @return sexp or empty.
|
||||||
|
*/
|
||||||
|
std::string cached_sexp() const {
|
||||||
|
return document().string_value(Field::Id::XCachedSexp);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert to Sexp
|
* Convert to Sexp
|
||||||
*/
|
*/
|
||||||
|
@ -299,11 +307,19 @@ public:
|
||||||
* convert the message to a Lisp symbolic expression (for further
|
* convert the message to a Lisp symbolic expression (for further
|
||||||
* processing in e.g. emacs)
|
* processing in e.g. emacs)
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @return a Mu::Sexp or a Mu::Sexp::List representing the message.
|
* @return a Mu::Sexp or a Mu::Sexp::List representing the message.
|
||||||
*/
|
*/
|
||||||
Mu::Sexp::List to_sexp_list(unsigned docid=0) const;
|
Mu::Sexp::List to_sexp_list() const;
|
||||||
Mu::Sexp to_sexp(unsigned docid=0) const;
|
Mu::Sexp to_sexp() const;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the cached sexp for this message which is stored in the
|
||||||
|
* document.This should be done when the document is complete,
|
||||||
|
* i.e., immediately before storing it in the database.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void update_cached_sexp();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* And some non-const message, for updating an existing
|
* And some non-const message, for updating an existing
|
||||||
|
|
|
@ -344,6 +344,9 @@ Store::add_message(Message& msg, bool use_transaction)
|
||||||
|
|
||||||
if (auto&& res = msg.set_maildir(mdir.value()); !res)
|
if (auto&& res = msg.set_maildir(mdir.value()); !res)
|
||||||
return Err(res.error());
|
return Err(res.error());
|
||||||
|
/* now, we're done with all the fields; generate the sexp string for this
|
||||||
|
* message */
|
||||||
|
msg.update_cached_sexp();
|
||||||
|
|
||||||
std::lock_guard guard{priv_->lock_};
|
std::lock_guard guard{priv_->lock_};
|
||||||
|
|
||||||
|
@ -374,8 +377,12 @@ Store::add_message(Message& msg, bool use_transaction)
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Store::update_message(const Message& msg, unsigned docid)
|
Store::update_message(Message& msg, unsigned docid)
|
||||||
{
|
{
|
||||||
|
msg.update_cached_sexp();
|
||||||
|
|
||||||
|
std::lock_guard guard{priv_->lock_};
|
||||||
|
|
||||||
return xapian_try(
|
return xapian_try(
|
||||||
[&]{
|
[&]{
|
||||||
priv_->writable_db().replace_document(
|
priv_->writable_db().replace_document(
|
||||||
|
|
|
@ -207,7 +207,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return false in case of failure; true otherwise.
|
* @return false in case of failure; true otherwise.
|
||||||
*/
|
*/
|
||||||
bool update_message(const Message& msg, Id id);
|
bool update_message(Message& msg, Id id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a message from the store. It will _not_ remove the message
|
* Remove a message from the store. It will _not_ remove the message
|
||||||
|
|
Loading…
Reference in New Issue