diff options
| -rw-r--r-- | tools/cru-py/cru/service/__main__.py | 54 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_base.py | 50 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_config.py | 2 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_data.py | 2 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_template.py | 9 | 
5 files changed, 105 insertions, 12 deletions
| diff --git a/tools/cru-py/cru/service/__main__.py b/tools/cru-py/cru/service/__main__.py index a7add4d..923c25b 100644 --- a/tools/cru-py/cru/service/__main__.py +++ b/tools/cru-py/cru/service/__main__.py @@ -1,11 +1,51 @@ -import argparse +from pathlib import Path +from cru import CruException -arg_parser = argparse.ArgumentParser(description="Service management") -command_subparser = arg_parser.add_subparsers(dest="command") +from ._base import AppBase, DATA_DIR_NAME, CommandDispatcher +from ._config import ConfigManager +from ._data import DataManager +from ._template import TemplateManager -template_parser = command_subparser.add_parser("template", help="Template management") -template_subparser = template_parser.add_subparsers(dest="template_command") -template_subparser.add_parser('list', description="List templates") -template_subparser.add_parser('generate') +class App(AppBase): +    def __init__(self, root: str): +        super().__init__("crupest-service", root) +        self.add_feature(DataManager()) +        self.add_feature(ConfigManager()) +        self.add_feature(TemplateManager()) +        self.add_feature(CommandDispatcher()) + +    def setup(self): +        for feature in self.features: +            feature.setup() + +    def run_command(self): +        command_dispatcher = self.get_feature(CommandDispatcher) +        command_dispatcher.run_command() + + +def _find_root() -> Path: +    cwd = Path.cwd() +    data_dir = cwd / DATA_DIR_NAME +    if data_dir.is_dir(): +        return data_dir +    raise CruException( +        "No valid data directory found. Please run 'init' to create one." +    ) + + +def create_app() -> App: +    root = _find_root() +    app = App(str(root)) +    app.setup() +    return app + + +def main(): +    app = create_app() +    app.run_command() + + +if __name__ == "__main__": +    main() diff --git a/tools/cru-py/cru/service/_base.py b/tools/cru-py/cru/service/_base.py index 98eed89..3b511a1 100644 --- a/tools/cru-py/cru/service/_base.py +++ b/tools/cru-py/cru/service/_base.py @@ -2,6 +2,8 @@ from __future__ import annotations  from argparse import ArgumentParser, Namespace  from abc import ABC, abstractmethod +import argparse +from collections.abc import Sequence  import os  from pathlib import Path  from typing import TypeVar, overload @@ -12,6 +14,7 @@ _F = TypeVar("_F")  OWNER_NAME = "crupest" +  class InternalAppException(CruInternalError):      pass @@ -153,7 +156,7 @@ class AppRootPath(AppPath):          return self._app -class AppFeatureProvider: +class AppFeatureProvider(ABC):      def __init__(self, name: str, /, app: AppBase | None = None):          super().__init__()          self._name = name @@ -168,15 +171,56 @@ class AppFeatureProvider:      def name(self) -> str:          return self._name +    @abstractmethod +    def setup(self) -> None: ... + + +class AppCommandFeatureProvider(AppFeatureProvider): +    @abstractmethod +    def get_command_info(self) -> tuple[str, str]: ... -class AppCommandFeatureProvider(AppFeatureProvider, ABC):      @abstractmethod -    def add_arg_parser(self, arg_parser: ArgumentParser) -> None: ... +    def setup_arg_parser(self, arg_parser: ArgumentParser): ...      @abstractmethod      def run_command(self, args: Namespace) -> None: ... +DATA_DIR_NAME = "data" + + +class CommandDispatcher(AppFeatureProvider): +    def __init__(self) -> None: +        super().__init__("command-dispatcher") + +    def _setup_arg_parser(self) -> None: +        self._map: dict[str, AppCommandFeatureProvider] = {} +        arg_parser = argparse.ArgumentParser(description="Service management") +        subparsers = arg_parser.add_subparsers(dest="command") +        for feature in self.app.features: +            if isinstance(feature, AppCommandFeatureProvider): +                info = feature.get_command_info() +                command_subparser = subparsers.add_parser(info[0], help=info[1]) +                feature.setup_arg_parser(command_subparser) +                self._map[info[0]] = feature +        self._arg_parser = arg_parser + +    def setup(self): +        self._setup_arg_parser() + +    @property +    def arg_parser(self) -> argparse.ArgumentParser: +        return self._arg_parser + +    @property +    def map(self) -> dict[str, AppCommandFeatureProvider]: +        return self._map + +    def run_command(self, _args: Sequence[str] | None = None) -> None: +        args = self.arg_parser.parse_args(_args) +        self.map[args.command].run_command(args) + +  class AppBase:      _instance: AppBase | None = None diff --git a/tools/cru-py/cru/service/_config.py b/tools/cru-py/cru/service/_config.py index 63b73b3..1c3a571 100644 --- a/tools/cru-py/cru/service/_config.py +++ b/tools/cru-py/cru/service/_config.py @@ -5,6 +5,8 @@ from ._data import DataManager  class ConfigManager(AppFeatureProvider):      def __init__(self) -> None:          super().__init__("config-manager") + +    def setup(self) -> None:          self._config_path = self.app.get_feature(DataManager).data_dir.add_subpath(              "config", False, description="Configuration file path."          ) diff --git a/tools/cru-py/cru/service/_data.py b/tools/cru-py/cru/service/_data.py index 3cddfc8..79a1f64 100644 --- a/tools/cru-py/cru/service/_data.py +++ b/tools/cru-py/cru/service/_data.py @@ -4,6 +4,8 @@ from ._base import AppFeaturePath, AppFeatureProvider  class DataManager(AppFeatureProvider):      def __init__(self) -> None:          super().__init__("data-manager") + +    def setup(self) -> None:          self._dir = self.app.add_path("data", True)      @property diff --git a/tools/cru-py/cru/service/_template.py b/tools/cru-py/cru/service/_template.py index 5f0252a..23bff4d 100644 --- a/tools/cru-py/cru/service/_template.py +++ b/tools/cru-py/cru/service/_template.py @@ -1,4 +1,4 @@ -from argparse import ArgumentParser, Namespace +from argparse import Namespace  from cru import CruIterator  from cru.template import TemplateTree @@ -11,6 +11,8 @@ class TemplateManager(AppCommandFeatureProvider):      def __init__(self, prefix: str = OWNER_NAME.upper()):          super().__init__("template-manager")          self._prefix = prefix + +    def setup(self) -> None:          self._templates_dir = self.app.add_path("templates", True)          self._generated_dir = self.app.add_path("generated", True)          self._template_tree: TemplateTree | None = None @@ -56,7 +58,10 @@ class TemplateManager(AppCommandFeatureProvider):              self.generated_dir.full_path_str, config_manager.config_map          ) -    def add_arg_parser(self, arg_parser: ArgumentParser) -> None: +    def get_command_info(self): +        return ("template", "Template Management") + +    def setup_arg_parser(self, arg_parser):          subparsers = arg_parser.add_subparsers(dest="template_command")          _list_parser = subparsers.add_parser("list", help="List templates.")          _generate_parser = subparsers.add_parser("generate", help="Generate template.") | 
