Files
2026-09-24 22:04:04 +02:00

77 lines
2.5 KiB
Python

"""Receipts: the record of exactly what an install placed, so updates know
what they're replacing and removal can take every piece back out."""
from dataclasses import asdict, dataclass, field
from pathlib import Path
import tomlkit
from Core import ReceiptsDir
@dataclass
class PlacedFile:
Path: str
Kind: str # "Template", "Config" or "ConfigNew" (a .new file left beside a config)
Root: bool = False
# Where a file that already existed at Path (not placed by SaraSuite) was
# moved to. It's put back when this program is removed.
Displaced: str = ""
@dataclass
class IniRecord:
File: str
Section: str
Key: str
Value: str
HadPrevious: bool = False
Previous: str = ""
@dataclass
class Receipt:
Name: str
Version: str = ""
Commit: str = ""
ManifestHash: str = ""
RequirementsHash: str = ""
InstalledAt: str = ""
UpdatedAt: str = ""
# False while an install/update is still running, or if it failed partway.
# The files it lists are still real, so Remove still cleans them up.
Complete: bool = False
Options: dict[str, bool] = field(default_factory=dict)
Answers: dict[str, str] = field(default_factory=dict)
Files: list[PlacedFile] = field(default_factory=list)
IniEdits: list[IniRecord] = field(default_factory=list)
EnabledServices: list[str] = field(default_factory=list)
@property
def FilePath(self) -> Path:
return ReceiptsDir / f"{self.Name}.toml"
def FindFile(self, TargetPath: str) -> PlacedFile | None:
return next((File for File in self.Files if File.Path == TargetPath), None)
def FindIni(self, File: str, Section: str, Key: str) -> IniRecord | None:
return next(
(Edit for Edit in self.IniEdits if (Edit.File, Edit.Section, Edit.Key) == (File, Section, Key)),
None,
)
def Save(self) -> None:
ReceiptsDir.mkdir(parents=True, exist_ok=True)
self.FilePath.write_text(tomlkit.dumps(asdict(self)))
def LoadReceipt(Name: str) -> Receipt | None:
ReceiptPath = ReceiptsDir / f"{Name}.toml"
if not ReceiptPath.exists():
return None
Data = tomlkit.parse(ReceiptPath.read_text()).unwrap()
Data["Files"] = [PlacedFile(**Item) for Item in Data.get("Files", [])]
Data["IniEdits"] = [IniRecord(**Item) for Item in Data.get("IniEdits", [])]
Known = set(Receipt.__dataclass_fields__)
return Receipt(**{Key: Value for Key, Value in Data.items() if Key in Known})