Add support for capturing the match in simple matches

This commit is contained in:
Tulir Asokan 2021-07-28 20:21:44 +03:00
parent 16e4b8e6d8
commit 3964aa6f12

View file

@ -1,5 +1,5 @@
# reminder - A maubot plugin that reacts to messages that match predefined rules. # reminder - A maubot plugin that reacts to messages that match predefined rules.
# Copyright (C) 2019 Tulir Asokan # Copyright (C) 2021 Tulir Asokan
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by # it under the terms of the GNU Affero General Public License as published by
@ -13,39 +13,59 @@
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import Callable, List, Dict, Optional from typing import Callable, List, Dict, Optional, NamedTuple
import re import re
class BlankMatch: class SimpleMatch(NamedTuple):
@staticmethod value: str
def groups() -> List[str]:
return []
@staticmethod def groups(self) -> List[str]:
def group(group: int) -> str: return [self.value]
return ""
@staticmethod def group(self, group: int) -> Optional[str]:
def groupdict() -> Dict[str, str]: if group == 0:
return self.value
return None
def groupdict(self) -> Dict[str, str]:
return {} return {}
class SimplePattern: def matcher_equals(val: str, pattern: str) -> bool:
_ptm = BlankMatch() return val == pattern
matcher: Callable[[str], bool]
def matcher_startswith(val: str, pattern: str) -> bool:
return val.startswith(pattern)
def matcher_endswith(val: str, pattern: str) -> bool:
return val.endswith(pattern)
def matcher_contains(val: str, pattern: str) -> bool:
return pattern in val
SimpleMatcherFunc = Callable[[str, str], bool]
class SimplePattern:
matcher: SimpleMatcherFunc
pattern: str
ignorecase: bool ignorecase: bool
def __init__(self, matcher: Callable[[str], bool], ignorecase: bool) -> None: def __init__(self, matcher: SimpleMatcherFunc, pattern: str, ignorecase: bool) -> None:
self.matcher = matcher self.matcher = matcher
self.pattern = pattern
self.ignorecase = ignorecase self.ignorecase = ignorecase
def search(self, val: str) -> BlankMatch: def search(self, val: str) -> SimpleMatch:
if self.ignorecase: if self.ignorecase:
val = val.lower() val = val.lower()
if self.matcher(val): if self.matcher(val, self.pattern):
return self._ptm return SimpleMatch(self.pattern)
@staticmethod @staticmethod
def compile(pattern: str, flags: re.RegexFlag = re.RegexFlag(0), force_raw: bool = False def compile(pattern: str, flags: re.RegexFlag = re.RegexFlag(0), force_raw: bool = False
@ -58,13 +78,16 @@ class SimplePattern:
first, last = pattern[0], pattern[-1] first, last = pattern[0], pattern[-1]
if first == '^' and last == '$' and (force_raw or esc == f"\\^{pattern[1:-1]}\\$"): if first == '^' and last == '$' and (force_raw or esc == f"\\^{pattern[1:-1]}\\$"):
s_pattern = s_pattern[1:-1] s_pattern = s_pattern[1:-1]
return SimplePattern(lambda val: val == s_pattern, ignorecase=ignorecase) func = matcher_equals
elif first == '^' and (force_raw or esc == f"\\^{pattern[1:]}"): elif first == '^' and (force_raw or esc == f"\\^{pattern[1:]}"):
s_pattern = s_pattern[1:] s_pattern = s_pattern[1:]
return SimplePattern(lambda val: val.startswith(s_pattern), ignorecase=ignorecase) func = matcher_startswith
elif last == '$' and (force_raw or esc == f"{pattern[:-1]}\\$"): elif last == '$' and (force_raw or esc == f"{pattern[:-1]}\\$"):
s_pattern = s_pattern[:-1] s_pattern = s_pattern[:-1]
return SimplePattern(lambda val: val.endswith(s_pattern), ignorecase=ignorecase) func = matcher_endswith
elif force_raw or esc == pattern: elif force_raw or esc == pattern:
return SimplePattern(lambda val: s_pattern in val, ignorecase=ignorecase) func = matcher_contains
return None else:
# Not a simple pattern
return None
return SimplePattern(matcher=func, pattern=s_pattern, ignorecase=ignorecase)