Coverage for src / lexigram / contracts / domain / pagination.py: 3%
29 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
1"""Pagination data models for Lexigram Framework."""
3from __future__ import annotations
5from dataclasses import dataclass
6from typing import Any, Generic, Protocol, TypeVar, runtime_checkable
8T = TypeVar("T")
11@runtime_checkable
12class OffsetPageProtocol(Protocol):
13 """Protocol for offset-based page request parameters.
15 Implemented by offset/limit style pagination inputs. Use this for
16 code that should work with any offset-pagination type.
17 """
19 @property
20 def offset(self) -> int:
21 """Zero-based record offset."""
22 ...
24 @property
25 def limit(self) -> int:
26 """Maximum number of records to return."""
27 ...
30@runtime_checkable
31class CursorPageProtocol(Protocol, Generic[T]):
32 """Protocol for cursor-based paginated result pages.
34 Implemented by ``CursorPage`` and Relay-compliant GraphQL page types.
35 Use this for code that should work with any cursor-pagination result.
36 """
38 @property
39 def items(self) -> list[T]:
40 """Items in this page."""
41 ...
43 @property
44 def next_cursor(self) -> str | None:
45 """Opaque cursor for the next page, or ``None`` if no more."""
46 ...
48 @property
49 def has_more(self) -> bool:
50 """Whether there are more results after this page."""
51 ...
54@dataclass(frozen=True)
55class CursorPage(Generic[T]):
56 """Cursor-based pagination for large datasets.
58 Attributes:
59 items: The entities in this page.
60 next_cursor: Opaque cursor for the next page, or None if no more.
61 prev_cursor: Opaque cursor for the previous page, or None.
62 has_more: Whether there are more results after this page.
63 has_previous: Whether there are results before this page.
64 total_count: Optional total count (may be None for performance).
65 """
67 items: list[T]
68 next_cursor: str | None = None
69 prev_cursor: str | None = None
70 has_more: bool = False
71 has_previous: bool = False
72 total_count: int | None = None
74 def to_dict(self) -> dict[str, Any]:
75 """Serialize to API-friendly dictionary."""
76 return {
77 "items": self.items,
78 "next_cursor": self.next_cursor,
79 "prev_cursor": self.prev_cursor,
80 "has_more": self.has_more,
81 "has_previous": self.has_previous,
82 "total_count": self.total_count,
83 }
86__all__ = [
87 "CursorPage",
88 "CursorPageProtocol",
89 "OffsetPageProtocol",
90 "T",
91]