diff --git a/gmail_yaml_filters/ruleset.py b/gmail_yaml_filters/ruleset.py index 3bc437e..b500f17 100644 --- a/gmail_yaml_filters/ruleset.py +++ b/gmail_yaml_filters/ruleset.py @@ -3,7 +3,7 @@ from __future__ import print_function, unicode_literals from collections import OrderedDict -from datetime import datetime +from datetime import date, datetime from functools import total_ordering from itertools import chain from operator import attrgetter @@ -125,13 +125,15 @@ def _format_has_shortcuts(key, value): return (key, value) -def _search_operator(keyword): +def _search_operator(keyword, wrap=True): def formatter(key, value): condition = "hasTheWord" if value and value[0] == "-": condition = "doesNotHaveTheWord" value = value[1:] - return (condition, "{}:({})".format(keyword, value)) + if wrap: + value = f"({value})" + return (condition, f"{keyword}:{value}") return formatter @@ -191,15 +193,20 @@ class RuleCondition(_RuleConstruction): # allows `has: attachment`, `has: drive`, etc. in YAML "has": _format_has_shortcuts, # allows `bcc: whatever`, `is: starred`, etc. in YAML + "after": _search_operator("after", wrap=False), "bcc": _search_operator("bcc"), + "before": _search_operator("before", wrap=False), "category": _search_operator("category"), "cc": _search_operator("cc"), "deliveredto": _search_operator("deliveredto"), "filename": _search_operator("filename"), + "in": _search_operator("in"), "is": _search_operator("is"), "labeled": _search_operator("label"), "larger": _search_operator("larger"), "list": _search_operator("list"), + "newer_than": _search_operator("newer_than", wrap=False), + "older_than": _search_operator("older_than", wrap=False), "rfc822msgid": _search_operator("rfc822msgid"), "size": _search_operator("size"), "smaller": _search_operator("smaller"), @@ -417,6 +424,8 @@ def add(self, key, value, validate=True): elif isinstance(value, Iterable): for actual_value in value: self.add(key, actual_value) + elif isinstance(value, date): + self.add_construction(key, value.isoformat()) else: raise InvalidRuleType(type(value)) diff --git a/gmail_yaml_filters/tests/test_ruleset.py b/gmail_yaml_filters/tests/test_ruleset.py index bd85b0e..55c527c 100644 --- a/gmail_yaml_filters/tests/test_ruleset.py +++ b/gmail_yaml_filters/tests/test_ruleset.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from datetime import date + +import pytest + from gmail_yaml_filters.ruleset import RuleAction, RuleCondition, RuleSet @@ -22,52 +26,32 @@ def sample_rule(name): # Test how identifier_map and formatter_map operate -def test_condition_has(): - assert _flat({"has": "whatever"}) == { - "hasTheWord": RuleCondition("hasTheWord", "whatever"), - } - - -def test_condition_has_special(): - assert _flat({"has": "drive"}) == { - "hasTheWord": RuleCondition("hasTheWord", "has:drive"), - } - - -def test_condition_has_label(): - assert _flat({"labeled": "whatever"}) == { - "hasTheWord": RuleCondition("hasTheWord", "label:(whatever)"), - } - - -def test_condition_does_not_have_label(): - assert _flat({"labeled": "-whatever"}) == { - "doesNotHaveTheWord": RuleCondition("doesNotHaveTheWord", "label:(whatever)"), - } - - -def test_condition_is(): - assert _flat({"is": "snoozed"}) == { - "hasTheWord": RuleCondition("hasTheWord", "is:(snoozed)"), - } - - -def test_condition_is_not(): - assert _flat({"is": "-snoozed"}) == { - "doesNotHaveTheWord": RuleCondition("doesNotHaveTheWord", "is:(snoozed)"), - } - - -def test_action_archive(): - assert _flat({"archive": True}) == { - "shouldArchive": RuleAction("shouldArchive", "true"), - } - - -def test_action_forward(): - assert _flat({"forward": "someone@example.com"}) == { - "forwardTo": RuleAction("forwardTo", "someone@example.com"), - } +@pytest.mark.parametrize( + "input,condition,value", + [ + ({"has": "whatever"}, "hasTheWord", "whatever"), + ({"has": "drive"}, "hasTheWord", "has:drive"), + ({"labeled": "whatever"}, "hasTheWord", "label:(whatever)"), + ({"labeled": "-whatever"}, "doesNotHaveTheWord", "label:(whatever)"), + ({"is": "snoozed"}, "hasTheWord", "is:(snoozed)"), + ({"is": "-snoozed"}, "doesNotHaveTheWord", "is:(snoozed)"), + ({"after": date(2024, 2, 15)}, "hasTheWord", "after:2024-02-15"), + ({"before": date(2024, 2, 15)}, "hasTheWord", "before:2024-02-15"), + ], +) +def test_flattened_condition(input, condition, value): + assert _flat(input) == {condition: RuleCondition(condition, value)} + + +@pytest.mark.parametrize( + "input,condition,value", + [ + ({"archive": True}, "shouldArchive", "true"), + ({"forward": "someone@example.com"}, "forwardTo", "someone@example.com"), + ], +) +def test_flattened_action(input, condition, value): + assert _flat(input) == {condition: RuleAction(condition, value)} def test_publishable():