2020-02-15 23:31:55 +00:00
|
|
|
import enum, re, typing
|
2020-01-26 00:06:47 +00:00
|
|
|
from .time import duration
|
2020-01-27 11:57:29 +00:00
|
|
|
from .types import try_int
|
2020-01-30 21:21:12 +00:00
|
|
|
from src.utils.datetime.parse import date_human
|
2020-01-26 00:06:47 +00:00
|
|
|
|
2020-02-14 22:03:53 +00:00
|
|
|
class SpecTypeError(Exception):
|
|
|
|
def __init__(self, message: str, arg_count: int=1):
|
|
|
|
self.message = message
|
|
|
|
self.arg_count = arg_count
|
|
|
|
|
2020-01-26 00:06:47 +00:00
|
|
|
class SpecArgumentContext(enum.IntFlag):
|
|
|
|
CHANNEL = 1
|
|
|
|
PRIVATE = 2
|
|
|
|
ALL = 3
|
|
|
|
|
|
|
|
class SpecArgumentType(object):
|
|
|
|
context = SpecArgumentContext.ALL
|
|
|
|
|
2020-01-26 02:09:48 +00:00
|
|
|
def __init__(self, type_name: str, name: typing.Optional[str],
|
2020-02-15 23:31:55 +00:00
|
|
|
modifier: typing.Optional[str], exported: typing.Optional[str]):
|
2020-01-26 00:06:47 +00:00
|
|
|
self.type = type_name
|
|
|
|
self._name = name
|
2020-02-15 23:31:55 +00:00
|
|
|
self._set_modifier(modifier)
|
2020-01-26 00:06:47 +00:00
|
|
|
self.exported = exported
|
|
|
|
|
2020-02-15 23:31:55 +00:00
|
|
|
def _set_modifier(self, modifier: str):
|
|
|
|
pass
|
|
|
|
|
2020-01-26 00:06:47 +00:00
|
|
|
def name(self) -> typing.Optional[str]:
|
|
|
|
return self._name
|
|
|
|
def simple(self, args: typing.List[str]) -> typing.Tuple[typing.Any, int]:
|
|
|
|
return None, -1
|
|
|
|
def error(self) -> typing.Optional[str]:
|
|
|
|
return None
|
|
|
|
|
2020-02-15 23:31:55 +00:00
|
|
|
class SpecArgumentTypePattern(SpecArgumentType):
|
|
|
|
_pattern: typing.Pattern
|
|
|
|
def _set_modifier(self, modifier):
|
|
|
|
print(modifier)
|
|
|
|
self._pattern = re.compile(modifier)
|
|
|
|
def simple(self, args):
|
|
|
|
match = self._pattern.search(" ".join(args))
|
|
|
|
if match:
|
|
|
|
return match, match.group(0).rstrip(" ").count(" ")
|
|
|
|
else:
|
|
|
|
return None, 1
|
|
|
|
|
2020-01-26 00:06:47 +00:00
|
|
|
class SpecArgumentTypeWord(SpecArgumentType):
|
2020-02-14 21:57:06 +00:00
|
|
|
def simple(self, args):
|
2020-01-26 00:06:47 +00:00
|
|
|
if args:
|
|
|
|
return args[0], 1
|
|
|
|
return None, 1
|
2020-01-27 11:56:28 +00:00
|
|
|
class SpecArgumentTypeAdditionalWord(SpecArgumentType):
|
2020-02-14 21:57:06 +00:00
|
|
|
def simple(self, args):
|
2020-01-27 11:56:28 +00:00
|
|
|
if len(args) > 1:
|
|
|
|
return args[0], 1
|
|
|
|
return None, 1
|
2020-01-26 00:06:47 +00:00
|
|
|
class SpecArgumentTypeWordLower(SpecArgumentTypeWord):
|
2020-02-14 21:57:06 +00:00
|
|
|
def simple(self, args):
|
2020-01-26 00:06:47 +00:00
|
|
|
out = SpecArgumentTypeWord.simple(self, args)
|
|
|
|
if out[0]:
|
|
|
|
return out[0].lower(), out[1]
|
|
|
|
return out
|
|
|
|
|
|
|
|
class SpecArgumentTypeString(SpecArgumentType):
|
|
|
|
def name(self):
|
|
|
|
return "%s ..." % SpecArgumentType.name(self)
|
2020-02-14 21:57:06 +00:00
|
|
|
def simple(self, args):
|
2020-01-29 17:04:17 +00:00
|
|
|
if args:
|
|
|
|
return " ".join(args), len(args)
|
|
|
|
return None, 1
|
2020-01-26 11:21:23 +00:00
|
|
|
class SpecArgumentTypeTrimString(SpecArgumentTypeString):
|
2020-02-14 21:57:06 +00:00
|
|
|
def simple(self, args):
|
2020-01-26 11:21:23 +00:00
|
|
|
return SpecArgumentTypeString.simple(self, list(filter(None, args)))
|
2020-02-05 16:32:41 +00:00
|
|
|
class SpecArgumentTypeWords(SpecArgumentTypeString):
|
2020-02-14 21:57:06 +00:00
|
|
|
def simple(self, args):
|
2020-02-05 16:32:41 +00:00
|
|
|
if args:
|
|
|
|
out = list(filter(None, args))
|
|
|
|
return out, len(out)
|
|
|
|
return None, 1
|
2020-01-26 00:06:47 +00:00
|
|
|
|
2020-01-27 11:57:29 +00:00
|
|
|
class SpecArgumentTypeInt(SpecArgumentType):
|
|
|
|
def simple(self, args):
|
|
|
|
if args:
|
|
|
|
return try_int(args[0]), 1
|
|
|
|
return None, 1
|
|
|
|
|
2020-01-26 00:06:47 +00:00
|
|
|
class SpecArgumentTypeDuration(SpecArgumentType):
|
|
|
|
def name(self):
|
|
|
|
return "+%s" % (SpecArgumentType.name(self) or "duration")
|
2020-02-14 21:57:06 +00:00
|
|
|
def simple(self, args):
|
2020-01-26 00:06:47 +00:00
|
|
|
if args:
|
|
|
|
return duration(args[0]), 1
|
|
|
|
return None, 1
|
|
|
|
def error(self) -> typing.Optional[str]:
|
|
|
|
return "Invalid timeframe"
|
|
|
|
|
2020-01-30 21:21:12 +00:00
|
|
|
class SpecArgumentTypeDate(SpecArgumentType):
|
|
|
|
def name(self):
|
|
|
|
return SpecArgumentType.name(self) or "yyyy-mm-dd"
|
|
|
|
def simple(self, args):
|
|
|
|
if args:
|
2020-02-06 16:40:02 +00:00
|
|
|
return date_human(args[0]), 1
|
2020-01-30 21:21:12 +00:00
|
|
|
return None, 1
|
|
|
|
|
2020-01-26 00:06:47 +00:00
|
|
|
class SpecArgumentPrivateType(SpecArgumentType):
|
|
|
|
context = SpecArgumentContext.PRIVATE
|
|
|
|
|
|
|
|
SPEC_ARGUMENT_TYPES = {
|
|
|
|
"word": SpecArgumentTypeWord,
|
2020-01-27 11:56:28 +00:00
|
|
|
"aword": SpecArgumentTypeAdditionalWord,
|
2020-01-26 00:06:47 +00:00
|
|
|
"wordlower": SpecArgumentTypeWordLower,
|
|
|
|
"string": SpecArgumentTypeString,
|
2020-02-05 16:32:41 +00:00
|
|
|
"words": SpecArgumentTypeWords,
|
2020-01-26 11:21:23 +00:00
|
|
|
"tstring": SpecArgumentTypeTrimString,
|
2020-01-27 11:57:29 +00:00
|
|
|
"int": SpecArgumentTypeInt,
|
2020-01-30 21:21:12 +00:00
|
|
|
"date": SpecArgumentTypeDate,
|
2020-02-15 23:31:55 +00:00
|
|
|
"duration": SpecArgumentTypeDuration,
|
|
|
|
"pattern": SpecArgumentTypePattern
|
2020-01-26 00:06:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class SpecArgument(object):
|
2020-01-27 12:13:28 +00:00
|
|
|
consume = True
|
2020-01-26 02:01:18 +00:00
|
|
|
optional: bool = False
|
|
|
|
types: typing.List[SpecArgumentType] = []
|
2020-01-26 00:06:47 +00:00
|
|
|
|
2020-01-26 02:01:18 +00:00
|
|
|
@staticmethod
|
|
|
|
def parse(optional: bool, argument_types: typing.List[str]):
|
|
|
|
out: typing.List[SpecArgumentType] = []
|
|
|
|
for argument_type in argument_types:
|
2020-01-26 02:09:48 +00:00
|
|
|
exported = None
|
2020-01-26 00:06:47 +00:00
|
|
|
if "~" in argument_type:
|
|
|
|
exported = argument_type.split("~", 1)[1]
|
|
|
|
argument_type = argument_type.replace("~", "", 1)
|
|
|
|
|
|
|
|
argument_type_name: typing.Optional[str] = None
|
|
|
|
name_end = argument_type.find(">")
|
2020-02-15 23:31:55 +00:00
|
|
|
if name_end > 0 and argument_type.startswith("<"):
|
2020-01-26 00:06:47 +00:00
|
|
|
argument_type_name = argument_type[1:name_end]
|
|
|
|
argument_type = argument_type[name_end+1:]
|
|
|
|
|
2020-02-15 23:31:55 +00:00
|
|
|
argument_type_modifier: typing.Optional[str] = None
|
|
|
|
modifier_start = argument_type.find("(")
|
|
|
|
if modifier_start > 0 and argument_type.endswith(")"):
|
|
|
|
argument_type_modifier = argument_type[modifier_start+1:-1]
|
|
|
|
argument_type = argument_type[:modifier_start]
|
|
|
|
|
2020-01-26 00:06:47 +00:00
|
|
|
argument_type_class = SpecArgumentType
|
|
|
|
if argument_type in SPEC_ARGUMENT_TYPES:
|
|
|
|
argument_type_class = SPEC_ARGUMENT_TYPES[argument_type]
|
|
|
|
elif exported:
|
|
|
|
argument_type_class = SpecArgumentPrivateType
|
|
|
|
|
2020-01-26 02:01:18 +00:00
|
|
|
out.append(argument_type_class(argument_type,
|
2020-02-15 23:31:55 +00:00
|
|
|
argument_type_name, argument_type_modifier, exported))
|
2020-01-26 00:06:47 +00:00
|
|
|
|
2020-01-26 02:01:18 +00:00
|
|
|
spec_argument = SpecArgument()
|
|
|
|
spec_argument.optional = optional
|
|
|
|
spec_argument.types = out
|
|
|
|
return spec_argument
|
|
|
|
|
|
|
|
def format(self, context: SpecArgumentContext) -> typing.Optional[str]:
|
|
|
|
if self.optional:
|
|
|
|
format = "[%s]"
|
|
|
|
else:
|
|
|
|
format = "<%s>"
|
|
|
|
|
2020-01-26 00:06:47 +00:00
|
|
|
names: typing.List[str] = []
|
2020-01-26 02:01:18 +00:00
|
|
|
for argument_type in self.types:
|
2020-01-26 00:06:47 +00:00
|
|
|
if not (context&argument_type.context) == 0:
|
|
|
|
name = argument_type.name() or argument_type.type
|
|
|
|
if name:
|
|
|
|
names.append(name)
|
|
|
|
if names:
|
2020-01-26 02:01:18 +00:00
|
|
|
return format % "|".join(names)
|
|
|
|
return None
|
|
|
|
|
|
|
|
class SpecArgumentTypeLiteral(SpecArgumentType):
|
|
|
|
def simple(self, args: typing.List[str]) -> typing.Tuple[typing.Any, int]:
|
|
|
|
if args and args[0] == self.name():
|
|
|
|
return args[0], 1
|
|
|
|
return None, 1
|
|
|
|
def error(self) -> typing.Optional[str]:
|
|
|
|
return None
|
|
|
|
class SpecLiteralArgument(SpecArgument):
|
|
|
|
@staticmethod
|
|
|
|
def parse(optional: bool, literals: typing.List[str]) -> SpecArgument:
|
|
|
|
spec_argument = SpecLiteralArgument()
|
|
|
|
spec_argument.optional = optional
|
|
|
|
spec_argument.types = [
|
2020-02-15 23:31:55 +00:00
|
|
|
SpecArgumentTypeLiteral("literal", l, None, None) for l in literals]
|
2020-01-26 02:01:18 +00:00
|
|
|
return spec_argument
|
|
|
|
|
|
|
|
def format(self, context: SpecArgumentContext) -> typing.Optional[str]:
|
2020-01-26 02:09:48 +00:00
|
|
|
return "|".join(t.name() or "" for t in self.types)
|
2020-01-26 02:01:18 +00:00
|
|
|
|
|
|
|
def argument_spec(spec: str) -> typing.List[SpecArgument]:
|
|
|
|
out: typing.List[SpecArgument] = []
|
|
|
|
for spec_argument in spec.split(" "):
|
|
|
|
optional = spec_argument[0] == "?"
|
|
|
|
|
|
|
|
if spec_argument[1] == "'":
|
|
|
|
out.append(SpecLiteralArgument.parse(optional,
|
|
|
|
spec_argument[2:].split(",")))
|
|
|
|
else:
|
2020-01-27 12:13:28 +00:00
|
|
|
consume = True
|
2020-01-28 15:42:56 +00:00
|
|
|
if spec_argument[1] == "-":
|
2020-01-27 12:13:28 +00:00
|
|
|
consume = False
|
|
|
|
spec_argument = spec_argument[1:]
|
|
|
|
|
|
|
|
spec_argument_obj = SpecArgument.parse(optional,
|
|
|
|
spec_argument[1:].split("|"))
|
|
|
|
spec_argument_obj.consume = consume
|
|
|
|
out.append(spec_argument_obj)
|
2020-01-26 02:01:18 +00:00
|
|
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
def argument_spec_human(spec: typing.List[SpecArgument],
|
|
|
|
context: SpecArgumentContext=SpecArgumentContext.ALL) -> str:
|
|
|
|
arguments: typing.List[str] = []
|
|
|
|
for spec_argument in spec:
|
2020-01-27 12:13:28 +00:00
|
|
|
if spec_argument.consume:
|
|
|
|
out = spec_argument.format(context)
|
|
|
|
if out:
|
|
|
|
arguments.append(out)
|
2020-01-26 02:01:18 +00:00
|
|
|
return " ".join(arguments)
|