diff options
Diffstat (limited to 'tools/cru-py/cru')
| -rw-r--r-- | tools/cru-py/cru/config.py | 99 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_config.py | 78 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_template.py | 6 | ||||
| -rw-r--r-- | tools/cru-py/cru/value.py | 11 | 
4 files changed, 164 insertions, 30 deletions
| diff --git a/tools/cru-py/cru/config.py b/tools/cru-py/cru/config.py index 3fd994b..926ed6a 100644 --- a/tools/cru-py/cru/config.py +++ b/tools/cru-py/cru/config.py @@ -1,10 +1,12 @@ -from typing import TypeVar, Generic -import copy +from __future__ import annotations +from typing import TypeVar, Generic +from ._error import CruInternalError, CruException  from .list import CruUniqueKeyList -from ._error import CruInternalError  from .value import ( +    INTEGER_VALUE_TYPE, +    TEXT_VALUE_TYPE,      CruValueTypeError,      ValueGeneratorBase,      ValueType, @@ -13,23 +15,32 @@ from .value import (  _T = TypeVar("_T") +class CruConfigError(CruException): +    def __init__(self, message: str, item: ConfigItem, *args, **kwargs): +        super().__init__(message, *args, **kwargs) +        self._item = item + +    @property +    def item(self) -> ConfigItem: +        return self._item + +  class ConfigItem(Generic[_T]):      def __init__(          self,          name: str,          description: str,          value_type: ValueType[_T], -        value: _T | None, -        default_value: _T, -        *, -        value_generator: ValueGeneratorBase | None = None, +        value: _T | None = None, +        /, +        default: ValueGeneratorBase[_T] | _T | None = None,      ) -> None:          self._name = name          self._description = description          self._value_type = value_type -        self._default_value = default_value -        self._value_generator = value_generator          self._value = value +        self._default = default +        self._default_value: _T | None = None      @property      def name(self) -> str: @@ -44,24 +55,32 @@ class ConfigItem(Generic[_T]):          return self._value_type      @property -    def default_value(self) -> _T: -        return self._default_value +    def is_set(self) -> bool: +        return self._value is not None      @property -    def is_default(self) -> bool: -        return self._value is None +    def value(self) -> _T: +        if self._value is None: +            raise CruConfigError("Config value is not set.", self) +        return self._value      @property -    def is_set(self) -> bool: -        return not self.is_default +    def value_or_default(self) -> _T: +        if self._value is not None: +            return self._value +        elif self._default_value is not None: +            return self._default_value +        else: +            self._default_value = self.generate_default_value() +            return self._default_value      @property -    def value(self) -> _T: -        return self._value or self._default_value +    def default(self) -> ValueGeneratorBase[_T] | _T | None: +        return self._default      @property -    def value_generator(self) -> ValueGeneratorBase | None: -        return self._value_generator +    def can_generate_default(self) -> bool: +        return self.default is not None      def set_value(self, v: _T | str, /, allow_convert_from_str=False):          if allow_convert_from_str: @@ -69,16 +88,21 @@ class ConfigItem(Generic[_T]):          else:              self._value = self.value_type.check_value_or_try_convert_from_str(v) -    def generate_value(self) -> _T | None: -        if self.value_generator is None: -            return None -        v = self.generate_value() +    def generate_default_value(self) -> _T: +        if self.default is None: +            raise CruConfigError( +                "Config item does not support default value generation.", self +            ) +        elif isinstance(self.default, ValueGeneratorBase): +            v = self.default.generate() +        else: +            v = self.default          try:              self.value_type.check_value(v)              return v          except CruValueTypeError as e:              raise CruInternalError( -                "Config value generator returns invalid value." +                "Config value generator returns an invalid value."              ) from e      def copy(self) -> "ConfigItem": @@ -86,12 +110,33 @@ class ConfigItem(Generic[_T]):              self.name,              self.description,              self.value_type, -            copy.deepcopy(self._value) if self._value is not None else None, -            copy.deepcopy(self._default_value), -            value_generator=self.value_generator, +            self.value, +            self.default,          )  class Configuration(CruUniqueKeyList[ConfigItem, str]):      def __init__(self):          super().__init__(lambda c: c.name) + +    def add_text_config( +        self, +        name: str, +        description: str, +        value: str | None = None, +        default: ValueGeneratorBase[str] | str | None = None, +    ) -> ConfigItem: +        item = ConfigItem(name, description, TEXT_VALUE_TYPE, value, default) +        self.add(item) +        return item + +    def add_int_config( +        self, +        name: str, +        description: str, +        value: int | None = None, +        default: ValueGeneratorBase[int] | int | None = None, +    ) -> ConfigItem: +        item = ConfigItem(name, description, INTEGER_VALUE_TYPE, value, default) +        self.add(item) +        return item diff --git a/tools/cru-py/cru/service/_config.py b/tools/cru-py/cru/service/_config.py index 3bfb6c9..b5f3e7c 100644 --- a/tools/cru-py/cru/service/_config.py +++ b/tools/cru-py/cru/service/_config.py @@ -1,9 +1,81 @@ -from ._base import AppFeaturePath, AppFeatureProvider +from cru.config import Configuration, ConfigItem +from cru.value import ( +    INTEGER_VALUE_TYPE, +    TEXT_VALUE_TYPE, +    RandomStringValueGenerator, +    UuidValueGenerator, +) + +from ._base import AppFeaturePath, AppFeatureProvider, OWNER_NAME  class ConfigManager(AppFeatureProvider):      def __init__(self) -> None:          super().__init__("config-manager") +        configuration = Configuration() +        self._configuration = configuration +        self._add_text_item("DOMAIN", "domain name") +        self._add_text_item("EMAIL", "admin email address") +        self._add_text_item( +            "AUTO_BACKUP_COS_SECRET_ID", +            "access key id for Tencent COS, used for auto backup", +        ) +        self._add_text_item( +            "AUTO_BACKUP_COS_SECRET_KEY", +            "access key secret for Tencent COS, used for auto backup", +        ) +        self._add_text_item( +            "AUTO_BACKUP_COS_REGION", "region for Tencent COS, used for auto backup" +        ) +        self._add_text_item( +            "AUTO_BACKUP_BUCKET_NAME", +            "bucket name for Tencent COS, used for auto backup", +        ) +        self._add_text_item("GITHUB_USERNAME", "github username for fetching todos") +        self._add_int_item( +            "GITHUB_PROJECT_NUMBER", "github project number for fetching todos" +        ) +        self._add_text_item("GITHUB_TOKEN", "github token for fetching todos") +        self._add_text_item("GITHUB_TODO_COUNT", "github todo count") +        self._add_uuid_item("V2RAY_TOKEN", "v2ray user id") +        self._add_uuid_item("V2RAY_PATH", "v2ray path, which will be prefixed by _") +        self._add_text_item("FORGEJO_MAILER_USER", "Forgejo SMTP user") +        self._add_text_item("FORGEJO_MAILER_PASSWD", "Forgejo SMTP password") +        self._add_random_string_item("2FAUTH_APP_KEY", "2FAuth App Key") +        self._add_text_item("2FAUTH_MAIL_USERNAME", "2FAuth SMTP user") +        self._add_text_item("2FAUTH_MAIL_PASSWORD", "2FAuth SMTP password") + +    def _add_text_item(self, name: str, description: str) -> None: +        self.configuration.add( +            ConfigItem(f"{OWNER_NAME}_{name}", description, TEXT_VALUE_TYPE) +        ) + +    def _add_uuid_item(self, name: str, description: str) -> None: +        self.configuration.add( +            ConfigItem( +                f"{OWNER_NAME}_{name}", +                description, +                TEXT_VALUE_TYPE, +                default=UuidValueGenerator(), +            ) +        ) + +    def _add_random_string_item( +        self, name: str, description: str, length: int = 32, secure: bool = True +    ) -> None: +        self.configuration.add( +            ConfigItem( +                f"{OWNER_NAME}_{name}", +                description, +                TEXT_VALUE_TYPE, +                default=RandomStringValueGenerator(length, secure), +            ) +        ) + +    def _add_int_item(self, name: str, description: str) -> None: +        self.configuration.add( +            ConfigItem(f"{OWNER_NAME}_{name}", description, INTEGER_VALUE_TYPE) +        )      def setup(self) -> None:          self._config_path = self.app.data_dir.add_subpath( @@ -15,5 +87,9 @@ class ConfigManager(AppFeatureProvider):          return self._config_path      @property +    def configuration(self) -> Configuration: +        return self._configuration + +    @property      def config_map(self) -> dict[str, str]:          raise NotImplementedError() diff --git a/tools/cru-py/cru/service/_template.py b/tools/cru-py/cru/service/_template.py index 23bff4d..3ffb15e 100644 --- a/tools/cru-py/cru/service/_template.py +++ b/tools/cru-py/cru/service/_template.py @@ -64,10 +64,16 @@ class TemplateManager(AppCommandFeatureProvider):      def setup_arg_parser(self, arg_parser):          subparsers = arg_parser.add_subparsers(dest="template_command")          _list_parser = subparsers.add_parser("list", help="List templates.") +        _variables_parser = subparsers.add_parser( +            "variables", help="List variables for a specific template." +        )          _generate_parser = subparsers.add_parser("generate", help="Generate template.")      def run_command(self, args: Namespace) -> None:          if args.template_command == "list":              self.print_file_lists() +        elif args.template_command == "variables": +            for var in self.template_tree.variables: +                print(var)          elif args.template_command == "generate":              self.generate_files() diff --git a/tools/cru-py/cru/value.py b/tools/cru-py/cru/value.py index 4096362..9c03219 100644 --- a/tools/cru-py/cru/value.py +++ b/tools/cru-py/cru/value.py @@ -105,6 +105,9 @@ class ValueType(Generic[_T], metaclass=ABCMeta):              else:                  raise +    def create_default_value(self) -> _T: +        return self.type() +  class TextValueType(ValueType[str]):      def __init__(self) -> None: @@ -121,7 +124,6 @@ class TextValueType(ValueType[str]):  class IntegerValueType(ValueType[int]): -      def __init__(self) -> None:          super().__init__("integer", int) @@ -201,6 +203,9 @@ class BooleanValueType(ValueType[bool]):      def _do_convert_str_to_value(self, s):          return _str_case_in(s, self.case_sensitive, self._valid_true_strs) +    def create_default_value(self): +        return self.valid_false_strs[0] +  class EnumValueType(ValueType[str]):      def __init__(self, valid_values: list[str], /, case_sensitive=False) -> None: @@ -229,6 +234,9 @@ class EnumValueType(ValueType[str]):      def _do_convert_str_to_value(self, s):          return s +    def create_default_value(self): +        return self.valid_values[0] +  TEXT_VALUE_TYPE = TextValueType()  INTEGER_VALUE_TYPE = IntegerValueType() @@ -257,7 +265,6 @@ class ValueGenerator(ValueGeneratorBase[_T]):  class UuidValueGenerator(ValueGeneratorBase[str]): -      def generate(self):          return str(uuid.uuid4()) | 
