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>2024-12-19 22:23:05 +0800
commit7e85627eb0d0126baf1e698ffdeae0f3c5fc693d (patch)
treeacf21fbfe5dc96060ecdfc2ba33dacb337caf4dd /tools/cru-py/cru
parentf9aa02ec1a4c24e80a206857d4f68198bb027bb4 (diff)
downloadcrupest-7e85627eb0d0126baf1e698ffdeae0f3c5fc693d.tar.gz
crupest-7e85627eb0d0126baf1e698ffdeae0f3c5fc693d.tar.bz2
crupest-7e85627eb0d0126baf1e698ffdeae0f3c5fc693d.zip
HALF WORK: 2024.12.19
code.
Diffstat (limited to 'tools/cru-py/cru')
-rw-r--r--tools/cru-py/cru/service/_manager.py2
-rw-r--r--tools/cru-py/cru/template.py174
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