diff options
author | crupest <crupest@outlook.com> | 2024-11-11 01:12:29 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2024-12-19 22:23:05 +0800 |
commit | 7e85627eb0d0126baf1e698ffdeae0f3c5fc693d (patch) | |
tree | acf21fbfe5dc96060ecdfc2ba33dacb337caf4dd /tools | |
parent | f9aa02ec1a4c24e80a206857d4f68198bb027bb4 (diff) | |
download | crupest-7e85627eb0d0126baf1e698ffdeae0f3c5fc693d.tar.gz crupest-7e85627eb0d0126baf1e698ffdeae0f3c5fc693d.tar.bz2 crupest-7e85627eb0d0126baf1e698ffdeae0f3c5fc693d.zip |
HALF WORK: 2024.12.19
code.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/cru-py/cru/service/_manager.py | 2 | ||||
-rw-r--r-- | tools/cru-py/cru/template.py | 174 |
2 files changed, 102 insertions, 74 deletions
diff --git a/tools/cru-py/cru/service/_manager.py b/tools/cru-py/cru/service/_manager.py new file mode 100644 index 0000000..45a9e47 --- /dev/null +++ b/tools/cru-py/cru/service/_manager.py @@ -0,0 +1,2 @@ +class CruServiceManager: + "TODO: Continue here tomorrow!" diff --git a/tools/cru-py/cru/template.py b/tools/cru-py/cru/template.py index 8e06418..2161804 100644 --- a/tools/cru-py/cru/template.py +++ b/tools/cru-py/cru/template.py @@ -3,6 +3,7 @@ import os import os.path from string import Template +from ._iter import CruIterator from ._error import CruException @@ -10,69 +11,103 @@ class CruTemplateError(CruException): pass -class TemplateFile: - def __init__(self, source: str, destination_path: str | None): - self._source = source - self._destination = destination_path - self._template: Template | None = None +class CruTemplate: + def __init__(self, prefix: str, text: str): + self._prefix = prefix + self._template = Template(text) + self._variables = ( + CruIterator(self._template.get_identifiers()) + .filter(lambda i: i.startswith(self._prefix)) + .to_set() + ) + self._all_variables = set(self._template.get_identifiers()) @property - def source(self) -> str: - return self._source + def prefix(self) -> str: + return self._prefix @property - def destination(self) -> str | None: - return self._destination + def real_template(self) -> Template: + return self._template + + @property + def variables(self) -> set[str]: + return self._variables - @destination.setter - def destination(self, value: str | None) -> None: - self._destination = value + @property + def all_variables(self) -> set[str]: + return self._all_variables @property - def template(self) -> Template: - if self._template is None: - return self.reload_template() - return self._template + def has_no_variables(self) -> bool: + return len(self._variables) == 0 + + def generate(self, mapping: Mapping[str, str], allow_extra: bool = True) -> str: + values = dict(mapping) + if not self.variables <= set(values.keys()): + raise CruTemplateError("Missing variables.") + if not allow_extra and not set(values.keys()) <= self.variables: + raise CruTemplateError("Extra variables.") + return self._template.safe_substitute(values) - def reload_template(self) -> Template: - with open(self._source, "r") as f: - self._template = Template(f.read()) - return self._template + +class CruTemplateFile(CruTemplate): + def __init__(self, prefix: str, source: str, destination_path: str): + self._source = source + self._destination = destination_path + with open(source, "r") as f: + super().__init__(prefix, f.read()) @property - def variables(self) -> set[str]: - return set(self.template.get_identifiers()) + def source(self) -> str: + return self._source - def generate(self, variables: Mapping[str, str]) -> str: - return self.template.substitute(variables) + @property + def destination(self) -> str | None: + return self._destination - def generate_to_destination(self, variables: Mapping[str, str]) -> None: - if self._destination is None: - raise CruTemplateError("No destination specified for this template.") + def generate_to_destination( + self, mapping: Mapping[str, str], allow_extra: bool = True + ) -> None: with open(self._destination, "w") as f: - f.write(self.generate(variables)) + f.write(self.generate(mapping, allow_extra)) -class TemplateDirectory: +class TemplateTree: def __init__( self, + prefix: str, source: str, destination: str, exclude: Iterable[str], - file_suffix: str = ".template", + template_file_suffix: str = ".template", ): - self._files: list[TemplateFile] | None = None + self._prefix = prefix + self._files: list[CruTemplateFile] | None = None self._source = source self._destination = destination self._exclude = [os.path.normpath(p) for p in exclude] - self._file_suffix = file_suffix + self._template_file_suffix = template_file_suffix @property - def files(self) -> list[TemplateFile]: + def prefix(self) -> str: + return self._prefix + + @property + def files(self) -> list[CruTemplateFile]: if self._files is None: - return self.reload() - else: - return self._files + self.reload() + return self._files # type: ignore + + @property + def template_files(self) -> list[CruTemplateFile]: + return ( + CruIterator(self.files).filter(lambda f: not f.has_no_variables).to_list() + ) + + @property + def non_template_files(self) -> list[CruTemplateFile]: + return CruIterator(self.files).filter(lambda f: f.has_no_variables).to_list() @property def source(self) -> str: @@ -87,42 +122,39 @@ class TemplateDirectory: return self._exclude @property - def file_suffix(self) -> str: - return self._file_suffix + def template_file_suffix(self) -> str: + return self._template_file_suffix @staticmethod - def _scan_files( - root_path: str, exclude: list[str], suffix: str | None - ) -> Iterable[str]: + def _scan_files(root_path: str, exclude: list[str]) -> Iterable[str]: for root, _dirs, files in os.walk(root_path): for file in files: - if suffix is None or file.endswith(suffix): - path = os.path.join(root, file) - path = os.path.relpath(path, root_path) - if suffix is not None: - path = path[: -len(suffix)] - is_exclude = False - for exclude_path in exclude: - if path.startswith(exclude_path): - is_exclude = True - break - if not is_exclude: - yield path - - def reload(self) -> list[TemplateFile]: - if not os.path.isdir(self.source): - raise CruTemplateError( - f"Source directory {self.source} does not exist or is not a directory." - ) - files = self._scan_files(self.source, self.exclude, self.file_suffix) - self._files = [ - TemplateFile( - os.path.join(self._source, file + self.file_suffix), - os.path.join(self._destination, file), - ) - for file in files - ] - return self._files + path = os.path.join(root, file) + path = os.path.relpath(path, root_path) + is_exclude = False + for exclude_path in exclude: + if path.startswith(exclude_path): + is_exclude = True + break + if not is_exclude: + yield path + + def reload(self, strict=True) -> None: + self._files = [] + file_names = self._scan_files(self.source, self.exclude) + for file_name in file_names: + source = os.path.join(self.source, file_name) + destination = os.path.join(self.destination, file_name) + file = CruTemplateFile(self._prefix, source, destination) + if file_name.endswith(self.template_file_suffix): + if strict and file.has_no_variables: + raise CruTemplateError( + f"Template file {file_name} has no variables." + ) + else: + if strict and not file.has_no_variables: + raise CruTemplateError(f"Non-template {file_name} has variables.") + self._files.append(file) @property def variables(self) -> set[str]: @@ -134,9 +166,3 @@ class TemplateDirectory: def generate_to_destination(self, variables: Mapping[str, str]) -> None: for file in self.files: file.generate_to_destination(variables) - - def extra_files_in_destination(self) -> Iterable[str]: - source_files = set(os.path.relpath(f.source, self.source) for f in self.files) - for file in self._scan_files(self.destination, self.exclude, None): - if file not in source_files: - yield file |