aboutsummaryrefslogtreecommitdiff
path: root/tools/cru-py/cru/_list.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/cru-py/cru/_list.py')
-rw-r--r--tools/cru-py/cru/_list.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/tools/cru-py/cru/_list.py b/tools/cru-py/cru/_list.py
new file mode 100644
index 0000000..f1965db
--- /dev/null
+++ b/tools/cru-py/cru/_list.py
@@ -0,0 +1,128 @@
+
+
+class CruInplaceList(CruList, Generic[_V]):
+
+ def clear(self) -> "CruInplaceList[_V]":
+ self.clear()
+ return self
+
+ def extend(self, *l: Iterable[_V]) -> "CruInplaceList[_V]":
+ self.extend(l)
+ return self
+
+ def reset(self, *l: Iterable[_V]) -> "CruInplaceList[_V]":
+ self.clear()
+ self.extend(l)
+ return self
+
+ def transform(self, *f: OptionalElementTransformer) -> "CruInplaceList"[Any]:
+ return self.reset(super().transform(*f))
+
+ def transform_if(
+ self, f: OptionalElementTransformer, p: ElementPredicate[_V]
+ ) -> "CruInplaceList"[Any]:
+ return self.reset(super().transform_if(f, p))
+
+ def remove_by_indices(self, *index: int) -> "CruInplaceList"[_V]:
+ return self.reset(super().remove_by_indices(*index))
+
+ def remove_all_if(self, p: ElementPredicate[_V]) -> "CruInplaceList"[_V]:
+ return self.reset(super().remove_all_if(p))
+
+ def remove_all_value(self, *r: Any) -> "CruInplaceList"[_V]:
+ return self.reset(super().remove_all_value(*r))
+
+ def replace_all_value(
+ self, old_value: Any, new_value: R
+ ) -> "CruInplaceList"[_V | R]:
+ return self.reset(super().replace_all_value(old_value, new_value))
+
+ @staticmethod
+ def make(l: CanBeList[_V]) -> "CruInplaceList"[_V]:
+ return CruInplaceList(ListOperations.make(l))
+
+
+
+K = TypeVar("K")
+
+
+class CruUniqueKeyInplaceList(Generic[_V, K]):
+ KeyGetter = Callable[[_V], K]
+
+ def __init__(
+ self, get_key: KeyGetter, *, before_add: Callable[[_V], _V] | None = None
+ ):
+ super().__init__()
+ self._get_key = get_key
+ self._before_add = before_add
+ self._l: CruInplaceList[_V] = CruInplaceList()
+
+ @property
+ def object_key_getter(self) -> KeyGetter:
+ return self._get_key
+
+ @property
+ def internal_list(self) -> CruInplaceList[_V]:
+ return self._l
+
+ def validate_self(self):
+ keys = self._l.transform(self._get_key)
+ if len(keys) != len(set(keys)):
+ raise ValueError("Duplicate keys!")
+
+ def get_or(self, k: K, fallback: Any = CRU_NOT_FOUND) -> _V | Any:
+ r = self._l.find_if(lambda i: k == self._get_key(i))
+ return r if r is not CRU_NOT_FOUND else fallback
+
+ def get(self, k: K) -> _V:
+ v = self.get_or(k, CRU_NOT_FOUND)
+ if v is CRU_NOT_FOUND:
+ raise KeyError(f"Key not found!")
+ return v
+
+ def has_key(self, k: K) -> bool:
+ return self.get_or(k, CRU_NOT_FOUND) is not CRU_NOT_FOUND
+
+ def has_any_key(self, *k: K) -> bool:
+ return self._l.any(lambda i: self._get_key(i) in k)
+
+ def try_remove(self, k: K) -> bool:
+ i = self._l.find_index_if(lambda v: k == self._get_key(v))
+ if i is CRU_NOT_FOUND:
+ return False
+ self._l.remove_by_indices(i)
+ return True
+
+ def remove(self, k: K, allow_absense: bool = False) -> None:
+ if not self.try_remove(k) and not allow_absense:
+ raise KeyError(f"Key {k} not found!")
+
+ def add(self, v: _V, /, replace: bool = False) -> None:
+ if self.has_key(self._get_key(v)):
+ if replace:
+ self.remove(self._get_key(v))
+ else:
+ raise ValueError(f"Key {self._get_key(v)} already exists!")
+ if self._before_add is not None:
+ v = self._before_add(v)
+ self._l.append(v)
+
+ def set(self, v: _V) -> None:
+ self.add(v, True)
+
+ def extend(self, l: Iterable[_V], /, replace: bool = False) -> None:
+ if not replace and self.has_any_key([self._get_key(i) for i in l]):
+ raise ValueError("Keys already exists!")
+ if self._before_add is not None:
+ l = [self._before_add(i) for i in l]
+ keys = [self._get_key(i) for i in l]
+ self._l.remove_all_if(lambda i: self._get_key(i) in keys).extend(l)
+
+ def clear(self) -> None:
+ self._l.clear()
+
+ def __iter__(self):
+ return iter(self._l)
+
+ def __len__(self):
+ return len(self._l)