diff options
Diffstat (limited to 'worker/notmuch/search.go')
-rw-r--r-- | worker/notmuch/search.go | 143 |
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 } |