from itertools import chain, tee from bcrypter.lib.result import Result from bcrypter.cli.opt import Overlap class Command: NAME = '' # intentionally left empty (invalid name) ARGS = [] FLAGS = [] OPTIONS = [] def __init__(self, args: list[string]) -> None: pass @classmethod def parse(cls: Command, cmd: list[str]) -> Result[Command]: for arg in cmd[1:]: # flag or option if arg.startswith('--'): match = cls._match_arg(arg[2:]) if match ''' Check whether FLAGS and OPTIONS are defined consistently (ie no duplicate names or parsing ambiguity) ''' @classmethod def _is_well_defined(cls: Command) -> Result[list[Overlap]]: overlaps: list[Overlap] = [] opts = chain(cls.FLAGS, cls.OPTIONS) # Detect duplicate names and duplicate short/longforms for optX in opts: overlap = Overlap.empty(optX) other_opts = tee(opts) # duplicate the opts iterator for optY in other_opts: overlap += Overlap.of(optX, optY) # check if overlap occurred, add to list of all overlaps if not overlap.is_empty(): overlaps.append(overlap) if overlaps: return Result.fail(overlaps) return Result.succeed([]) ''' Attempt to match an arg to its flag or option NOTE: _match_arg() assumes _is_well_defined() == True ''' @classmethod def _match_arg(cls: Command, arg: str) -> Result[None]: for opt in chain(cls.FLAGS, cls.OPTIONS): if opt.matches(arg): return Result.succeed(None) return Result.fail() class Builtin(Command): NAME = '' # intentionally left empty (invalid name) ARGS = [] FLAGS = [] OPTIONS = [] def __init__(self, repl_builtins: list[Builtin], repl_cmds: list[Command]) -> None: super().__init__() self._repl_builtins = repl_builtins self._repl_cmds = repl_cmds