from __future__ import annotations import dataclasses import datetime import enum from abc import abstractmethod from typing import Generic, Optional, Protocol, TypeVar class Comparable(Protocol): @abstractmethod def __gt__(self: T, other: T, /) -> bool: pass @abstractmethod def __ge__(self: T, other: T, /) -> bool: pass @abstractmethod def __lt__(self: T, other: T, /) -> bool: pass @abstractmethod def __le__(self: T, other: T, /) -> bool: pass T = TypeVar("T", bound=Comparable) @dataclasses.dataclass class Range(Generic[T]): gt: Optional[T] = None gte: Optional[T] = None lt: Optional[T] = None lte: Optional[T] = None def lower_bound(self) -> Optional[tuple[T, bool]]: if self.gt is None and self.gte is None: return None elif self.gt is not None and self.gte is not None: lb = self.gt if (self.gt > self.gte) else self.gte eq = self.gt < self.gte return lb, eq elif self.gt is not None: return self.gt, False elif self.gte is not None: return self.gte, True def upper_bound(self) -> Optional[tuple[T, bool]]: if self.lt is None and self.lte is None: return None elif self.lt is not None and self.lte is not None: ub = self.lt if (self.lt < self.lte) else self.lte eq = self.lt > self.lte return ub, eq elif self.lt is not None: return self.lt, False elif self.lte is not None: return self.lte, True class Order(enum.Enum): ASCENDING = 'ASC' DESCENDING = 'DESC' ColT = TypeVar("ColT") @dataclasses.dataclass class OrderCol(Generic[ColT]): column: ColT order: Order