class String(Atom):
    def init(self, value):
        Atom.init(self)

        self._value = unicode(value)

    def unique_key(self):
        return self._value

    def __repr__(self):
        return Atom.__repr__(self, self._value)

    @property
    def value(self):
        return self._value

    def match(self, value):
        return self._value == value

    def dump(self):
        return self._value

    @classmethod
    def load(cls, value):
        return cls(value) class RegExp(Atom):
    _forbidden_flags = [
        (re.X, "re.X / re.VERBOSE"),
        (re.M, "re.M / re.MULTILINE"),
        (re.L, "re.L / re.LOCALE")
    ]

    @classmethod
    def from_string(cls, string, ignore_case=False):
        return cls(re.escape(string), ignore_case=ignore_case)

    @classmethod
    def from_re(cls, re_obj):
        for flag, name in cls._forbidden_flags:
            if re_obj.flags & flag == flag:
                raise ValueError("forbidden regular expression flag " + name)

        ignore_case = (re_obj.flags & re.I) != 0
        return cls(re_obj.pattern, ignore_case=ignore_case)

    @property
    def pattern(self):
        return self._regexp.pattern

    @property
    def ignore_case(self):
        return self._regexp.flags & re.IGNORECASE != 0

    def init(self, pattern, ignore_case=False):
        Atom.init(self)

        flags = re.U | re.S | (re.I if ignore_case else 0)
        try:
            self._regexp = re.compile(pattern, flags)
        except re.error as error:
            raise ValueError(error)

    def unique_key(self):
        return self._regexp.pattern, bool(self._regexp.flags & re.I)

    def __repr__(self):
        pattern = self._regexp.pattern
        if self._regexp.flags & re.IGNORECASE == re.I:
            return Atom.__repr__(self, pattern, ignore_case=True)
        return Atom.__repr__(self, pattern)

    def match(self, value):
        return self._regexp.search(value)

    def dump(self):
        return self._regexp.pattern, bool(self._regexp.flags & re.I)

    @classmethod
    def load(cls, (pattern, ignore_case)):
        return cls(pattern, ignore_case) class IP(Atom):
    @property
    def range(self):
        return self._range

    def init(self, range, extra=None):
        Atom.init(self)

        if isinstance(range, iprange.IPRange):
            if extra is not None:
                raise TypeError("unexpected second argument")
        else:
            range = iprange.IPRange.from_autodetected(range, extra)
        self._range = range

    def unique_key(self):
        return self._range

    def __repr__(self):
        return Atom.__repr__(self, unicode(self._range))

    def __unicode__(self):
        return unicode(self._range)

    def match(self, value):
        try:
            range = iprange.IPRange.from_autodetected(value)
        except ValueError:
            return False
        return self._range.contains(range)

    def dump(self):
        return unicode(self._range)

    @classmethod
    def load(cls, value):
        return cls(value) class DomainName(Atom):
    @property
    def pattern(self):
        return self._pattern

    def init(self, pattern):
        Atom.init(self)

        if not isinstance(pattern, _domainname.Pattern):
            pattern = _domainname.Pattern.from_string(pattern)
        self._pattern = pattern

    def unique_key(self):
        return self._pattern

    def __repr__(self):
        return Atom.__repr__(self, unicode(self._pattern))

    def __unicode__(self):
        return unicode(self._pattern)

    def match(self, value):
        name = _domainname.parse_name(value)
        if name is None:
            return False
        return self._pattern.contains(name)

    def dump(self):
        return unicode(self._pattern)

    @classmethod
    def load(cls, value):
        return cls(value) class And(Rule):
    def init(self, first, *rest):
        Rule.init(self)

        self._rules = frozenset((first,) + rest)

    def unique_key(self):
        return self._rules

    def __repr__(self):
        return Rule.__repr__(self, *self._rules)

    @property
    def subrules(self):
        return self._rules

    def match_with_cache(self, obj, cache):
        for rule in self.subrules:
            if not rule.match(obj, cache):
                return False
        return True

    def dump(self):
        return self._rules

    @classmethod
    def load(cls, subrules):
        return cls(*subrules) class Or(And):
    def match_with_cache(self, obj, cache):
        for rule in self.subrules:
            if rule.match(obj, cache):
                return True
        return False class No(Rule):
    def init(self, rule):
        Rule.init(self)

        self._rule = rule

    def unique_key(self):
        return self._rule

    def __repr__(self):
        return Rule.__repr__(self, self._rule)

    @property
    def subrule(self):
        return self._rule

    def match_with_cache(self, obj, cache):
        return not self._rule.match(obj, cache)

    def dump(self):
        return self._rule

    @classmethod
    def load(cls, rule):
        return cls(rule) class Match(Rule):
    _to_atom = [
        (basestring, atoms.String),
        (type(re.compile(".")), atoms.RegExp.from_re)
    ]

    _from_atom = [
        (atoms.String, lambda x: x.value)
    ]

    def _convert(self, obj, conversions):
        for converted_type, conversion_func in conversions:
            if isinstance(obj, converted_type):
                return conversion_func(obj)
        return obj

    def init(self, key=None, value=None):
        Rule.init(self)

        self._key = self._convert(key, self._to_atom)
        self._value = self._convert(value, self._to_atom)

    def unique_key(self):
        return self._key, self._value

    def __repr__(self):
        key = self._convert(self._key, self._from_atom)
        value = self._convert(self._value, self._from_atom)

        if key is None and value is None:
            return Rule.__repr__(self)
        if key is None:
            return Rule.__repr__(self, value=value)
        if value is None:
            return Rule.__repr__(self, key=key)
        return Rule.__repr__(self, key, value)

    @property
    def key(self):
        return self._key

    @property
    def value(self):
        return self._value

    def match_with_cache(self, event, cache):
        if self._key is None:
            return event.contains(filter=self.filter)
        return event.contains(self._key.value, filter=self.filter)

    def filter(self, value):
        return self._value is None or self._value.match(value)

    def dump(self):
        return (self._key, self._value)

    @classmethod
    def load(cls, (key, value)):
        return cls(key, value) class NonMatch(Match):
    def filter(self, value):
        return self._value is None or not self._value.match(value) class Fuzzy(Rule):
    @property
    def atom(self):
        return self._atom

    def init(self, atom):
        Rule.init(self)

        self._atom = atom

        if isinstance(atom, atoms.String):
            self._matcher = atoms.RegExp.from_string(atom.value, ignore_case=True)
        else:
            self._matcher = atom

    def unique_key(self):
        return self._atom

    def __repr__(self):
        return Rule.__repr__(self, self._atom)

    def match_with_cache(self, event, cache):
        if any(self._matcher.match(x) for x in event.keys()):
            return True
        if event.contains(filter=self._matcher.match):
            return True
        return False

    def dump(self):
        return self._atom

    @classmethod
    def load(cls, dumped):
        return cls(dumped) class Anything(Rule):
    def match_with_cache(self, obj, cache):
        return True def format(self, obj):
    return "".join(self._format(obj)) def parse(string):
    match = expr.parse(string)
    if not match or match[1] != "":
        raise ValueError("could not parse " + repr(string))
    return match[0] def rule(obj):
    if isinstance(obj, basestring):
        return parse(obj)
    return obj
import sys
import unittest
from ..atoms import String, RegExp, IP, DomainName
from ..rules import And, Or, No, Match, NonMatch, Fuzzy, Anything
from ..rulelang import format, parse, rule
from __future__ import unicode_literals




class TestRule(unittest.TestCase):
    def test_basic(self):
        a = Match("a", "a")
        self.assertEqual(a, rule(a))
        self.assertEqual(a, rule("a=a"))


class TestParse(unittest.TestCase):
    def test_ignore_extra_whitespace(self):
        self.assertEqual(parse("  a  "), parse("a"))
        self.assertEqual(parse("(  a  )"), parse("a"))

    def test_and(self):
        a = Match("a", "a")
        b = Match("b", "b")
        c = Match("c", "c")
        self.assertEqual(And(a, b, c), parse("a=a and b=b and c=c"))
        self.assertEqual(And(a, And(b, c)), parse("a=a and (b=b and c=c)"))
        self.assertEqual(And(And(a, b), c), parse("(a=a and b=b) and c=c"))

        self.assertEqual(And(a, b, c), parse("(a=a) and (b=b) and (c=c)"))
        self.assertEqual(And(a, b, c), parse("(a=a)and(b=b)and(c=c)"))

        self.assertEqual(And(a, b, Or(c, And(a, b))), parse("a=a and b=b and c=c or a=a and b=b"))

    def test_or(self):
        a = Match("a", "a")
        b = Match("b", "b")
        c = Match("c", "c")
        self.assertEqual(Or(a, b, c), parse("a=a or b=b or c=c"))
        self.assertEqual(Or(a, Or(b, c)), parse("a=a or (b=b or c=c)"))
        self.assertEqual(Or(Or(a, b), c), parse("(a=a or b=b) or c=c"))

        self.assertEqual(Or(a, b, c), parse("(a=a) or (b=b) or (c=c)"))
        self.assertEqual(Or(a, b, c), parse("(a=a)or(b=b)or(c=c)"))

        self.assertEqual(Or(a, b, And(c, Or(a, b))), parse("a=a or b=b or c=c and a=a or b=b"))

    def test_match(self):
        string = Match('a', 'b')
        self.assertEqual(string, parse('a=b'))
        self.assertEqual(string, parse('a = b'))
        self.assertEqual(string, parse('"\u0061"="\u0062"'))

        rex = Match('a', RegExp('b'))
        self.assertEqual(rex, parse('a=/b/'))

        self.assertEqual(Match("a"), parse("a=*"))
        self.assertEqual(Match(value="b"), parse("*=b"))
        self.assertEqual(Match(), parse("*=*"))

        self.assertEqual(Match("a", IP("1.2.3.4")), parse("a in 1.2.3.4"))
        self.assertEqual(Match("a", IP("1.2.3.4")), parse("a in 1.2.3.4/32"))

        self.assertEqual(Match("a", DomainName("domain.example")), parse("a in domain.example"))
        self.assertEqual(Match("a", DomainName("domain.example")), parse("a in DOMAIN.example"))
        self.assertEqual(Match("a", DomainName("*.example")), parse("a in *.example"))
        self.assertEqual(Match("a", DomainName("\xe4.example")), parse("a in \xe4.example"))

        self.assertEqual(Match("a", DomainName("\xe4.example")), parse("a in \xc4.example"))