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

1"""Domain value objects — immutable data structures used across the domain.""" 

2 

3from dataclasses import dataclass 

4from enum import Enum 

5from pathlib import Path 

6from typing import Literal 

7 

8 

9@dataclass(frozen=True) 

10class FileSpec: 

11 """A template file's relative path and binary content.""" 

12 

13 relative_path: Path 

14 content: bytes 

15 

16 

17@dataclass(frozen=True) 

18class TemplateSource: 

19 """Identifies where a template originates (bundled, local, or URL).""" 

20 

21 kind: Literal["bundled", "local", "url"] 

22 location: str 

23 

24 

25class ConnectionState(Enum): 

26 """Possible states of a project's connection to a template source.""" 

27 

28 CONNECTED = "connected" 

29 DISCONNECTED = "disconnected" 

30 PARTIAL = "partial" 

31 

32 

33@dataclass(frozen=True) 

34class ConnectionStatus: 

35 """Snapshot of a project's connection state and file presence.""" 

36 

37 state: ConnectionState 

38 source: TemplateSource | None 

39 present_files: list[Path] 

40 missing_files: list[Path] 

41 

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 } 

54 

55 

56@dataclass(frozen=True) 

57class GitignoreSection: 

58 """A delimited block of patterns inside .gitignore managed by smith.""" 

59 

60 patterns: list[str] 

61 start_marker: str = "# smith managed" 

62 end_marker: str = "# end smith managed"