diff options
author | Koni Marti <koni.marti@gmail.com> | 2023-05-14 17:16:35 +0200 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2023-06-17 23:18:47 +0200 |
commit | 46c6dfc625649bace7002a77008bf3cadc3e9e3e (patch) | |
tree | b1047e13ed9052b965750498f835036792709538 /worker/notmuch | |
parent | 263d8cbec504bf56791dd3aa8c1e440d96b27d4a (diff) | |
download | aerc-46c6dfc625649bace7002a77008bf3cadc3e9e3e.tar.gz |
notmuch: translate filter/search options to query
Translate the regular filter and search options to a notmuch query to
achieve a basic equivalence across backends for these commands.
The following examples have the same filtering effect:
:filter -uf koni
:filter -u from:koni
:filter tag:unread from:koni
And ':filter -x Flagged' would translate to ':filter tag:flagged'.
Fixes: https://todo.sr.ht/~rjarry/aerc/177
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Tested-by: Inwit <inwit@sindominio.net>
Diffstat (limited to 'worker/notmuch')
-rw-r--r-- | worker/notmuch/search.go | 86 | ||||
-rw-r--r-- | worker/notmuch/worker.go | 24 |
2 files changed, 106 insertions, 4 deletions
diff --git a/worker/notmuch/search.go b/worker/notmuch/search.go new file mode 100644 index 00000000..e1428b45 --- /dev/null +++ b/worker/notmuch/search.go @@ -0,0 +1,86 @@ +//go:build notmuch +// +build notmuch + +package notmuch + +import ( + "strconv" + "strings" + + "git.sr.ht/~rjarry/aerc/log" + "git.sr.ht/~sircmpwn/getopt" +) + +type queryBuilder struct { + s string +} + +func (q *queryBuilder) add(s string) { + if len(s) == 0 { + return + } + if len(q.s) != 0 { + q.s += " and " + } + q.s += "(" + s + ")" +} + +func translate(args []string) (string, error) { + if len(args) == 0 { + return "", nil + } + var qb queryBuilder + opts, optind, err := getopt.Getopts(args, "rux:X:bat:H:f:c:d:") + if err != nil { + // if error occurs here, don't fail + log.Errorf("getopts failed: %v", err) + return strings.Join(args[1:], ""), nil + } + body := false + for _, opt := range opts { + switch opt.Option { + case 'r': + qb.add("not tag:unread") + case 'u': + qb.add("tag:unread") + case 'x': + qb.add(getParsedFlag(opt.Value)) + case 'X': + qb.add("not " + getParsedFlag(opt.Value)) + case 'H': + // TODO + case 'f': + qb.add("from:" + opt.Value) + case 't': + qb.add("to:" + opt.Value) + case 'c': + qb.add("cc:" + opt.Value) + case 'a': + // TODO + case 'b': + body = true + case 'd': + qb.add("date:" + strconv.Quote(opt.Value)) + } + } + switch { + case body: + qb.add("body:" + strconv.Quote(strings.Join(args[optind:], " "))) + default: + qb.add(strings.Join(args[optind:], " ")) + } + return qb.s, nil +} + +func getParsedFlag(name string) string { + switch strings.ToLower(name) { + case "answered": + return "tag:replied" + case "seen": + return "(not tag:unread)" + case "flagged": + return "tag:flagged" + default: + return name + } +} diff --git a/worker/notmuch/worker.go b/worker/notmuch/worker.go index 0ac37ff3..af0f279c 100644 --- a/worker/notmuch/worker.go +++ b/worker/notmuch/worker.go @@ -535,13 +535,19 @@ func (w *worker) handleFlagMessages(msg *types.FlagMessages) error { } func (w *worker) handleSearchDirectory(msg *types.SearchDirectory) error { - // the first item is the command (search / filter) - s := strings.Join(msg.Argv[1:], " ") + // the first item is the command (:search) + log.Debugf("search args: %v", msg.Argv) + s, err := translate(msg.Argv) + if err != nil { + log.Debugf("ERROR: %v", err) + return err + } // we only want to search in the current query, so merge the two together search := w.query if s != "" { search = fmt.Sprintf("(%v) and (%v)", w.query, s) } + log.Debugf("search query: '%s'", search) uids, err := w.uidsFromQuery(search) if err != nil { return err @@ -634,9 +640,14 @@ func (w *worker) loadExcludeTags( func (w *worker) emitDirectoryContents(parent types.WorkerMessage) error { query := w.query if msg, ok := parent.(*types.FetchDirectoryContents); ok { - s := strings.Join(msg.FilterCriteria[1:], " ") + log.Debugf("filter input: '%v'", msg.FilterCriteria) + s, err := translate(msg.FilterCriteria) + if err != nil { + return err + } if s != "" { query = fmt.Sprintf("(%v) and (%v)", query, s) + log.Debugf("filter query: '%s'", query) } } uids, err := w.uidsFromQuery(query) @@ -658,9 +669,14 @@ func (w *worker) emitDirectoryContents(parent types.WorkerMessage) error { func (w *worker) emitDirectoryThreaded(parent types.WorkerMessage) error { query := w.query if msg, ok := parent.(*types.FetchDirectoryThreaded); ok { - s := strings.Join(msg.FilterCriteria[1:], " ") + log.Debugf("filter input: '%v'", msg.FilterCriteria) + s, err := translate(msg.FilterCriteria) + if err != nil { + return err + } if s != "" { query = fmt.Sprintf("(%v) and (%v)", query, s) + log.Debugf("filter query: '%s'", query) } } threads, err := w.db.ThreadsFromQuery(query) |