7f1cb97742
- Added `certifi` version 2026.4.22 and `charset-normalizer` version 3.4.7 with multiple wheel options. - Introduced `idna` version 3.14 and `prettytable` version 3.17.0 with their respective wheel files. - Updated `requests` to version 2.34.0, including its dependencies. - Added `urllib3` version 2.7.0 and `wcwidth` version 0.7.0 with their wheel files. - Updated `cli` package metadata to include new dependencies: `prettytable` and `requests`.
137 lines
3.6 KiB
Python
137 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
class Config:
|
|
def __init__(self, file_path: str | Path | None = None) -> None:
|
|
default_path = Path.home() / ".config" / "rs-cli" / "config.json"
|
|
self.file_path = Path(file_path).expanduser() if file_path else default_path
|
|
self.data: dict[str, Any] = {}
|
|
|
|
def load(self) -> dict[str, Any]:
|
|
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
if not self.file_path.exists():
|
|
self.data = {}
|
|
self.save()
|
|
return self.data
|
|
|
|
content = self.file_path.read_text(encoding="utf-8").strip()
|
|
if not content:
|
|
self.data = {}
|
|
self.save()
|
|
return self.data
|
|
|
|
parsed = json.loads(content)
|
|
if not isinstance(parsed, dict):
|
|
raise ValueError("El archivo de configuracion debe contener un objeto JSON.")
|
|
|
|
self.data = parsed
|
|
return self.data
|
|
|
|
def save(self) -> None:
|
|
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
self.file_path.write_text(
|
|
json.dumps(self.data, indent=2, ensure_ascii=False) + "\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
def get(self, path: str, default: Any = None) -> Any:
|
|
tokens = self._parse_path(path)
|
|
current: Any = self.data
|
|
|
|
for token in tokens:
|
|
if isinstance(token, str):
|
|
if not isinstance(current, dict) or token not in current:
|
|
return default
|
|
current = current[token]
|
|
continue
|
|
|
|
if not isinstance(current, list) or token >= len(current):
|
|
return default
|
|
current = current[token]
|
|
|
|
return current
|
|
|
|
def set(self, path: str, value: Any) -> None:
|
|
tokens = self._parse_path(path)
|
|
current: Any = self.data
|
|
|
|
for idx, token in enumerate(tokens):
|
|
is_last = idx == len(tokens) - 1
|
|
next_token = None if is_last else tokens[idx + 1]
|
|
|
|
if isinstance(token, str):
|
|
if not isinstance(current, dict):
|
|
raise TypeError(f"No se puede usar clave '{token}' en un nodo no-dict.")
|
|
|
|
if is_last:
|
|
current[token] = value
|
|
break
|
|
|
|
expected_type = list if isinstance(next_token, int) else dict
|
|
if token not in current or current[token] is None:
|
|
current[token] = [] if expected_type is list else {}
|
|
elif not isinstance(current[token], expected_type):
|
|
current[token] = [] if expected_type is list else {}
|
|
|
|
current = current[token]
|
|
continue
|
|
|
|
if not isinstance(current, list):
|
|
raise TypeError(f"No se puede usar indice [{token}] en un nodo no-list.")
|
|
|
|
while len(current) <= token:
|
|
current.append(None)
|
|
|
|
if is_last:
|
|
current[token] = value
|
|
break
|
|
|
|
expected_type = list if isinstance(next_token, int) else dict
|
|
if current[token] is None or not isinstance(current[token], expected_type):
|
|
current[token] = [] if expected_type is list else {}
|
|
|
|
current = current[token]
|
|
|
|
self.save()
|
|
|
|
def _parse_path(self, path: str) -> list[str | int]:
|
|
if not path or not isinstance(path, str):
|
|
raise ValueError("La ruta no puede estar vacia.")
|
|
|
|
tokens: list[str | int] = []
|
|
|
|
for segment in path.split("."):
|
|
if not segment:
|
|
raise ValueError(f"Ruta invalida: '{path}'")
|
|
|
|
index = 0
|
|
if segment[0] != "[":
|
|
match = re.match(r"[^\[\]]+", segment)
|
|
if not match:
|
|
raise ValueError(f"Ruta invalida: '{path}'")
|
|
key = match.group(0)
|
|
tokens.append(key)
|
|
index = len(key)
|
|
|
|
while index < len(segment):
|
|
if segment[index] != "[":
|
|
raise ValueError(f"Ruta invalida: '{path}'")
|
|
end = segment.find("]", index)
|
|
if end == -1:
|
|
raise ValueError(f"Ruta invalida: '{path}'")
|
|
|
|
raw_index = segment[index + 1 : end]
|
|
if not raw_index.isdigit():
|
|
raise ValueError(f"Indice invalido en ruta: '{path}'")
|
|
|
|
tokens.append(int(raw_index))
|
|
index = end + 1
|
|
|
|
return tokens
|