Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add delegate command #95

Merged
merged 7 commits into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
315 changes: 4 additions & 311 deletions cmdcomp/v2/command/__init__.py
Original file line number Diff line number Diff line change
@@ -1,312 +1,5 @@
from abc import ABCMeta, abstractmethod
from functools import cached_property
from typing import Annotated, Literal, OrderedDict, TypeAlias
from .delegate_command import V2DelegateCommand
from .positional_arguments_command import V2PositionalArgumentsCommand
from .subcommands_command import V2SubcommandsCommand

from pydantic import ConfigDict, Field
from typing_extensions import override

from cmdcomp.model import Model
from cmdcomp.v2.command.argument.flag_argument import V2FlagArgument
from cmdcomp.v2.command.argument.select_argument import V2SelectArgument
from cmdcomp.v2.mixin.has_alias import HasAlias

from .argument import V2Argument

Position: TypeAlias = Annotated[int, Field(ge=1)]
Keyword = Annotated[str, Field(pattern=r"^--?[a-zA-Z0-9_-]+$")]
SubcommandName: TypeAlias = str
_InputArgument = str | list[str] | V2Argument


class _V2BaseCommand(HasAlias, Model, metaclass=ABCMeta):
model_config = ConfigDict(arbitrary_types_allowed=True)

description: Annotated[
str | None,
Field(title="description of the command."),
] = None

alias: Annotated[
str | list[str] | None,
Field(title="alias of the command."),
] = None

@cached_property
def subcommand_names_with_alias(self) -> list[SubcommandName]:
result: list[SubcommandName] = []
for subcommand_name, subcommand in self.subcommands.items():
result.append(subcommand_name)
result.extend(subcommand.aliases)

return result

@cached_property
def keyword_names_with_alias(self) -> list[Keyword]:
result: list[Keyword] = []
for keyword, argument in self.keyword_arguments.items():
result.append(keyword)
result.extend(argument.aliases)

return result

@property
@abstractmethod
def subcommands(self) -> OrderedDict[SubcommandName, "V2Command"]:
...

@property
@abstractmethod
def positional_arguments(self) -> OrderedDict[Position, V2Argument]:
...

@property
@abstractmethod
def positional_wildcard_argument(self) -> V2Argument | None:
...

@property
@abstractmethod
def keyword_arguments(self) -> OrderedDict[Keyword, V2Argument]:
...

@property
@abstractmethod
def has_subcommands(self) -> bool:
...

@property
@abstractmethod
def has_positional_arguments(self) -> bool:
...

@property
@abstractmethod
def has_positional_wildcard_argument(self) -> bool:
...

@property
@abstractmethod
def has_keyword_arguments(self) -> bool:
...


class _V2EmptyCommand(_V2BaseCommand):
@property
@override
def subcommands(self) -> OrderedDict[SubcommandName, "V2Command"]:
return OrderedDict()

@property
@override
def positional_arguments(self) -> OrderedDict[Position, V2Argument]:
return OrderedDict()

@property
@override
def positional_wildcard_argument(self) -> V2Argument | None:
return None

@property
@override
def keyword_arguments(self) -> OrderedDict[Keyword, V2Argument]:
return OrderedDict()

@property
@override
def has_subcommands(self) -> bool:
return False

@property
@override
def has_positional_arguments(self) -> bool:
return False

@property
@override
def has_positional_wildcard_argument(self) -> bool:
return False

@property
@override
def has_keyword_arguments(self) -> bool:
return False


class V2PoristionalArgumentsCommand(_V2BaseCommand):
arguments: Annotated[
OrderedDict[Position | Literal["*"] | Keyword, _InputArgument | None],
Field(
title="arguments of the command.",
description=(
"argment key allow "
"positional integer (like 1, 2), "
'keyword string (like "--f", "-f"), '
'wildcard string ("*").'
),
),
]

@property
@override
def subcommands(self) -> OrderedDict[SubcommandName, "V2Command"]:
return OrderedDict()

@cached_property
@override
def positional_arguments(self) -> OrderedDict[Position, V2Argument]:
return OrderedDict(
[
(k, _convert_argument(v))
for k, v in self.arguments.items()
if isinstance(k, int)
]
)

@cached_property
@override
def positional_wildcard_argument(self) -> V2Argument | None:
if "*" in self.arguments:
return _convert_argument(self.arguments["*"])
else:
return None

@cached_property
@override
def keyword_arguments(self) -> OrderedDict[Keyword, V2Argument]:
return OrderedDict(
[
(k, _convert_argument(v))
for k, v in self.arguments.items()
if isinstance(k, str) and k != "*"
]
)

@property
@override
def has_subcommands(self) -> bool:
return False

@property
@override
def has_positional_arguments(self) -> bool:
return len(self.positional_arguments) != 0

@property
@override
def has_positional_wildcard_argument(self) -> bool:
return self.positional_wildcard_argument is not None

@property
@override
def has_keyword_arguments(self) -> bool:
return len(self.keyword_arguments) != 0


class V2SubcommandsCommand(_V2BaseCommand):
arguments: OrderedDict[Keyword, _InputArgument | None] = Field(
title="arguments of the command.",
description='argment key allow keyword string (like "--f", "-f") only.',
default_factory=OrderedDict,
)

raw_subcommands: OrderedDict[SubcommandName, "V2Command | None"] = Field(
title="subcommands of the command.",
alias="subcommands",
default_factory=OrderedDict,
)

@property
@override
def positional_arguments(self) -> OrderedDict[Position, V2Argument]:
return OrderedDict()

@property
@override
def positional_wildcard_argument(self) -> V2Argument | None:
return None

@cached_property
@override
def keyword_arguments(self) -> OrderedDict[Keyword, V2Argument]:
return OrderedDict(
[
(k, _convert_argument(v))
for k, v in self.arguments.items()
if isinstance(k, str)
]
)

@cached_property
@override
def subcommands(self) -> OrderedDict[SubcommandName, "V2Command"]:
return OrderedDict(
[
(
name,
V2SubcommandsCommand() if subcommand is None else subcommand,
)
for name, subcommand in self.raw_subcommands.items()
]
)

@property
@override
def has_subcommands(self) -> bool:
return len(self.subcommands) != 0

@property
@override
def has_positional_arguments(self) -> bool:
return False

@property
@override
def has_positional_wildcard_argument(self) -> bool:
return self.positional_wildcard_argument is not None

@property
@override
def has_keyword_arguments(self) -> bool:
return len(self.keyword_arguments) != 0


class V2RelayCommand(_V2EmptyCommand):
"""relay completion of other command."""

type: Annotated[
Literal["relay"],
Field(title="relay completion of other command."),
]

description: Annotated[
str | None,
Field(title="description of the argument."),
] = None

alias: Annotated[
str | list[str] | None,
Field(title="alias of the argument."),
] = None

target: Annotated[str, Field(title="relay target.")]


V2Command = V2PoristionalArgumentsCommand | V2SubcommandsCommand | V2RelayCommand


def _convert_argument(value: _InputArgument | None) -> V2Argument:
match value:
case str():
return V2SelectArgument(type="select", raw_options=[value])

case list():
return V2SelectArgument(
type="select",
raw_options=[v for v in value],
)

case None:
return V2FlagArgument(type="flag")

case _:
return value
V2Command = V2PositionalArgumentsCommand | V2SubcommandsCommand | V2DelegateCommand
Loading
Loading