diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-12 11:42:14 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-12 11:42:14 +0200 |
| commit | f1890fc7b5fe1094445ce8b83c2d2f15258fceec (patch) | |
| tree | 6caf922da23b5a17ee758402b5687fe0a305750c /mailctl.py | |
| parent | f2feb50773b945aafed896b7d75e66035315aa5b (diff) | |
| download | mailctl-f1890fc7b5fe1094445ce8b83c2d2f15258fceec.tar.gz mailctl-f1890fc7b5fe1094445ce8b83c2d2f15258fceec.zip | |
feat(rules): read-only rules list and show
Editing stays out of this tool: a rule edit affects every future sync
and its gate is not designed yet. qtmaildir has the editor.
The install step now copies mailrules.py beside mailctl.py. The import
resolves from the script's own directory, so copying only mailctl.py
would break every command, not just the new ones.
Diffstat (limited to 'mailctl.py')
| -rwxr-xr-x | mailctl.py | 50 |
1 files changed, 50 insertions, 0 deletions
@@ -59,6 +59,8 @@ from email.message import EmailMessage from email.utils import make_msgid, formatdate from pathlib import Path +import mailrules + AUDIT_LOG = Path.home() / ".local" / "state" / "mailctl" / "audit.log" MAIL_ROOT = Path(os.environ.get("MAILCTL_MAIL_ROOT", Path.home() / "Mail")) @@ -472,6 +474,41 @@ def log_mutation(scope, query, tag_expr, count): f"change={' '.join(tag_expr)}\tcount={count}\n") +def cmd_rules_list(args): + store = mailrules.load() + for warning in store.warnings: + print(f"warning: {warning}", file=sys.stderr) + if store.missing: + print(f"No rules file at {mailrules.default_path()}", file=sys.stderr) + return + for rule in mailrules.ordered(store.rules) if args.enabled_only \ + else store.rules: + state = " " if rule.enabled else "-" + tags = " ".join(mailrules.tag_arguments(rule)) + print(f"{state} {rule.stage:>4} {rule.id:<28.28} {tags}") + total = len(store.rules) + disabled = sum(1 for r in store.rules if not r.enabled) + print(f"\n{total} rule(s), {disabled} disabled", file=sys.stderr) + + +def cmd_rules_show(args): + store = mailrules.load() + for rule in store.rules: + if rule.id != args.id: + continue + print(f"id: {rule.id}") + print(f"stage: {rule.stage}") + print(f"enabled: {rule.enabled}") + print(f"add: {', '.join(rule.add) or '(none)'}") + print(f"remove: {', '.join(rule.remove) or '(none)'}") + print(f"query: {rule.query}") + if rule.note: + print(f"note: {rule.note}") + return + print(f"No rule with id '{args.id}'", file=sys.stderr) + sys.exit(1) + + def build_parser(): p = argparse.ArgumentParser( prog="mailctl", @@ -550,6 +587,19 @@ def build_parser(): sp.add_argument("--body-file", help="read body from a file instead") sp.set_defaults(func=cmd_draft) + sp = sub.add_parser("rules", help="inspect the shared tagging rules " + "(read-only)") + rules_sub = sp.add_subparsers(dest="rules_command", required=True) + + rp = rules_sub.add_parser("list", help="list every rule in stage order") + rp.add_argument("--enabled-only", action="store_true", + help="only the rules the hook would run") + rp.set_defaults(func=cmd_rules_list) + + rp = rules_sub.add_parser("show", help="show one rule in full") + rp.add_argument("id") + rp.set_defaults(func=cmd_rules_show) + return p |
