Files

104 lines
4.0 KiB
Python

"""Named profiles: an enabled-set plus a load order (later = higher priority)."""
from __future__ import annotations
from pathlib import Path
from .util import atomic_write_json, read_json
DEFAULT = "Default"
class ProfileError(Exception):
pass
class Profiles:
def __init__(self, path: Path):
self.path = path
self.active = DEFAULT
self.data: dict[str, dict] = {DEFAULT: {"enabled": [], "order": []}}
raw = read_json(path, None)
if isinstance(raw, dict) and isinstance(raw.get("profiles"), dict) and raw["profiles"]:
self.data = {n: {"enabled": list(p.get("enabled", [])), "order": list(p.get("order", []))}
for n, p in raw["profiles"].items() if isinstance(p, dict)} or self.data
self.active = raw.get("active") if raw.get("active") in self.data else next(iter(self.data))
def save(self) -> None:
atomic_write_json(self.path, {"active": self.active, "profiles": self.data})
@property
def names(self) -> list[str]:
return list(self.data)
@property
def current(self) -> dict:
return self.data[self.active]
# -- reconciliation with the library ------------------------------------
def reconcile(self, known_ids: list[str]) -> None:
known = set(known_ids)
for p in self.data.values():
p["order"] = [i for i in dict.fromkeys(p["order"]) if i in known]
p["order"] += [i for i in known_ids if i not in p["order"]]
p["enabled"] = [i for i in dict.fromkeys(p["enabled"]) if i in known]
# -- queries ------------------------------------------------------------
def is_enabled(self, mod_id: str) -> bool:
return mod_id in self.current["enabled"]
def order(self) -> list[str]:
return list(self.current["order"])
def enabled_in_order(self) -> list[str]:
en = set(self.current["enabled"])
return [i for i in self.current["order"] if i in en]
# -- edits --------------------------------------------------------------
def set_enabled(self, mod_id: str, on: bool) -> None:
en = self.current["enabled"]
if on and mod_id not in en:
en.append(mod_id)
if mod_id not in self.current["order"]:
self.current["order"].append(mod_id)
elif not on and mod_id in en:
en.remove(mod_id)
def move(self, mod_id: str, new_index: int) -> None:
order = self.current["order"]
if mod_id not in order:
return
order.remove(mod_id)
order.insert(max(0, min(new_index, len(order))), mod_id)
def remove_mod(self, mod_id: str) -> None:
for p in self.data.values():
p["order"] = [i for i in p["order"] if i != mod_id]
p["enabled"] = [i for i in p["enabled"] if i != mod_id]
def create(self, name: str, copy_from: str | None = None) -> None:
name = name.strip()
if not name or name in self.data:
raise ProfileError("A profile with that name already exists" if name else "Name required")
src = self.data.get(copy_from or "", {"enabled": [], "order": list(self.current["order"])})
self.data[name] = {"enabled": list(src["enabled"]), "order": list(src["order"])}
def rename(self, old: str, new: str) -> None:
new = new.strip()
if old not in self.data or not new or new in self.data:
raise ProfileError("Invalid profile name")
self.data = {(new if k == old else k): v for k, v in self.data.items()}
if self.active == old:
self.active = new
def delete(self, name: str) -> None:
if len(self.data) <= 1:
raise ProfileError("Cannot delete the last profile")
self.data.pop(name, None)
if self.active == name:
self.active = next(iter(self.data))
def switch(self, name: str) -> None:
if name not in self.data:
raise ProfileError(f"No such profile: {name}")
self.active = name