mirror of https://github.com/djcb/mu.git
mu: add "modified" fields
Add a new "modified" field for checking the last modification time of the message.
This commit is contained in:
parent
9e0173f387
commit
a864616110
|
@ -39,39 +39,28 @@ struct Field {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
enum struct Id {
|
enum struct Id {
|
||||||
/*
|
Bcc = 0, /**< Blind Carbon-Copy */
|
||||||
* first all the string-based ones
|
BodyHtml, /**< HTML Body */
|
||||||
*/
|
BodyText, /**< Text body */
|
||||||
Bcc = 0, /**< Blind Carbon-Copy */
|
Cc, /**< Carbon-Copy */
|
||||||
BodyHtml, /**< HTML Body */
|
Date, /**< Message date */
|
||||||
BodyText, /**< Text body */
|
EmbeddedText, /**< Embedded text in message */
|
||||||
Cc, /**< Carbon-Copy */
|
File, /**< Filename */
|
||||||
EmbeddedText, /**< Embedded text in message */
|
Flags, /**< Message flags */
|
||||||
File, /**< Filename */
|
From, /**< Message sender */
|
||||||
From, /**< Message sender */
|
Maildir, /**< Maildir path */
|
||||||
Maildir, /**< Maildir path */
|
MailingList, /**< Mailing list */
|
||||||
Mime, /**< MIME-Type */
|
MessageId, /**< Message Id */
|
||||||
MessageId, /**< Message Id */
|
Mime, /**< MIME-Type */
|
||||||
Path, /**< File-system Path */
|
Modified, /**< Last modification time */
|
||||||
Subject, /**< Message subject */
|
Path, /**< File-system Path */
|
||||||
To, /**< To: recipient */
|
Priority, /**< Message priority */
|
||||||
/*
|
References, /**< All references (incl. Reply-To:) */
|
||||||
* string list items...
|
Size, /**< Message size (in bytes) */
|
||||||
*/
|
Subject, /**< Message subject */
|
||||||
References, /**< All references (incl. Reply-To:) */
|
Tags, /**< Message Tags */
|
||||||
Tags, /**< Message Tags */
|
ThreadId, /**< Thread Id */
|
||||||
/*
|
To, /**< To: recipient */
|
||||||
* 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 */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* <private>
|
* <private>
|
||||||
*/
|
*/
|
||||||
|
@ -146,10 +135,9 @@ struct Field {
|
||||||
Value = 1 << 11,
|
Value = 1 << 11,
|
||||||
/**< Field value is stored (so the literal value can be retrieved) */
|
/**< Field value is stored (so the literal value can be retrieved) */
|
||||||
|
|
||||||
DoNotCache = 1 << 20,
|
Range = 1 << 21,
|
||||||
/**< don't cache this field in * the MuMsg cache */
|
|
||||||
Range = 1 << 21
|
|
||||||
/**< whether this is a range field (e.g., date, size)*/
|
/**< whether this is a range field (e.g., date, size)*/
|
||||||
|
Internal = 1 << 26
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr bool any_of(Flag some_flag) const{
|
constexpr bool any_of(Flag some_flag) const{
|
||||||
|
@ -164,6 +152,7 @@ struct Field {
|
||||||
is_normal_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_internal() const { return any_of(Flag::Internal); }
|
||||||
|
|
||||||
constexpr bool is_contact() const { return any_of(Flag::Contact); }
|
constexpr bool is_contact() const { return any_of(Flag::Contact); }
|
||||||
constexpr bool is_range() const { return any_of(Flag::Range); }
|
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()>
|
static constexpr std::array<Field, Field::id_size()>
|
||||||
Fields = {
|
Fields = {
|
||||||
{
|
{
|
||||||
// Bcc
|
|
||||||
{
|
{
|
||||||
Field::Id::Bcc,
|
Field::Id::Bcc,
|
||||||
Field::Type::ContactList,
|
Field::Type::ContactList,
|
||||||
|
@ -226,7 +214,6 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Flag::Contact |
|
Field::Flag::Contact |
|
||||||
Field::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
// HTML Body
|
|
||||||
{
|
{
|
||||||
Field::Id::BodyHtml,
|
Field::Id::BodyHtml,
|
||||||
Field::Type::String,
|
Field::Type::String,
|
||||||
|
@ -234,19 +221,17 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
"Message html body",
|
"Message html body",
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{}
|
Field::Flag::Internal
|
||||||
},
|
},
|
||||||
// Body
|
|
||||||
{
|
{
|
||||||
Field::Id::BodyText,
|
Field::Id::BodyText,
|
||||||
Field::Type::String,
|
Field::Type::String,
|
||||||
"body",
|
"body",
|
||||||
"Message plain-text body",
|
"Message plain-text body",
|
||||||
"body:capybara", // example
|
"body:capybara",
|
||||||
'b',
|
'b',
|
||||||
Field::Flag::IndexableTerm,
|
Field::Flag::IndexableTerm,
|
||||||
},
|
},
|
||||||
// Cc
|
|
||||||
{
|
{
|
||||||
Field::Id::Cc,
|
Field::Id::Cc,
|
||||||
Field::Type::ContactList,
|
Field::Type::ContactList,
|
||||||
|
@ -257,124 +242,6 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Flag::Contact |
|
Field::Flag::Contact |
|
||||||
Field::Flag::Value
|
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::Id::Date,
|
||||||
Field::Type::TimeT,
|
Field::Type::TimeT,
|
||||||
|
@ -385,7 +252,24 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Flag::Value |
|
Field::Flag::Value |
|
||||||
Field::Flag::Range
|
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::Id::Flags,
|
||||||
Field::Type::Integer,
|
Field::Type::Integer,
|
||||||
|
@ -396,7 +280,75 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Flag::NormalTerm |
|
Field::Flag::NormalTerm |
|
||||||
Field::Flag::Value
|
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::Id::Priority,
|
||||||
Field::Type::Integer,
|
Field::Type::Integer,
|
||||||
|
@ -407,7 +359,15 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Flag::BooleanTerm |
|
Field::Flag::BooleanTerm |
|
||||||
Field::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
// Size
|
{
|
||||||
|
Field::Id::References,
|
||||||
|
Field::Type::StringList,
|
||||||
|
"refs",
|
||||||
|
"References to related messages",
|
||||||
|
{},
|
||||||
|
'r',
|
||||||
|
Field::Flag::Value
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Field::Id::Size,
|
Field::Id::Size,
|
||||||
Field::Type::ByteSize,
|
Field::Type::ByteSize,
|
||||||
|
@ -418,18 +378,26 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Flag::Value |
|
Field::Flag::Value |
|
||||||
Field::Flag::Range
|
Field::Flag::Range
|
||||||
},
|
},
|
||||||
// Mailing List
|
|
||||||
{
|
{
|
||||||
Field::Id::MailingList,
|
Field::Id::Subject,
|
||||||
Field::Type::String,
|
Field::Type::String,
|
||||||
"list",
|
"subject",
|
||||||
"Mailing list (List-Id:)",
|
"Message subject",
|
||||||
"list:mu-discuss.googlegroups.com",
|
"subject:wombat",
|
||||||
'v',
|
's',
|
||||||
Field::Flag::BooleanTerm |
|
Field::Flag::Value |
|
||||||
|
Field::Flag::IndexableTerm
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Field::Id::Tags,
|
||||||
|
Field::Type::StringList,
|
||||||
|
"tag",
|
||||||
|
"Message tags",
|
||||||
|
"tag:projectx",
|
||||||
|
'x',
|
||||||
|
Field::Flag::NormalTerm |
|
||||||
Field::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
// ThreadId
|
|
||||||
{
|
{
|
||||||
Field::Id::ThreadId,
|
Field::Id::ThreadId,
|
||||||
Field::Type::String,
|
Field::Type::String,
|
||||||
|
@ -440,6 +408,16 @@ static constexpr std::array<Field, Field::id_size()>
|
||||||
Field::Flag::BooleanTerm |
|
Field::Flag::BooleanTerm |
|
||||||
Field::Flag::Value
|
Field::Flag::Value
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Field::Id::To,
|
||||||
|
Field::Type::ContactList,
|
||||||
|
"to",
|
||||||
|
"Message recipient",
|
||||||
|
"to:flimflam@example.com",
|
||||||
|
't',
|
||||||
|
Field::Flag::Contact |
|
||||||
|
Field::Flag::Value
|
||||||
|
},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -596,6 +596,9 @@ fill_document(Message::Private& priv)
|
||||||
for (auto&& part: priv.parts)
|
for (auto&& part: priv.parts)
|
||||||
doc.add(field.id, part.mime_type());
|
doc.add(field.id, part.mime_type());
|
||||||
break;
|
break;
|
||||||
|
case Field::Id::Modified:
|
||||||
|
doc.add(field.id, priv.mtime);
|
||||||
|
break;
|
||||||
case Field::Id::Path: /* already */
|
case Field::Id::Path: /* already */
|
||||||
break;
|
break;
|
||||||
case Field::Id::Priority:
|
case Field::Id::Priority:
|
||||||
|
@ -708,8 +711,10 @@ Message::update_after_move(const std::string& new_path,
|
||||||
return Err(statbuf.error());
|
return Err(statbuf.error());
|
||||||
|
|
||||||
priv_->doc.add(Field::Id::Path, new_path);
|
priv_->doc.add(Field::Id::Path, new_path);
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
|
@ -84,18 +84,29 @@ public:
|
||||||
/**
|
/**
|
||||||
* Construct a message based on a Message::Document
|
* Construct a message based on a Message::Document
|
||||||
*
|
*
|
||||||
* @param doc
|
* @param doc a Mu Document
|
||||||
*
|
*
|
||||||
* @return a message or an error
|
* @return a message or an error
|
||||||
*/
|
*/
|
||||||
static Result<Message> make_from_document(Xapian::Document&& doc) try {
|
static Result<Message> make_from_document(Mu::Document&& doc) try {
|
||||||
return Ok(Message{Mu::Document{std::move(doc)}});
|
return Ok(Message{std::move(doc)});
|
||||||
} catch (Error& err) {
|
} catch (Error& err) {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return Err(Mu::Error(Error::Code::Message, "failed to create message"));
|
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.
|
* 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);}
|
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
|
* @return message date/time or 0 in case of error or if there
|
||||||
* is no such header.
|
* 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.
|
* get the flags for this message.
|
||||||
|
|
|
@ -177,7 +177,7 @@ process_range(const std::string& field_str,
|
||||||
std::string u2 = upper;
|
std::string u2 = upper;
|
||||||
constexpr auto upper_limit = std::numeric_limits<int64_t>::max();
|
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));
|
l2 = to_lexnum(parse_date_time(lower, true).value_or(0));
|
||||||
u2 = to_lexnum(parse_date_time(upper, false).value_or(upper_limit));
|
u2 = to_lexnum(parse_date_time(upper, false).value_or(upper_limit));
|
||||||
} else if (field_opt->id == Field::Id::Size) {
|
} 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
|
op = Node::Type::OpAnd; // implicit AND
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: return empty();
|
default:
|
||||||
|
return empty();
|
||||||
}
|
}
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue