mirror of
https://github.com/d9pouces/id_configurator.git
synced 2026-11-09 01:47:21 +01:00
3b6eb7290f
Signed-off-by: d9pouces <github@19pouces.net>
72 lines
2.5 KiB
Python
72 lines
2.5 KiB
Python
"""Base class and registry for configuration modules.
|
|
|
|
Each module inspects the configuration and the source filesystem (via the
|
|
`Context`) to decide whether it applies, then writes the resulting files to
|
|
the destination directory.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from abc import ABC, abstractmethod
|
|
|
|
from id_configurator.context import Context
|
|
|
|
_REGISTRY: list[type[Module]] = []
|
|
|
|
|
|
class Module(ABC):
|
|
name: str
|
|
|
|
@abstractmethod
|
|
def is_relevant(self, context: Context) -> bool:
|
|
"""Return True if this module has something to do for the given configuration."""
|
|
|
|
@abstractmethod
|
|
def apply(self, context: Context) -> None:
|
|
"""Write the module's changes to the destination directory."""
|
|
|
|
|
|
def register(module_cls: type[Module]) -> type[Module]:
|
|
"""Class decorator registering a module so it is picked up by the CLI."""
|
|
_REGISTRY.append(module_cls)
|
|
return module_cls
|
|
|
|
|
|
def all_modules() -> list[type[Module]]:
|
|
return list(_REGISTRY)
|
|
|
|
|
|
_IDENTIFIER_FORBIDDEN_CHARS = re.compile(r"[^A-Za-z0-9_-]")
|
|
|
|
|
|
def sanitize_filename(name: str) -> str:
|
|
"""Make a user-supplied name safe to use as a single filesystem path
|
|
component.
|
|
|
|
Modules that derive a file's name from configuration data (a
|
|
certificate's name, a repository's name, ...) must run it through this
|
|
first. Every character that isn't a letter, a digit, `_` or `-` is
|
|
replaced with `_`: this rules out `/` and `\\` (which would silently
|
|
turn into extra, unintended path segments), `.` (so the result can
|
|
never be `.` or `..`), as well as spaces, control characters and any
|
|
other character that could confuse the tools reading these files. An
|
|
empty name yields `_`, so the result is always a non-empty file name.
|
|
"""
|
|
return _IDENTIFIER_FORBIDDEN_CHARS.sub("_", name) or "_"
|
|
|
|
|
|
def sanitize_repository_name(name: str) -> str:
|
|
"""Replace every character of `name` that isn't a plain identifier
|
|
character (letters, digits, `_`, `-`) with `_`.
|
|
|
|
Several modules write a repository's name verbatim as a bare,
|
|
unquoted identifier in a generated config file - an INI section
|
|
header, a TOML table key, ... - where an unrestricted name could break
|
|
out of that syntax (e.g. `]`, a newline, or - for TOML - a literal
|
|
`.`, which would silently open extra, nested tables instead of naming
|
|
the one table it's meant to). Spaces and every other non-identifier
|
|
character are replaced the same way, for consistency across callers.
|
|
"""
|
|
return _IDENTIFIER_FORBIDDEN_CHARS.sub("_", name)
|