diff options
author | crupest <crupest@outlook.com> | 2024-11-11 01:12:29 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2025-01-11 17:01:20 +0800 |
commit | b9788b23bbd000d2a2d14402b4e2abacd5a57365 (patch) | |
tree | a5cdbb5fb3ac2ff609efc822e294182dcaa0876d | |
parent | 0d7dd933dfab91faf1a42b7883eb059d7fb31d80 (diff) | |
download | crupest-b9788b23bbd000d2a2d14402b4e2abacd5a57365.tar.gz crupest-b9788b23bbd000d2a2d14402b4e2abacd5a57365.tar.bz2 crupest-b9788b23bbd000d2a2d14402b4e2abacd5a57365.zip |
HALF WORK: 2024.1.11 - 2
-rw-r--r-- | tools/cru-py/cru/parsing.py | 7 | ||||
-rw-r--r-- | tools/cru-py/cru/service/_config.py | 97 | ||||
-rw-r--r-- | tools/cru-py/cru/service/_template.py | 2 |
3 files changed, 99 insertions, 7 deletions
diff --git a/tools/cru-py/cru/parsing.py b/tools/cru-py/cru/parsing.py index 5049a33..598a2bb 100644 --- a/tools/cru-py/cru/parsing.py +++ b/tools/cru-py/cru/parsing.py @@ -4,7 +4,7 @@ from abc import ABCMeta, abstractmethod from typing import NamedTuple, TypeAlias, TypeVar, Generic, NoReturn, Callable from ._error import CruException -from ._iter import CruIterable +from ._iter import CruIterable _T = TypeVar("_T") @@ -62,9 +62,8 @@ class SimpleLineConfigParserItem(NamedTuple): line_number: int | None = None -SimpleLineConfigParserResult: TypeAlias = CruIterable.IterList[ - SimpleLineConfigParserItem -] +class SimpleLineConfigParserResult(CruIterable.IterList[SimpleLineConfigParserItem]): + pass class SimpleLineConfigParser(Parser[SimpleLineConfigParserResult]): diff --git a/tools/cru-py/cru/service/_config.py b/tools/cru-py/cru/service/_config.py index 018b45b..6e1edda 100644 --- a/tools/cru-py/cru/service/_config.py +++ b/tools/cru-py/cru/service/_config.py @@ -1,3 +1,4 @@ +from cru import CruException, CruUserFriendlyException from cru.config import Configuration, ConfigItem from cru.value import ( INTEGER_VALUE_TYPE, @@ -7,10 +8,42 @@ from cru.value import ( ) from cru.parsing import SimpleLineConfigParser -from ._base import AppFeaturePath, AppFeatureProvider, OWNER_NAME +from ._base import AppFeaturePath, AppCommandFeatureProvider, OWNER_NAME -class ConfigManager(AppFeatureProvider): +class AppConfigError(CruException): + pass + + +class AppConfigDuplicateItemsError(AppConfigError): + def __init__( + self, message: str, items: list[SimpleLineConfigParser.Item], *args, **kwargs + ) -> None: + super().__init__(message, *args, **kwargs) + self._items = items + + @property + def duplicate_items(self) -> list[SimpleLineConfigParser.Item]: + return self._items + + @staticmethod + def duplicate_items_to_friendly_message( + items: list[SimpleLineConfigParser.Item], + ) -> str: + return "".join( + f"line {item.line_number}: {item.key}={item.value}\n" for item in items + ) + + def to_friendly_error(self) -> CruUserFriendlyException: + e = CruUserFriendlyException( + f"Duplicate configuration items detected:\n" + f"{self.duplicate_items_to_friendly_message(self.duplicate_items)}" + ) + e.__cause__ = self + return e + + +class ConfigManager(AppCommandFeatureProvider): def __init__(self) -> None: super().__init__("config-manager") configuration = Configuration() @@ -99,6 +132,36 @@ class ConfigManager(AppFeatureProvider): def config_map(self) -> dict[str, str]: raise NotImplementedError() + def _parse_config_file(self) -> SimpleLineConfigParser.Result | None: + if not self.config_file_path.check_self(): + return None + parser = SimpleLineConfigParser() + return parser.parse(self.config_file_path.full_path.read_text()) + + def _check_duplicate( + self, + result: SimpleLineConfigParser.Result + | dict[str, list[SimpleLineConfigParser.Item]], + ) -> dict[str, str]: + if isinstance(result, SimpleLineConfigParser.Result): + result = result.cru_iter().group_by(lambda i: i.key) + + config = {} + error_items = [] + for key, items in result.items(): + config[key] = items[0].value + for item in items[1:]: + error_items.append(item) + + if len(error_items) > 0: + raise AppConfigDuplicateItemsError("Duplicate items found.", error_items) + + return config + + def _check_config_file(self) -> dict[str, str]: + # TODO: Continue here! + raise NotImplementedError() + def reload_config_file(self) -> bool: self.configuration.reset_all() if not self.config_file_path.check_self(): @@ -107,3 +170,33 @@ class ConfigManager(AppFeatureProvider): parse_result = parser.parse(self.config_file_path.full_path.read_text()) config_dict = parse_result.cru_iter().group_by(lambda i: i.key) return True + + def print_app_config_info(self): + for item in self.configuration: + print(f"{item.name} ({item.value_type.name}): {item.description}") + + def get_command_info(self): + return "config", "Manage configuration." + + def setup_arg_parser(self, arg_parser) -> None: + subparsers = arg_parser.add_subparsers(dest="config_command") + _print_app_parser = subparsers.add_parser( + "print-app", + help="Print application configuration information " + "of the items defined in the application.", + ) + _check_config_parser = subparsers.add_parser( + "check", + help="Check the validity of the configuration file.", + ) + _check_config_parser.add_argument( + "-f", + "--format-only", + action="store_true", + help="Only check content format, not " + "for application configuration requirements.", + ) + + def run_command(self, args) -> None: + if args.config_command == "print-app": + self.print_app_config_info() diff --git a/tools/cru-py/cru/service/_template.py b/tools/cru-py/cru/service/_template.py index 3ffb15e..cc7fddf 100644 --- a/tools/cru-py/cru/service/_template.py +++ b/tools/cru-py/cru/service/_template.py @@ -59,7 +59,7 @@ class TemplateManager(AppCommandFeatureProvider): ) def get_command_info(self): - return ("template", "Template Management") + return ("template", "Manage templates.") def setup_arg_parser(self, arg_parser): subparsers = arg_parser.add_subparsers(dest="template_command") |