icydb_core/db/query/fluent/load/
pagination.rs1use crate::{
7 db::{
8 PagedLoadExecution, PagedLoadExecutionWithTrace, PersistedRow,
9 query::fluent::load::FluentLoadQuery,
10 query::{
11 intent::{IntentError, QueryError},
12 read_intent::{ADMIN_BATCH_ROWS, AdminBatchRequest, PageRequest, ReadIntentKind},
13 },
14 },
15 traits::{EntityKind, EntityValue},
16};
17
18struct PagedLoadQuery<'a, E>
19where
20 E: EntityKind,
21{
22 inner: FluentLoadQuery<'a, E>,
23 read_intent: ReadIntentKind,
24}
25
26#[derive(Clone, Copy)]
27enum PagedTerminal {
28 PublicPage,
29 TrustedAdminBatch,
30}
31
32impl PagedTerminal {
33 const fn raw_limit_error(self) -> IntentError {
34 match self {
35 Self::PublicPage => IntentError::raw_limit_before_page_terminal(),
36 Self::TrustedAdminBatch => IntentError::raw_limit_before_admin_batch_terminal(),
37 }
38 }
39
40 const fn read_intent(self) -> ReadIntentKind {
41 match self {
42 Self::PublicPage => ReadIntentKind::PublicPage,
43 Self::TrustedAdminBatch => ReadIntentKind::TrustedAdminBatch,
44 }
45 }
46
47 const fn validate<E>(self, query: &FluentLoadQuery<'_, E>) -> Result<(), QueryError>
48 where
49 E: EntityKind,
50 {
51 match self {
52 Self::PublicPage => Ok(()),
53 Self::TrustedAdminBatch => {
54 if !query.trusted_read_unchecked_enabled() {
55 return Err(QueryError::intent(
56 IntentError::admin_batch_requires_trusted_read(),
57 ));
58 }
59
60 Ok(())
61 }
62 }
63 }
64}
65
66impl<'a, E> FluentLoadQuery<'a, E>
67where
68 E: PersistedRow,
69{
70 pub fn page(self, limit: u32) -> Result<PagedLoadExecution<E>, QueryError>
81 where
82 E: EntityValue,
83 {
84 self.page_request(PageRequest::first(limit))?.execute()
85 }
86
87 pub fn next_page(
92 self,
93 limit: u32,
94 cursor: impl Into<String>,
95 ) -> Result<PagedLoadExecution<E>, QueryError>
96 where
97 E: EntityValue,
98 {
99 self.page_request(PageRequest::next(limit, cursor))?
100 .execute()
101 }
102
103 fn page_request(self, request: PageRequest) -> Result<PagedLoadQuery<'a, E>, QueryError> {
104 let limit = request.effective_limit();
105 let cursor = request.into_cursor();
106
107 self.into_paged_query(limit, cursor, PagedTerminal::PublicPage)
108 }
109
110 pub fn admin_batch(
117 self,
118 request: AdminBatchRequest,
119 ) -> Result<PagedLoadExecution<E>, QueryError>
120 where
121 E: PersistedRow + EntityValue,
122 {
123 let cursor = request.into_cursor();
124 self.into_paged_query(ADMIN_BATCH_ROWS, cursor, PagedTerminal::TrustedAdminBatch)?
125 .execute()
126 }
127
128 fn into_paged_query(
129 self,
130 limit: u32,
131 cursor: Option<String>,
132 terminal: PagedTerminal,
133 ) -> Result<PagedLoadQuery<'a, E>, QueryError> {
134 self.ensure_semantic_terminal_owns_limit(terminal.raw_limit_error())?;
135 self.ensure_page_request_owns_cursor()?;
136 terminal.validate(&self)?;
137
138 let mut inner = self.map_query(|query| query.with_load_limit(limit));
139 if let Some(cursor) = cursor {
140 inner = inner.with_cursor_token(cursor);
141 }
142
143 inner.ensure_paged_mode_ready()?;
144
145 Ok(PagedLoadQuery {
146 inner,
147 read_intent: terminal.read_intent(),
148 })
149 }
150}
151
152impl<E> PagedLoadQuery<'_, E>
153where
154 E: PersistedRow,
155{
156 fn execute(self) -> Result<PagedLoadExecution<E>, QueryError>
162 where
163 E: PersistedRow + EntityValue,
164 {
165 self.execute_with_trace()
166 .map(PagedLoadExecutionWithTrace::into_execution)
167 }
168
169 fn execute_with_trace(self) -> Result<PagedLoadExecutionWithTrace<E>, QueryError>
175 where
176 E: PersistedRow + EntityValue,
177 {
178 self.inner.ensure_default_read_admission()?;
179 self.execute_with_trace_unchecked()
180 }
181
182 fn execute_with_trace_unchecked(self) -> Result<PagedLoadExecutionWithTrace<E>, QueryError>
183 where
184 E: PersistedRow + EntityValue,
185 {
186 let read_intent = self.read_intent;
189 self.inner
190 .session
191 .execute_load_query_paged_with_trace(
192 self.inner.query(),
193 self.inner.cursor_token.as_deref(),
194 )
195 .map(|execution| execution.with_read_intent(read_intent))
196 }
197}