relay/relay/database/config.py
2024-11-28 07:09:53 -05:00

164 lines
3.8 KiB
Python

from __future__ import annotations
# removing the above line turns annotations into types instead of str objects which messes with
# `Field.type`
from blib import convert_to_boolean
from bsql import Row
from collections.abc import Callable, Sequence
from dataclasses import Field, asdict, dataclass, fields
from typing import TYPE_CHECKING, Any
from .. import logger as logging
if TYPE_CHECKING:
from typing import Self
THEMES = {
"default": {
"text": "#DDD",
"background": "#222",
"primary": "#D85",
"primary-hover": "#DA8",
"section-background": "#333",
"table-background": "#444",
"border": "#444",
"message-text": "#DDD",
"message-background": "#335",
"message-border": "#446",
"error-text": "#DDD",
"error-background": "#533",
"error-border": "#644"
},
"pink": {
"text": "#DDD",
"background": "#222",
"primary": "#D69",
"primary-hover": "#D36",
"section-background": "#333",
"table-background": "#444",
"border": "#444",
"message-text": "#DDD",
"message-background": "#335",
"message-border": "#446",
"error-text": "#DDD",
"error-background": "#533",
"error-border": "#644"
},
"blue": {
"text": "#DDD",
"background": "#222",
"primary": "#69D",
"primary-hover": "#36D",
"section-background": "#333",
"table-background": "#444",
"border": "#444",
"message-text": "#DDD",
"message-background": "#335",
"message-border": "#446",
"error-text": "#DDD",
"error-background": "#533",
"error-border": "#644"
}
}
# serializer | deserializer
CONFIG_CONVERT: dict[str, tuple[Callable[[Any], str], Callable[[str], Any]]] = {
"str": (str, str),
"int": (str, int),
"bool": (str, convert_to_boolean),
"logging.LogLevel": (lambda x: x.name, logging.LogLevel.parse)
}
@dataclass()
class ConfigData:
schema_version: int = 20240625
private_key: str = ""
approval_required: bool = False
log_level: logging.LogLevel = logging.LogLevel.INFO
name: str = "ActivityRelay"
note: str = ""
theme: str = "default"
whitelist_enabled: bool = False
def __getitem__(self, key: str) -> Any:
if (value := getattr(self, key.replace("-", "_"), None)) is None:
raise KeyError(key)
return value
def __setitem__(self, key: str, value: Any) -> None:
self.set(key, value)
@classmethod
def KEYS(cls: type[Self]) -> Sequence[str]:
return list(cls.__dataclass_fields__)
@staticmethod
def SYSTEM_KEYS() -> Sequence[str]:
return ("schema-version", "schema_version", "private-key", "private_key")
@classmethod
def USER_KEYS(cls: type[Self]) -> Sequence[str]:
return tuple(key for key in cls.KEYS() if key not in cls.SYSTEM_KEYS())
@classmethod
def DEFAULT(cls: type[Self], key: str) -> str | int | bool:
return cls.FIELD(key.replace("-", "_")).default # type: ignore[return-value]
@classmethod
def FIELD(cls: type[Self], key: str) -> Field[str | int | bool]:
parsed_key = key.replace("-", "_")
for field in fields(cls):
if field.name == parsed_key:
return field
raise KeyError(key)
@classmethod
def from_rows(cls: type[Self], rows: Sequence[Row]) -> Self:
data = cls()
set_schema_version = False
for row in rows:
data.set(row["key"], row["value"])
if row["key"] == "schema-version":
set_schema_version = True
if not set_schema_version:
data.schema_version = 0
return data
def get(self, key: str, default: Any = None, serialize: bool = False) -> Any:
field = type(self).FIELD(key)
value = getattr(self, field.name, None)
if not serialize:
return value
converter = CONFIG_CONVERT[str(field.type)][0]
return converter(value)
def set(self, key: str, value: Any) -> None:
field = type(self).FIELD(key)
converter = CONFIG_CONVERT[str(field.type)][1]
setattr(self, field.name, converter(value))
def to_dict(self) -> dict[str, Any]:
return {key.replace("_", "-"): value for key, value in asdict(self).items()}