aboutsummaryrefslogtreecommitdiffstats
path: root/worker/notmuch/search.go
diff options
context:
space:
mode:
Diffstat (limited to 'worker/notmuch/search.go')
-rw-r--r--worker/notmuch/search.go143
1 files changed, 87 insertions, 56 deletions
diff --git a/worker/notmuch/search.go b/worker/notmuch/search.go
index e1428b45..b3592d41 100644
--- a/worker/notmuch/search.go
+++ b/worker/notmuch/search.go
@@ -4,18 +4,18 @@
package notmuch
import (
- "strconv"
- "strings"
+ "fmt"
- "git.sr.ht/~rjarry/aerc/log"
- "git.sr.ht/~sircmpwn/getopt"
+ "git.sr.ht/~rjarry/aerc/models"
+ "git.sr.ht/~rjarry/aerc/worker/types"
+ "git.sr.ht/~rjarry/go-opt"
)
type queryBuilder struct {
s string
}
-func (q *queryBuilder) add(s string) {
+func (q *queryBuilder) and(s string) {
if len(s) == 0 {
return
}
@@ -25,62 +25,93 @@ func (q *queryBuilder) add(s string) {
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))
+func (q *queryBuilder) or(s string) {
+ if len(s) == 0 {
+ return
+ }
+ if len(q.s) != 0 {
+ q.s += " or "
+ }
+ q.s += "(" + s + ")"
+}
+
+func translate(crit *types.SearchCriteria) string {
+ if crit == nil {
+ return ""
+ }
+ var base queryBuilder
+
+ // recipients
+ var from queryBuilder
+ for _, f := range crit.From {
+ from.or("from:" + opt.QuoteArg(f))
+ }
+ if from.s != "" {
+ base.and(from.s)
+ }
+
+ var to queryBuilder
+ for _, t := range crit.To {
+ to.or("to:" + opt.QuoteArg(t))
+ }
+ if to.s != "" {
+ base.and(to.s)
+ }
+
+ var cc queryBuilder
+ for _, c := range crit.Cc {
+ cc.or("cc:" + opt.QuoteArg(c))
+ }
+ if cc.s != "" {
+ base.and(cc.s)
+ }
+
+ // flags
+ for _, f := range []models.Flags{models.SeenFlag, models.AnsweredFlag, models.FlaggedFlag} {
+ if crit.WithFlags.Has(f) {
+ base.and(getParsedFlag(f, false))
+ }
+ if crit.WithoutFlags.Has(f) {
+ base.and(getParsedFlag(f, true))
}
}
+
+ // dates
switch {
- case body:
- qb.add("body:" + strconv.Quote(strings.Join(args[optind:], " ")))
- default:
- qb.add(strings.Join(args[optind:], " "))
+ case !crit.StartDate.IsZero() && !crit.EndDate.IsZero():
+ base.and(fmt.Sprintf("date:@%d..@%d",
+ crit.StartDate.Unix(), crit.EndDate.Unix()))
+ case !crit.StartDate.IsZero():
+ base.and(fmt.Sprintf("date:@%d..", crit.StartDate.Unix()))
+ case !crit.EndDate.IsZero():
+ base.and(fmt.Sprintf("date:..@%d", crit.EndDate.Unix()))
+ }
+
+ // other terms
+ if crit.Terms != "" {
+ if crit.SearchBody {
+ base.and("body:" + opt.QuoteArg(crit.Terms))
+ } else {
+ base.and(crit.Terms)
+ }
}
- return qb.s, nil
+
+ return base.s
}
-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
+func getParsedFlag(flag models.Flags, inverse bool) string {
+ name := ""
+ switch flag {
+ case models.AnsweredFlag:
+ name = "tag:replied"
+ case models.SeenFlag:
+ name = "tag:unread"
+ inverse = !inverse
+ case models.FlaggedFlag:
+ name = "tag:flagged"
+ }
+ if inverse {
+ name = "not " + name
}
+ return name
}