diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-12 11:30:38 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-12 11:30:38 +0200 |
| commit | 436efafb13079538f3ad5ab1b3df90e02311143c (patch) | |
| tree | 15ca143ca718d50f2ba08d180e7e43c383f69cda /mailrules.py | |
| parent | c2896d8896235df027dc38d7c93ffe4761d4b02d (diff) | |
| download | mailctl-436efafb13079538f3ad5ab1b3df90e02311143c.tar.gz mailctl-436efafb13079538f3ad5ab1b3df90e02311143c.zip | |
feat(rules): build the scoped query in one place
A rule stores no scope. The hook supplies tag:new, a dry run supplies
nothing. The parentheses around the rule's own query are what stop a
disjunction of senders from escaping the scope and matching everything.
Diffstat (limited to 'mailrules.py')
| -rwxr-xr-x | mailrules.py | 19 |
1 files changed, 19 insertions, 0 deletions
diff --git a/mailrules.py b/mailrules.py index 8c65708..dbb80a5 100755 --- a/mailrules.py +++ b/mailrules.py @@ -126,6 +126,25 @@ def load(path=None): return store +def scoped_query(rule, scope): + """The rule's query narrowed by `scope`, or the bare query when scope is + empty. + + The parentheses are load-bearing. notmuch's `and` binds tighter than + `or`, so `tag:new and a or b` means `(tag:new and a) or b`: a rule that + is a disjunction of senders would escape its scope and match the whole + corpus. Do not remove them, and do not build this string anywhere else. + """ + if not scope: + return rule.query + return f"{scope} and ({rule.query})" + + +def tag_arguments(rule): + """The +tag/-tag arguments for `notmuch tag`, adds before removes.""" + return [f"+{t}" for t in rule.add] + [f"-{t}" for t in rule.remove] + + def save(store, path=None): """Write the store atomically: a temp file in the same directory, then rename. Rename within a filesystem is atomic, so a concurrent reader sees |
