forked from Shinonome/caelestia-cli
feat: prompt installing optional components
This commit is contained in:
@@ -10,7 +10,7 @@ from caelestia.utils.dots.manifest import ComponentError, Manifest, ManifestErro
|
||||
from caelestia.utils.dots.packages import DEFAULT_AUR_HELPER, PackageInstaller
|
||||
from caelestia.utils.dots.source import DotsSource, SourceError
|
||||
from caelestia.utils.dots.state import DotsState
|
||||
from caelestia.utils.io import confirm, disable_input, fatal, info, log, pause, warn
|
||||
from caelestia.utils.io import PROMPT_COLOUR, confirm, disable_input, fatal, format_msg, info, log, pause, prompt, warn
|
||||
from caelestia.utils.paths import (
|
||||
config_backup_dir,
|
||||
config_dir,
|
||||
@@ -102,12 +102,14 @@ class Command:
|
||||
except SourceError as e:
|
||||
fatal(e)
|
||||
|
||||
enable = _parse_list_arg(self.args.enable_components)
|
||||
disable = _parse_list_arg(self.args.disable_components)
|
||||
try:
|
||||
manifest = source.manifest_at(tip)
|
||||
manifest.resolve_components(
|
||||
enable=_parse_list_arg(self.args.enable_components),
|
||||
disable=_parse_list_arg(self.args.disable_components),
|
||||
)
|
||||
manifest.resolve_components(enable=enable, disable=disable)
|
||||
|
||||
if enable is None and disable is None:
|
||||
self.prompt_optional_components(manifest)
|
||||
except (SourceError, ManifestError, ComponentError) as e:
|
||||
fatal(e)
|
||||
|
||||
@@ -116,6 +118,47 @@ class Command:
|
||||
|
||||
return source, tip, manifest
|
||||
|
||||
def prompt_optional_components(self, manifest: Manifest) -> None:
|
||||
comp_arr = manifest.disabled_components
|
||||
if not comp_arr:
|
||||
return
|
||||
|
||||
print(format_msg(PROMPT_COLOUR, "Components to enable?"))
|
||||
for i, comp in enumerate(comp_arr):
|
||||
print(format_msg(PROMPT_COLOUR, f" [{i + 1}] {comp}"))
|
||||
print(format_msg(PROMPT_COLOUR, "[A]ll or (1 2 3, 1-3, ^4)"))
|
||||
ans = prompt("", end="").lower().strip()
|
||||
|
||||
def _valid_v(v: str) -> int:
|
||||
try:
|
||||
i_v = int(v, base=10) - 1 # -1 to translate to 0 index
|
||||
except ValueError:
|
||||
fatal(f'Invalid input. Given value "{v}" must be an integer.')
|
||||
if i_v < 0 or i_v >= len(comp_arr):
|
||||
fatal(f'Invalid input. Given value "{v}" must be between 1 and {len(comp_arr)} inclusive.')
|
||||
return i_v
|
||||
|
||||
if ans in ("a", "all"):
|
||||
manifest.resolve_components(enable=list(manifest.components))
|
||||
elif ans:
|
||||
enabled: list[str] = []
|
||||
toks = ans.split()
|
||||
for tok in toks:
|
||||
fr, sep, to = tok.partition("-")
|
||||
if sep:
|
||||
fr = _valid_v(fr)
|
||||
to = _valid_v(to)
|
||||
if fr > to:
|
||||
fatal(f'Invalid input. Given range "{tok}" must be lo-hi.')
|
||||
enabled += comp_arr[fr : to + 1]
|
||||
elif tok.startswith("^"):
|
||||
t = _valid_v(tok[1:])
|
||||
enabled += comp_arr[:t] + comp_arr[t + 1 :]
|
||||
else:
|
||||
t = _valid_v(tok)
|
||||
enabled.append(comp_arr[t])
|
||||
manifest.resolve_components(enable=list(set(enabled)))
|
||||
|
||||
def deploy_configs(self, source: DotsSource, manifest: Manifest) -> None:
|
||||
log("Installing configs...")
|
||||
deployer = Deployer()
|
||||
|
||||
@@ -68,7 +68,6 @@ class ManifestComponent:
|
||||
|
||||
@dataclass
|
||||
class _ManifestData:
|
||||
resolved_comps: bool = False
|
||||
enabled_comps: list[str] = field(default_factory=list)
|
||||
disabled_comps: list[str] = field(default_factory=list)
|
||||
|
||||
@@ -121,9 +120,6 @@ class Manifest:
|
||||
) -> None:
|
||||
"""Resolves enabled/disabled components. This MUST be called before calling any other method."""
|
||||
|
||||
if self._data.resolved_comps:
|
||||
return
|
||||
|
||||
enable_set = set(enable or [])
|
||||
disable_set = set(disable or [])
|
||||
known = set(self.components)
|
||||
@@ -140,12 +136,13 @@ class Manifest:
|
||||
enabled |= enable_set
|
||||
enabled -= disable_set
|
||||
|
||||
self._data.enabled_comps.clear()
|
||||
self._data.disabled_comps.clear()
|
||||
for name in self.components:
|
||||
if name in enabled:
|
||||
self._data.enabled_comps.append(name)
|
||||
else:
|
||||
self._data.disabled_comps.append(name)
|
||||
self._data.resolved_comps = True
|
||||
|
||||
def enabled_entries(self) -> list[ManifestEntry]:
|
||||
"""The entries of every enabled component."""
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import sys
|
||||
from typing import Never
|
||||
|
||||
LOG_COLOUR: int = 2
|
||||
INFO_COLOUR: int = 0
|
||||
PROMPT_COLOUR: int = 36
|
||||
WARNING_COLOUR: int = 33
|
||||
ERROR_COLOUR: int = 31
|
||||
|
||||
_disable_input: bool = False
|
||||
|
||||
|
||||
@@ -25,28 +31,28 @@ def log_exception(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
def _format_msg(colour: int, msg: str) -> str:
|
||||
def format_msg(colour: int, msg: str) -> str:
|
||||
return f"\033[{colour}m:: {msg}\033[0m"
|
||||
|
||||
|
||||
def log(msg: str) -> None:
|
||||
print(_format_msg(2, msg))
|
||||
print(format_msg(LOG_COLOUR, msg))
|
||||
|
||||
|
||||
def info(msg: str) -> None:
|
||||
print(_format_msg(0, msg))
|
||||
print(format_msg(INFO_COLOUR, msg))
|
||||
|
||||
|
||||
def warn(msg: str) -> None:
|
||||
print(_format_msg(33, f"Warning: {msg}"))
|
||||
print(format_msg(WARNING_COLOUR, f"Warning: {msg}"))
|
||||
|
||||
|
||||
def error(err: str | Exception) -> None:
|
||||
print(_format_msg(31, f"Error: {err}"), file=sys.stderr)
|
||||
print(format_msg(ERROR_COLOUR, f"Error: {err}"), file=sys.stderr)
|
||||
|
||||
|
||||
def fatal(err: str | Exception) -> Never:
|
||||
print(_format_msg(31, f"Fatal: {err}"), file=sys.stderr)
|
||||
print(format_msg(ERROR_COLOUR, f"Fatal: {err}"), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -62,8 +68,8 @@ def _input(prompt: str) -> str:
|
||||
raise KeyboardInterrupt()
|
||||
|
||||
|
||||
def prompt(msg: str) -> str:
|
||||
return _input(_format_msg(36, msg) + " ")
|
||||
def prompt(msg: str, end: str = " ") -> str:
|
||||
return _input(format_msg(PROMPT_COLOUR, msg) + end)
|
||||
|
||||
|
||||
def confirm(msg: str, default: bool = True) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user