from collections.abc import Iterator from dataclasses import dataclass from pathlib import Path from typing import Self, cast, final from docker_compose.cfg.compose_paths import ServicePath, VolumePath from docker_compose.cfg.org_yaml import OrgYaml from docker_compose.util.Ts import TypeYamlCompatibleDict, TypeYamlCompatibleRes from docker_compose.util.yaml_util import read_yaml, write_yaml YAML_EXTS = frozenset((".yml", ".yaml")) class ComposeFileTemplate(Path): def write_dict(self, data: TypeYamlCompatibleDict) -> None: write_yaml(data, self) def write(self, data: str) -> None: with self.open("wt") as f: _ = f.write(data) class OrgFile(Path): @property def as_dict(self) -> OrgYaml: return cast(OrgYaml, cast(object, read_yaml(self))) class YamlDir(Path): @property def yaml_files(self) -> Iterator[Path]: if not self: raise FileNotFoundError(self) for service in self.iterdir(): if service.suffix not in YAML_EXTS: continue yield service def __bool__(self) -> bool: return self.exists() class CfgDir(YamlDir): @property def cfg_file(self) -> OrgFile: for file in self.yaml_files: if file.stem != "cfg": continue return OrgFile(file) raise FileNotFoundError(self.joinpath("cfg.y(a)ml")) class ServiceDir(YamlDir): @property def files(self) -> Iterator[ServicePath]: for file in self.yaml_files: yield ServicePath(file) class VolumesDir(YamlDir): @property def files(self) -> Iterator[VolumePath]: try: for file in self.yaml_files: yield VolumePath(file) except FileNotFoundError: return class VolumeData(Path): def write(self, data: TypeYamlCompatibleRes) -> None: write_yaml(data, self) @final @dataclass(frozen=True, slots=True) class SrcPaths: cfg_dir: CfgDir org_file: OrgFile env_file: Path service_dir: ServiceDir vol_dir: VolumesDir compose_file: ComposeFileTemplate volume_data: VolumeData @classmethod def from_path(cls, src: Path) -> Self: cfg_dir = CfgDir(src) return cls( cfg_dir, cfg_dir.cfg_file, src.joinpath(".env"), ServiceDir(src.joinpath("services")), VolumesDir(src.joinpath("volumes")), ComposeFileTemplate(src.joinpath("docker-compose.yml")), VolumeData(src.joinpath("volume_paths.yml")), ) @property def app(self) -> str: return self.cfg_dir.stem