bcrypt.ctf/bcrypter/cli/cmd.py

66 lines
2 KiB
Python
Raw Normal View History

from itertools import chain, tee
2025-06-21 21:29:00 +10:00
from bcrypter.lib.result import Result
from bcrypter.cli.opt import Overlap
2025-06-21 21:29:00 +10:00
class Command:
NAME = '' # intentionally left empty (invalid name)
2025-06-21 21:29:00 +10:00
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([])
2025-06-21 21:29:00 +10:00
'''
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 = []
2025-06-21 21:29:00 +10:00
def __init__(self,
repl_builtins: list[Builtin],
repl_cmds: list[Command]) -> None:
super().__init__()
self._repl_builtins = repl_builtins
self._repl_cmds = repl_cmds