From 8f9d1e5e60daf2817cb6421c063dfbb2570708fd Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sat, 30 Apr 2022 01:10:31 +0300 Subject: [PATCH] mu: add fields/flags commands Useful information for devising queries. Directly generated from the source. Add manpages, too. --- lib/message/mu-flags.hh | 63 ++++++++++++------- man/Makefile.am | 2 + man/meson.build | 2 + man/mu-fields.1 | 32 ++++++++++ man/mu-find.1 | 24 ++++---- man/mu-flags.1 | 31 ++++++++++ man/mu-query.7 | 33 +++++----- mu/meson.build | 1 + mu/mu-cmd-fields.cc | 131 ++++++++++++++++++++++++++++++++++++++++ mu/mu-cmd.cc | 46 ++++++++++---- mu/mu-cmd.hh | 20 ++++++ mu/mu-config.cc | 4 +- mu/mu-config.hh | 2 + 13 files changed, 329 insertions(+), 62 deletions(-) create mode 100644 man/mu-fields.1 create mode 100644 man/mu-flags.1 create mode 100644 mu/mu-cmd-fields.cc diff --git a/lib/message/mu-flags.hh b/lib/message/mu-flags.hh index d4841406..637bc4a6 100644 --- a/lib/message/mu-flags.hh +++ b/lib/message/mu-flags.hh @@ -88,10 +88,12 @@ enum struct MessageFlagCategory { * */ struct MessageFlagInfo { - Flags flag; /**< The message flag */ - char shortcut; /**< Shortcut character */ - std::string_view name; /**< Name of the flag */ - MessageFlagCategory category; /**< Flag category */ + + Flags flag; /**< The message flag */ + char shortcut; /**< Shortcut character */ + std::string_view name; /**< Name of the flag */ + MessageFlagCategory category; /**< Flag category */ + std::string_view description; /**< Description */ /** * Get the lower-case version of shortcut @@ -108,24 +110,43 @@ struct MessageFlagInfo { * Array of all flag information. */ constexpr std::array AllMessageFlagInfos = {{ - MessageFlagInfo{Flags::Draft, 'D', "draft", MessageFlagCategory::Mailfile}, - MessageFlagInfo{Flags::Flagged, 'F', "flagged", MessageFlagCategory::Mailfile}, - MessageFlagInfo{Flags::Passed, 'P', "passed", MessageFlagCategory::Mailfile}, - MessageFlagInfo{Flags::Replied, 'R', "replied", MessageFlagCategory::Mailfile}, - MessageFlagInfo{Flags::Seen, 'S', "seen", MessageFlagCategory::Mailfile}, - MessageFlagInfo{Flags::Trashed, 'T', "trashed", MessageFlagCategory::Mailfile}, + MessageFlagInfo{Flags::Draft, 'D', "draft", MessageFlagCategory::Mailfile, + "Draft (in progress)" + }, + MessageFlagInfo{Flags::Flagged, 'F', "flagged", MessageFlagCategory::Mailfile, + "User-flagged" + }, + MessageFlagInfo{Flags::Passed, 'P', "passed", MessageFlagCategory::Mailfile, + "Forwarded message" + }, + MessageFlagInfo{Flags::Replied, 'R', "replied", MessageFlagCategory::Mailfile, + "Replied-to" + }, + MessageFlagInfo{Flags::Seen, 'S', "seen", MessageFlagCategory::Mailfile, + "Viewed at least once" + }, + MessageFlagInfo{Flags::Trashed, 'T', "trashed", MessageFlagCategory::Mailfile, + "Marked for deletion" + }, + MessageFlagInfo{Flags::New, 'N', "new", MessageFlagCategory::Maildir, + "New message" + }, + MessageFlagInfo{Flags::Signed, 'z', "signed", MessageFlagCategory::Content, + "Cryptographically signed" + }, + MessageFlagInfo{Flags::Encrypted, 'x', "encrypted", MessageFlagCategory::Content, + "Encrypted" + }, + MessageFlagInfo{Flags::HasAttachment,'a', "attach", MessageFlagCategory::Content, + "Has at least one attachment" + }, - MessageFlagInfo{Flags::New, 'N', "new", MessageFlagCategory::Maildir}, - - MessageFlagInfo{Flags::Signed, 'z', "signed", MessageFlagCategory::Content}, - MessageFlagInfo{Flags::Encrypted, 'x', "encrypted", - MessageFlagCategory::Content}, - MessageFlagInfo{Flags::HasAttachment, 'a', "attach", - MessageFlagCategory::Content}, - - MessageFlagInfo{Flags::Unread, 'u', "unread", MessageFlagCategory::Pseudo}, - - MessageFlagInfo{Flags::MailingList, 'l', "list", MessageFlagCategory::Content}, + MessageFlagInfo{Flags::Unread, 'u', "unread", MessageFlagCategory::Pseudo, + "New or not seen message" + }, + MessageFlagInfo{Flags::MailingList, 'l', "list", MessageFlagCategory::Content, + "Mailing list message" + }, }}; diff --git a/man/Makefile.am b/man/Makefile.am index e0556000..163c75f6 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -22,7 +22,9 @@ dist_man_MANS = \ mu-cfind.1 \ mu-easy.1 \ mu-extract.1 \ + mu-fields.1 \ mu-find.1 \ + mu-flags.1 \ mu-help.1 \ mu-index.1 \ mu-info.1 \ diff --git a/man/meson.build b/man/meson.build index 4d7075e8..6236e1ff 100644 --- a/man/meson.build +++ b/man/meson.build @@ -21,7 +21,9 @@ install_man( 'mu-cfind.1', 'mu-easy.1', 'mu-extract.1', + 'mu-fields.1', 'mu-find.1', + 'mu-flags.1', 'mu-help.1', 'mu-index.1', 'mu-info.1', diff --git a/man/mu-fields.1 b/man/mu-fields.1 new file mode 100644 index 00000000..e86c22a7 --- /dev/null +++ b/man/mu-fields.1 @@ -0,0 +1,32 @@ +.TH MU FIELDS 1 "April 2022" "User Manuals" + +.SH NAME + +mu fields\- list all message fields + +.SH SYNOPSIS + +.B mu fields [options] + +.SH DESCRIPTION + +\fBmu fields\fR is the \fBmu\fR command for showing a table of message fields +and their properties. + +.SH OPTIONS + +Inherits common options from +.BR mu(1) + +.SH BUGS + +Please report bugs if you find them: +.BR https://github.com/djcb/mu/issues + +.SH AUTHOR + +Dirk-Jan C. Binnema + +.SH "SEE ALSO" + +.BR mu (1) diff --git a/man/mu-find.1 b/man/mu-find.1 index 65ee4482..ca64d3e1 100644 --- a/man/mu-find.1 +++ b/man/mu-find.1 @@ -1,4 +1,4 @@ -.TH MU FIND 1 "29 January 2022" "User Manuals" +.TH MU FIND 1 "29 April 2022" "User Manuals" .SH NAME @@ -23,10 +23,10 @@ some search pattern. The search patterns are described in detail in For example: .nf - $ mu find subject:snow and date:2017.. + $ mu find subject:snow and date:2009.. .fi -would find all messages in 2017 with 'snow' in the subject field, e.g: +would find all messages in 2009 with 'snow' in the subject field, e.g: .nf 2009-03-05 17:57:33 EET Lucia running in the snow @@ -71,23 +71,20 @@ would list the date, subject and sender of all messages with 'snow' in the their subject. The table of replacement characters is superset of the list mentions for -search parameters; the complete list: - +search parameters, such as: .nf t \fBt\fRo: recipient - c \fBc\fRc: (carbon-copy) recipient - h Bcc: (blind carbon-copy, \fBh\fRidden) recipient d Sent \fBd\fRate of the message f Message sender (\fBf\fRrom:) g Message flags (fla\fBg\fRs) l Full path to the message (\fBl\fRocation) - p Message \fBp\fRriority (high, normal, low) s Message \fBs\fRubject i Message-\fBi\fRd m \fBm\fRaildir - v Mailing-list Id .fi +For the complete, up-to-date list, see: +.BR mu-fields(1) The message flags are described in \fBmu-query\fR(7). As an example, a message which is 'seen', has an attachment and is signed would @@ -97,12 +94,10 @@ message would have 'nx'. .TP \fB\-s\fR, \fB\-\-sortfield\fR \fR=\fI\fR and \fB\-z\fR, \fB\-\-reverse\fR specifies the field to sort the search results by, and the -direction (i.e., 'reverse' means that the sort should be reverted - Z-A). The -following fields are supported: +direction (i.e., 'reverse' means that the sort should be reverted - Z-A). Examples include: .nf cc,c Cc (carbon-copy) recipient(s) - bcc,h Bcc (blind-carbon-copy) recipient(s) date,d Message sent date from,f Message sender maildir,m Maildir @@ -110,9 +105,11 @@ following fields are supported: prio,p Nessage priority subject,s Message subject to,t To:-recipient(s) - list,v Mailing-list id .fi +For the complete, up-to-date list, see: +.BR mu-fields(1) + Thus, for example, to sort messages by date, you could specify: .nf @@ -338,3 +335,4 @@ Dirk-Jan C. Binnema .BR mu (1), .BR mu-index (1), .BR mu-query (7) +.BR mu-fields (1) diff --git a/man/mu-flags.1 b/man/mu-flags.1 new file mode 100644 index 00000000..7ba9e7b4 --- /dev/null +++ b/man/mu-flags.1 @@ -0,0 +1,31 @@ +.TH MU FLAGS 1 "April 2022" "User Manuals" + +.SH NAME + +mu flags\- list all message flags + +.SH SYNOPSIS + +.B mu flags [options] + +.SH DESCRIPTION + +\fBmu flags\fR is the \fBmu\fR command for showing a table of message flags and their properties, which is useful for devising queries. + +.SH OPTIONS + +Inherits common options from +.BR mu(1) + +.SH BUGS + +Please report bugs if you find them: +.BR https://github.com/djcb/mu/issues + +.SH AUTHOR + +Dirk-Jan C. Binnema + +.SH "SEE ALSO" + +.BR mu (1) diff --git a/man/mu-query.7 b/man/mu-query.7 index 7e42e230..e00891bf 100644 --- a/man/mu-query.7 +++ b/man/mu-query.7 @@ -1,25 +1,24 @@ -.TH MU QUERY 7 "28 December 2017" "User Manuals" +.TH MU QUERY 7 "22 April 2022" "User Manuals" .SH NAME -mu query language \- a language for finding messages in \fBmu\fR -databases. +mu query language \- a language for finding messages in \fBmu\fR databases. .SH DESCRIPTION -The mu query language is a language used by \fBmu find\fR and -\fBmu4e\fR to find messages in \fBmu\fR's Xapian databases. The -language is quite similar to Xapian's default query-parser, but is an -independent implementation that is customized for the mu/mu4e -use-case. +The mu query language is a language used by \fBmu find\fR and \fBmu4e\fR to find +messages in \fBmu\fR's Xapian databases. The language is quite similar to +Xapian's default query-parser, but is an independent implementation that is +customized for the mu/mu4e use-case. -In this article, we give a structured but informal overview of the -query language and provide examples. +In this article, we give a structured but informal overview of the query +language and provide examples. -\fBNOTE:\fR if you use queries on the command-line (say, for \fBmu -find\fR), you need to quote any characters that would otherwise be -interpreted by the shell, such as \fB""\fR, \fB(\fR and \fB)\fR and -whitespace. +As a companion to this, we recommend the \fBmu fields\fR command to get an up-to-date list of the available fields. + +\fBNOTE:\fR if you use queries on the command-line (say, for \fBmu find\fR), you +need to quote any characters that would otherwise be interpreted by the shell, +such as \fB""\fR, \fB(\fR and \fB)\fR and whitespace. .de EX1 .nf @@ -132,8 +131,7 @@ take quite a bit longer than 'normal' queries. .SH FIELDS We already saw a number of search fields, such as \fBsubject:\fR and -\fBbody:\fR. Here is the full table, a shortcut character and a -description. +\fBbody:\fR. Here is the full table, a shortcut character and a description. .EX1 cc,c Cc (carbon-copy) recipient(s) bcc,h Bcc (blind-carbon-copy) recipient(s) @@ -142,6 +140,7 @@ description. subject,s Message subject body,b Message body maildir,m Maildir + modified,k Last modification time msgid,i Message-ID prio,p Message priority (\fIlow\fR, \fInormal\fR or \fIhigh\fR) flag,g Message Flags @@ -152,6 +151,8 @@ description. mime,y MIME-type of one or more message parts tag,x Tags for the message list,v Mailing list (e.g. the List-Id value) + +The \fBmu fields\fR command is recommended to get the latest version. .EX2 The shortcut character can be used instead of the full name: .EX1 diff --git a/mu/meson.build b/mu/meson.build index 47b390dd..99659ead 100644 --- a/mu/meson.build +++ b/mu/meson.build @@ -25,6 +25,7 @@ mu = executable( 'mu.cc', 'mu-cmd-cfind.cc', 'mu-cmd-extract.cc', + 'mu-cmd-fields.cc', 'mu-cmd-find.cc', 'mu-cmd-index.cc', 'mu-cmd-script.cc', diff --git a/mu/mu-cmd-fields.cc b/mu/mu-cmd-fields.cc new file mode 100644 index 00000000..c82c2f4b --- /dev/null +++ b/mu/mu-cmd-fields.cc @@ -0,0 +1,131 @@ +/* +** Copyright (C) 2022 Dirk-Jan C. Binnema +** +** 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-cmd.hh" +#include +#include +#include "mu-flags.hh" +#include "utils/mu-utils.hh" +#include "thirdparty/tabulate.hpp" + +using namespace Mu; + +static void +show_fields(const MuConfig* opts) +{ + using namespace tabulate; + using namespace std::string_literals; + + Table fields; + fields.add_row({"field-name", "alias", "short", "search", + "value", "example", "description"}); + + if (!opts->nocolor) { + (*fields.begin()).format() + .font_style({FontStyle::bold}) + .font_color({Color::blue}); + } + + auto disp= [&](std::string_view sv)->std::string { + if (sv.empty()) + return ""; + else + return format("%*s", STR_V(sv)); + }; + + auto searchable=[&](const Field& field)->std::string { + if (field.is_boolean_term()) + return "boolean"; + if (field.is_indexable_term()) + return "index"; + if (field.is_normal_term()) + return "yes"; + if (field.is_contact()) + return "contact"; + if (field.is_range()) + return "range"; + return "no"; + }; + + size_t row{}; + field_for_each([&](auto&& field){ + if (field.is_internal()) + return; // skip. + + fields.add_row({format("%*s", STR_V(field.name)), + field.alias.empty() ? "" : format("%*s", STR_V(field.alias)), + field.shortcut ? format("%c", field.shortcut) : ""s, + searchable(field), + field.is_value() ? "yes" : "no", + disp(field.example_query), + disp(field.description)}); + ++row; + }); + + std::cout << fields << '\n'; +} + +static void +show_flags(const MuConfig* opts) +{ + using namespace tabulate; + using namespace std::string_literals; + + Table flags; + flags.add_row({"flag", "shortcut", "category", "description"}); + + if (!opts->nocolor) { + (*flags.begin()).format() + .font_style({FontStyle::bold}) + .font_color({Color::green}); + } + + flag_infos_for_each([&](const MessageFlagInfo& info) { + + flags.add_row({format("%*s", STR_V(info.name)), + format("%c", info.shortcut), + ""s, + std::string{info.description}}); + }); + + std::cout << flags << '\n'; +} + + + +Result +Mu::mu_cmd_fields(const MuConfig* opts) +{ + g_return_val_if_fail(opts, Err(Error::Code::Internal, "no opts")); + + show_fields(opts); + + return Ok(); + +} + +Result +Mu::mu_cmd_flags(const MuConfig* opts) +{ + g_return_val_if_fail(opts, Err(Error::Code::Internal, "no opts")); + + show_flags(opts); + + return Ok(); + +} diff --git a/mu/mu-cmd.cc b/mu/mu-cmd.cc index e48a83ff..1891aa2b 100644 --- a/mu/mu-cmd.cc +++ b/mu/mu-cmd.cc @@ -43,6 +43,8 @@ #include "utils/mu-utils.hh" #include "message/mu-message.hh" +#include + #define VIEW_TERMINATOR '\f' /* form-feed */ using namespace Mu; @@ -467,14 +469,18 @@ cmd_verify(const MuConfig* opts) static MuError cmd_info(const Mu::Store& store, const MuConfig* opts, GError** err) { - Mu::MaybeAnsi col{!opts->nocolor}; + using namespace tabulate; - key_val(col, "maildir", store.properties().root_maildir); - key_val(col, "database-path", store.properties().database_path); - key_val(col, "schema-version", store.properties().schema_version); - key_val(col, "max-message-size", store.properties().max_message_size); - key_val(col, "batch-size", store.properties().batch_size); - key_val(col, "messages in store", store.size()); + Table info; + + //Mu::MaybeAnsi col{!opts->nocolor}; + + info.add_row({"maildir", store.properties().root_maildir}); + info.add_row({"database-path", store.properties().database_path}); + info.add_row({"schema-version", store.properties().schema_version}); + info.add_row({"max-message-size", format("%zu", store.properties().max_message_size)}); + info.add_row({"batch-size", format("%zu", store.properties().batch_size)}); + info.add_row({"messages in store", format("%zu", store.size())}); const auto created{store.properties().created}; const auto tstamp{::localtime(&created)}; @@ -485,14 +491,27 @@ cmd_info(const Mu::Store& store, const MuConfig* opts, GError** err) strftime(tbuf, sizeof(tbuf), "%c", tstamp); #pragma GCC diagnostic pop - key_val(col, "created", tbuf); + info.add_row({"created", tbuf}); const auto addrs{store.properties().personal_addresses}; if (addrs.empty()) - key_val(col, "personal-address", ""); + info.add_row({"personal-address", ""}); else for (auto&& c : addrs) - key_val(col, "personal-address", c); + info.add_row({"personal-address", c}); + + if (!opts->nocolor) { + for (auto&& row: info) { + row.cells().at(0)->format().font_style({FontStyle::bold}) + .font_color({Color::green}); + row.cells().at(1)->format().font_color({Color::blue}); + } + } + + std::cout << info << std::endl; + + + return MU_OK; } @@ -621,7 +640,12 @@ Mu::mu_cmd_execute(const MuConfig* opts, GError** err) try { /* * no store needed */ - + case MU_CONFIG_CMD_FIELDS: + merr = mu_error_from_result(mu_cmd_fields(opts), err); + break; + case MU_CONFIG_CMD_FLAGS: + merr = mu_error_from_result(mu_cmd_flags(opts), err); + break; case MU_CONFIG_CMD_MKDIR: merr = cmd_mkdir(opts, err); break; case MU_CONFIG_CMD_SCRIPT: merr = mu_cmd_script(opts, err); break; case MU_CONFIG_CMD_VIEW: diff --git a/mu/mu-cmd.hh b/mu/mu-cmd.hh index 708db1ff..c293a4d5 100644 --- a/mu/mu-cmd.hh +++ b/mu/mu-cmd.hh @@ -45,6 +45,26 @@ Result mu_cmd_find(const Mu::Store& store, const MuConfig* opts); */ Result mu_cmd_extract(const MuConfig* opts); + +/** + * execute the 'fields' command + * + * @param opts configuration options + * + * @return Ok() or some error + */ +Result mu_cmd_fields(const MuConfig* opts); + +/** + * execute the 'flags' command + * + * @param opts configuration options + * + * @return Ok() or some error + */ +Result mu_cmd_flags(const MuConfig* opts); + + /** * execute the 'script' command * diff --git a/mu/mu-config.cc b/mu/mu-config.cc index d1e0b343..adb13f19 100644 --- a/mu/mu-config.cc +++ b/mu/mu-config.cc @@ -443,7 +443,9 @@ cmd_from_string(const char* str) {"info", MU_CONFIG_CMD_INFO}, {"init", MU_CONFIG_CMD_INIT}, {"mkdir", MU_CONFIG_CMD_MKDIR}, {"remove", MU_CONFIG_CMD_REMOVE}, {"script", MU_CONFIG_CMD_SCRIPT}, {"server", MU_CONFIG_CMD_SERVER}, - {"verify", MU_CONFIG_CMD_VERIFY}, {"view", MU_CONFIG_CMD_VIEW}}; + {"verify", MU_CONFIG_CMD_VERIFY}, {"view", MU_CONFIG_CMD_VIEW}, + {"fields", MU_CONFIG_CMD_FIELDS}, {"flags", MU_CONFIG_CMD_FLAGS} + }; if (!str) return MU_CONFIG_CMD_UNKNOWN; diff --git a/mu/mu-config.hh b/mu/mu-config.hh index 5d5ff47b..fa073533 100644 --- a/mu/mu-config.hh +++ b/mu/mu-config.hh @@ -64,7 +64,9 @@ typedef enum { MU_CONFIG_CMD_ADD, MU_CONFIG_CMD_CFIND, MU_CONFIG_CMD_EXTRACT, + MU_CONFIG_CMD_FIELDS, MU_CONFIG_CMD_FIND, + MU_CONFIG_CMD_FLAGS, MU_CONFIG_CMD_HELP, MU_CONFIG_CMD_INDEX, MU_CONFIG_CMD_INFO,