Coverage for smith / domain / value_objects.py: 100%
0 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 18:48 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 18:48 +0000
1"""Domain value objects — immutable data structures used across the domain."""
3from dataclasses import dataclass
4from enum import Enum
5from pathlib import Path
6from typing import Literal
9@dataclass(frozen=True)
10class FileSpec:
11 """A template file's relative path and binary content."""
13 relative_path: Path
14 content: bytes
17@dataclass(frozen=True)
18class TemplateSource:
19 """Identifies where a template originates (bundled, local, or URL)."""
21 kind: Literal["bundled", "local", "url"]
22 location: str
25class ConnectionState(Enum):
26 """Possible states of a project's connection to a template source."""
28 CONNECTED = "connected"
29 DISCONNECTED = "disconnected"
30 PARTIAL = "partial"
33@dataclass(frozen=True)
34class ConnectionStatus:
35 """Snapshot of a project's connection state and file presence."""
37 state: ConnectionState
38 source: TemplateSource | None
39 present_files: list[Path]
40 missing_files: list[Path]
42 def to_dict(self) -> dict:
43 """Serialise the status to a plain dictionary."""
44 return {
45 "state": self.state.value,
46 "source": (
47 {"kind": self.source.kind, "location": self.source.location}
48 if self.source
49 else None
50 ),
51 "present_files": [str(p) for p in self.present_files],
52 "missing_files": [str(p) for p in self.missing_files],
53 }
56@dataclass(frozen=True)
57class GitignoreSection:
58 """A delimited block of patterns inside .gitignore managed by smith."""
60 patterns: list[str]
61 start_marker: str = "# smith managed"
62 end_marker: str = "# end smith managed"