mirror of
https://git.srhtnin.garden/sarah/SaraSuite.git
synced 2026-10-13 18:18:12 +02:00
77 lines
2.5 KiB
Python
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})
|