mu: add "modified" fields

Add a new "modified" field for checking the last modification time of the
message.
This commit is contained in:
Dirk-Jan C. Binnema 2022-04-30 01:17:31 +03:00
parent 9e0173f387
commit a864616110
4 changed files with 185 additions and 178 deletions

View File

@ -39,39 +39,28 @@ struct Field {
*
*/
enum struct Id {
/*
* first all the string-based ones
*/
Bcc = 0, /**< Blind Carbon-Copy */
BodyHtml, /**< HTML Body */
BodyText, /**< Text body */
Cc, /**< Carbon-Copy */
EmbeddedText, /**< Embedded text in message */
File, /**< Filename */
From, /**< Message sender */
Maildir, /**< Maildir path */
Mime, /**< MIME-Type */
MessageId, /**< Message Id */
Path, /**< File-system Path */
Subject, /**< Message subject */
To, /**< To: recipient */
/*
* string list items...
*/
References, /**< All references (incl. Reply-To:) */
Tags, /**< Message Tags */
/*
* then the numerical ones
*/
Date, /**< Message date */
Flags, /**< Message flags */
Priority, /**< Message priority */
Size, /**< Message size (in bytes) */
/* add new ones here... */
MailingList, /**< Mailing list */
ThreadId, /**< Thread Id */
Bcc = 0, /**< Blind Carbon-Copy */
BodyHtml, /**< HTML Body */
BodyText, /**< Text body */
Cc, /**< Carbon-Copy */
Date, /**< Message date */
EmbeddedText, /**< Embedded text in message */
File, /**< Filename */
Flags, /**< Message flags */
From, /**< Message sender */
Maildir, /**< Maildir path */
MailingList, /**< Mailing list */
MessageId, /**< Message Id */
Mime, /**< MIME-Type */
Modified, /**< Last modification time */
Path, /**< File-system Path */
Priority, /**< Message priority */
References, /**< All references (incl. Reply-To:) */
Size, /**< Message size (in bytes) */
Subject, /**< Message subject */
Tags, /**< Message Tags */
ThreadId, /**< Thread Id */
To, /**< To: recipient */
/*
* <private>
*/
@ -146,10 +135,9 @@ struct Field {
Value = 1 << 11,
/**< Field value is stored (so the literal value can be retrieved) */
DoNotCache = 1 << 20,
/**< don't cache this field in * the MuMsg cache */
Range = 1 << 21
Range = 1 << 21,
/**< whether this is a range field (e.g., date, size)*/
Internal = 1 << 26
};
constexpr bool any_of(Flag some_flag) const{
@ -164,6 +152,7 @@ struct Field {
is_normal_term(); }
constexpr bool is_value() const { return any_of(Flag::Value); }
constexpr bool is_internal() const { return any_of(Flag::Internal); }
constexpr bool is_contact() const { return any_of(Flag::Contact); }
constexpr bool is_range() const { return any_of(Flag::Range); }
@ -215,7 +204,6 @@ MU_ENABLE_BITOPS(Field::Flag);
static constexpr std::array<Field, Field::id_size()>
Fields = {
{
// Bcc
{
Field::Id::Bcc,
Field::Type::ContactList,
@ -226,7 +214,6 @@ static constexpr std::array<Field, Field::id_size()>
Field::Flag::Contact |
Field::Flag::Value
},
// HTML Body
{
Field::Id::BodyHtml,
Field::Type::String,
@ -234,19 +221,17 @@ static constexpr std::array<Field, Field::id_size()>
"Message html body",
{},
{},
{}
Field::Flag::Internal
},
// Body
{
Field::Id::BodyText,
Field::Type::String,
"body",
"Message plain-text body",
"body:capybara", // example
"body:capybara",
'b',
Field::Flag::IndexableTerm,
},
// Cc
{
Field::Id::Cc,
Field::Type::ContactList,
@ -257,124 +242,6 @@ static constexpr std::array<Field, Field::id_size()>
Field::Flag::Contact |
Field::Flag::Value
},
// Embed
{
Field::Id::EmbeddedText,
Field::Type::String,
"embed",
"Embedded text",
"embed:war OR embed:peace",
'e',
Field::Flag::IndexableTerm
},
// File
{
Field::Id::File,
Field::Type::String,
"file",
"Attachment file name",
"file:/image\\.*.jpg/",
'j',
Field::Flag::NormalTerm
},
// From
{
Field::Id::From,
Field::Type::ContactList,
"from",
"Message sender",
"from:jimbo",
'f',
Field::Flag::Contact |
Field::Flag::Value
},
// Maildir
{
Field::Id::Maildir,
Field::Type::String,
"maildir",
"Maildir path for message",
"maildir:/private/archive",
'm',
Field::Flag::BooleanTerm |
Field::Flag::Value
},
// MIME
{
Field::Id::Mime,
Field::Type::String,
"mime",
"Attachment MIME-type",
"mime:image/jpeg",
'y',
Field::Flag::NormalTerm
},
// Message-ID
{
Field::Id::MessageId,
Field::Type::String,
"msgid",
"Attachment MIME-type",
"msgid:abc@123",
'i',
Field::Flag::BooleanTerm |
Field::Flag::Value
},
// Path
{
Field::Id::Path,
Field::Type::String,
"path",
"File system path to message",
{},
'l',
Field::Flag::BooleanTerm |
Field::Flag::Value
},
// Subject
{
Field::Id::Subject,
Field::Type::String,
"subject",
"Message subject",
"subject:wombat",
's',
Field::Flag::Value |
Field::Flag::IndexableTerm
},
// To
{
Field::Id::To,
Field::Type::ContactList,
"to",
"Message recipient",
"to:flimflam@example.com",
't',
Field::Flag::Contact |
Field::Flag::Value
},
// References
{
Field::Id::References,
Field::Type::StringList,
"refs",
"Message references to other messages",
{},
'r',
Field::Flag::Value
},
// Tags
{
Field::Id::Tags,
Field::Type::StringList,
"tag",
"Message tags",
"tag:projectx",
'x',
Field::Flag::NormalTerm |
Field::Flag::Value
},
// Date
{
Field::Id::Date,
Field::Type::TimeT,
@ -385,7 +252,24 @@ static constexpr std::array<Field, Field::id_size()>
Field::Flag::Value |
Field::Flag::Range
},
// Flags
{
Field::Id::EmbeddedText,
Field::Type::String,
"embed",
"Embedded text",
"embed:war OR embed:peace",
'e',
Field::Flag::IndexableTerm
},
{
Field::Id::File,
Field::Type::String,
"file",
"Attachment file name",
"file:/image\\.*.jpg/",
'j',
Field::Flag::NormalTerm
},
{
Field::Id::Flags,
Field::Type::Integer,
@ -396,7 +280,75 @@ static constexpr std::array<Field, Field::id_size()>
Field::Flag::NormalTerm |
Field::Flag::Value
},
// Priority
{
Field::Id::From,
Field::Type::ContactList,
"from",
"Message sender",
"from:jimbo",
'f',
Field::Flag::Contact |
Field::Flag::Value
},
{
Field::Id::Maildir,
Field::Type::String,
"maildir",
"Maildir path for message",
"maildir:/private/archive",
'm',
Field::Flag::BooleanTerm |
Field::Flag::Value
},
{
Field::Id::MailingList,
Field::Type::String,
"list",
"Mailing list (List-Id:)",
"list:mu-discuss.example.com",
'v',
Field::Flag::BooleanTerm |
Field::Flag::Value
},
{
Field::Id::MessageId,
Field::Type::String,
"msgid",
"Attachment MIME-type",
"msgid:abc@123",
'i',
Field::Flag::BooleanTerm |
Field::Flag::Value
},
{
Field::Id::Mime,
Field::Type::String,
"mime",
"Attachment MIME-type",
"mime:image/jpeg",
'y',
Field::Flag::NormalTerm
},
{
Field::Id::Modified,
Field::Type::TimeT,
"modified",
"Last modification time",
"modified:30m..now",
'k',
Field::Flag::Value |
Field::Flag::Range
},
{
Field::Id::Path,
Field::Type::String,
"path",
"File system path to message",
"path:/a/b/Maildir/cur/msg:2,S",
'l',
Field::Flag::BooleanTerm |
Field::Flag::Value
},
{
Field::Id::Priority,
Field::Type::Integer,
@ -407,7 +359,15 @@ static constexpr std::array<Field, Field::id_size()>
Field::Flag::BooleanTerm |
Field::Flag::Value
},
// Size
{
Field::Id::References,
Field::Type::StringList,
"refs",
"References to related messages",
{},
'r',
Field::Flag::Value
},
{
Field::Id::Size,
Field::Type::ByteSize,
@ -418,18 +378,26 @@ static constexpr std::array<Field, Field::id_size()>
Field::Flag::Value |
Field::Flag::Range
},
// Mailing List
{
Field::Id::MailingList,
Field::Id::Subject,
Field::Type::String,
"list",
"Mailing list (List-Id:)",
"list:mu-discuss.googlegroups.com",
'v',
Field::Flag::BooleanTerm |
"subject",
"Message subject",
"subject:wombat",
's',
Field::Flag::Value |
Field::Flag::IndexableTerm
},
{
Field::Id::Tags,
Field::Type::StringList,
"tag",
"Message tags",
"tag:projectx",
'x',
Field::Flag::NormalTerm |
Field::Flag::Value
},
// ThreadId
{
Field::Id::ThreadId,
Field::Type::String,
@ -440,6 +408,16 @@ static constexpr std::array<Field, Field::id_size()>
Field::Flag::BooleanTerm |
Field::Flag::Value
},
{
Field::Id::To,
Field::Type::ContactList,
"to",
"Message recipient",
"to:flimflam@example.com",
't',
Field::Flag::Contact |
Field::Flag::Value
},
}};
/*

View File

@ -596,6 +596,9 @@ fill_document(Message::Private& priv)
for (auto&& part: priv.parts)
doc.add(field.id, part.mime_type());
break;
case Field::Id::Modified:
doc.add(field.id, priv.mtime);
break;
case Field::Id::Path: /* already */
break;
case Field::Id::Priority:
@ -708,8 +711,10 @@ Message::update_after_move(const std::string& new_path,
return Err(statbuf.error());
priv_->doc.add(Field::Id::Path, new_path);
priv_->doc.add(Field::Id::Modified, statbuf->st_mtime);
priv_->doc.add(new_flags);
if (const auto res = set_maildir(new_maildir); !res)
return res;

View File

@ -84,18 +84,29 @@ public:
/**
* Construct a message based on a Message::Document
*
* @param doc
* @param doc a Mu Document
*
* @return a message or an error
*/
static Result<Message> make_from_document(Xapian::Document&& doc) try {
return Ok(Message{Mu::Document{std::move(doc)}});
static Result<Message> make_from_document(Mu::Document&& doc) try {
return Ok(Message{std::move(doc)});
} catch (Error& err) {
return Err(err);
} catch (...) {
return Err(Mu::Error(Error::Code::Message, "failed to create message"));
}
/**
* Construct a message based on a Xapian::Document
*
* @param doc a xapian document
*
* @return a message or an error
*/
static Result<Message> make_from_document(Xapian::Document&& doc) noexcept {
return make_from_document(Mu::Document{std::move(doc)});
}
/**
* Construct a message from a string. This is mostly useful for testing.
@ -216,12 +227,24 @@ public:
std::string mailing_list() const { return document().string_value(Field::Id::MailingList);}
/**
* get the message date/time (the Date: field) as time_t, using UTC
* get the message date/time (the Date: field) as time_t
*
* @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>(document().integer_value(Field::Id::Date)); }
::time_t date() const {
return static_cast<::time_t>(document().integer_value(Field::Id::Date));
}
/**
* get the last modification or creation time for this message
*
* @return message date/time or 0 if unknown
*/
::time_t modified() const {
return static_cast<::time_t>(document().integer_value(Field::Id::Modified));
}
/**
* get the flags for this message.

View File

@ -177,7 +177,7 @@ process_range(const std::string& field_str,
std::string u2 = upper;
constexpr auto upper_limit = std::numeric_limits<int64_t>::max();
if (field_opt->id == Field::Id::Date) {
if (field_opt->id == Field::Id::Date || field_opt->id == Field::Id::Modified) {
l2 = to_lexnum(parse_date_time(lower, true).value_or(0));
u2 = to_lexnum(parse_date_time(upper, false).value_or(upper_limit));
} else if (field_opt->id == Field::Id::Size) {
@ -420,7 +420,8 @@ Parser::Private::factor_2(Mu::Tokens& tokens, Node::Type& op, WarningVec& warnin
op = Node::Type::OpAnd; // implicit AND
break;
default: return empty();
default:
return empty();
}
#pragma GCC diagnostic pop