aboutsummaryrefslogtreecommitdiff
path: root/tools/cru-py/cru/config.py
blob: b0c83d5f1e6e31a01280f1b71f6c1621765b2f20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
from typing import Any, TypeVar, Generic

from .excp import CruInternalLogicError
from .value import ValueType, ValueGenerator, ValidationError

T = TypeVar("T")


class ConfigItem(Generic[T]):
    OptionalValueGenerator = ValueGenerator[T, []] | None

    def __init__(self, name: str, description: str, value_type: ValueType[T], value: T | None, default_value: T, *,
                 value_generator: OptionalValueGenerator = None) -> None:
        self._name = name
        self._description = description
        self._value_type = value_type
        self._default_value = default_value
        self._value_generator = value_generator
        self._value: T | None = value

    @property
    def name(self) -> str:
        return self._name

    @property
    def description(self) -> str:
        return self._description

    @property
    def value_type(self) -> ValueType[T]:
        return self._value_type

    @property
    def default_value(self) -> T:
        return self._default_value

    @property
    def is_default(self) -> bool:
        return self._value is None

    @property
    def is_set(self) -> bool:
        return not self.is_default

    @property
    def value(self) -> T:
        return self._value or self._default_value

    def set_value(self, v: T | str, /, allow_convert_from_str=False):
        if allow_convert_from_str:
            self._value = self.value_type.check_value(v)
        else:
            self._value = self.value_type.check_value_or_try_convert_from_str(v)

    @value.setter
    def value(self, v: T) -> None:
        self.set_value(v)

    @property
    def value_generator(self) -> OptionalValueGenerator:
        return self._value_generator

    def generate_value(self, allow_interactive=False) -> T | None:
        if self.value_generator is None: return None
        if self.value_generator.interactive and not allow_interactive:
            return None
        else:
            v = self.generate_value()
            try:
                self.value_type.check_value(v)
                return v
            except ValidationError as e:
                raise CruInternalLogicError("Config value generator returns invalid value.", name=self.name, inner=e)

    def copy(self) -> "ConfigItem":
        return ConfigItem(self.name, self.description, self.value_type,
                          self._value.copy() if self._value is not None else None, self._default_value.copy(),
                          value_generator=self.value_generator)


class Configuration:
    def __init__(self, items: None | list[ConfigItem] = None) -> None:
        self._items: list[ConfigItem] = items or []

    @property
    def items(self) -> list[ConfigItem]:
        return self._items

    @property
    def item_map(self) -> dict[str, ConfigItem]:
        return {i.name: i for i in self.items}

    def get_optional_item(self, name: str) -> ConfigItem | None:
        for i in self.items:
            if i.name == name:
                return i
        return None

    def clear(self) -> None:
        self._items.clear()

    def has_item(self, name: str) -> bool:
        return self.get_optional_item(name) is not None

    def add_item(self, item: ConfigItem):
        i = self.get_optional_item(item.name)
        if i is not None:
            raise CruInternalLogicError("Config item of the name already exists.", name=item.name)
        self.items.append(item)
        return item

    def set_value(self, name: str, v: Any, /, allow_convert_from_str=False):
        i = self.get_optional_item(name)
        if i is None:
            raise CruInternalLogicError("No config item of the name. Can't set value.", name=name)
        i.set_value(v, allow_convert_from_str)

    def copy(self) -> "Configuration":
        return Configuration([i.copy() for i in self.items])

    def __getitem__(self, name: str) -> ConfigItem:
        i = self.get_optional_item(name)
        if i is not None:
            return i
        raise CruInternalLogicError('No config item of the name.', name=name)

    def __contains__(self, name: str):
        return self.has_item(name)