aboutsummaryrefslogtreecommitdiff
path: root/tools/cru-py/cru
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2024-11-11 01:12:29 +0800
committerYuqian Yang <crupest@crupest.life>2025-01-09 20:33:26 +0800
commitc761b945bc8ab0226576b75144440e1f619e69e8 (patch)
tree177071951d8bcd2de37876f875fdfed07a1dd809 /tools/cru-py/cru
parent47ebda69daa34ea7992b6bbadf46de98dd17a390 (diff)
downloadcrupest-c761b945bc8ab0226576b75144440e1f619e69e8.tar.gz
crupest-c761b945bc8ab0226576b75144440e1f619e69e8.tar.bz2
crupest-c761b945bc8ab0226576b75144440e1f619e69e8.zip
HALF WORK: 2024.1.9 - 2
Diffstat (limited to 'tools/cru-py/cru')
-rw-r--r--tools/cru-py/cru/config.py99
-rw-r--r--tools/cru-py/cru/service/_config.py78
-rw-r--r--tools/cru-py/cru/service/_template.py6
-rw-r--r--tools/cru-py/cru/value.py11
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())