cloud_sdk/pagination/
driver.rs1use core::fmt;
2
3use super::PaginationError;
4
5pub trait PageStrategy {
7 type Request: Copy;
9 type Observation<'observation>;
11 type Boundary;
13
14 fn next_request(&self) -> Result<Self::Request, PaginationError>;
16
17 fn observe<'observation>(
19 &mut self,
20 observation: Self::Observation<'observation>,
21 ) -> Result<Self::Boundary, PaginationError>;
22}
23
24#[derive(Clone, Copy, Debug, Eq, PartialEq)]
26pub enum PagerControl {
27 Continue,
29 Cancel,
31}
32
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35pub enum PagerStep<R> {
36 Request(R),
38 Complete,
40 Cancelled,
42}
43
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub enum PagerDriverError {
47 ResponsePending,
49 UnexpectedObservation,
51 Terminal,
53 Strategy(PaginationError),
55}
56
57impl fmt::Display for PagerDriverError {
58 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
59 formatter.write_str(match self {
60 Self::ResponsePending => "a pagination response is still pending",
61 Self::UnexpectedObservation => "pagination observation has no admitted request",
62 Self::Terminal => "pagination driver already reached a terminal state",
63 Self::Strategy(_) => "pagination strategy rejected the response",
64 })
65 }
66}
67
68impl core::error::Error for PagerDriverError {}
69
70pub struct PagerDriver<S> {
79 strategy: S,
80 response_pending: bool,
81 terminal: bool,
82}
83
84impl<S> PagerDriver<S>
85where
86 S: PageStrategy,
87{
88 #[must_use]
90 pub const fn new(strategy: S) -> Self {
91 Self {
92 strategy,
93 response_pending: false,
94 terminal: false,
95 }
96 }
97
98 #[must_use]
100 pub const fn strategy(&self) -> &S {
101 &self.strategy
102 }
103
104 #[must_use]
106 pub const fn is_terminal(&self) -> bool {
107 self.terminal
108 }
109
110 pub fn next_request(
112 &mut self,
113 control: PagerControl,
114 ) -> Result<PagerStep<S::Request>, PagerDriverError> {
115 if self.terminal {
116 return Err(PagerDriverError::Terminal);
117 }
118 if control == PagerControl::Cancel {
119 self.terminal = true;
120 self.response_pending = false;
121 return Ok(PagerStep::Cancelled);
122 }
123 if self.response_pending {
124 return Err(PagerDriverError::ResponsePending);
125 }
126 match self.strategy.next_request() {
127 Ok(request) => {
128 self.response_pending = true;
129 Ok(PagerStep::Request(request))
130 }
131 Err(PaginationError::Complete) => {
132 self.terminal = true;
133 Ok(PagerStep::Complete)
134 }
135 Err(error) => Err(PagerDriverError::Strategy(error)),
136 }
137 }
138
139 pub fn observe<'observation>(
141 &mut self,
142 observation: S::Observation<'observation>,
143 ) -> Result<S::Boundary, PagerDriverError> {
144 if self.terminal {
145 return Err(PagerDriverError::Terminal);
146 }
147 if !self.response_pending {
148 return Err(PagerDriverError::UnexpectedObservation);
149 }
150 let boundary = self
151 .strategy
152 .observe(observation)
153 .map_err(PagerDriverError::Strategy)?;
154 self.response_pending = false;
155 Ok(boundary)
156 }
157}
158
159impl<S> fmt::Debug for PagerDriver<S>
160where
161 S: PageStrategy + fmt::Debug,
162{
163 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
164 formatter
165 .debug_struct("PagerDriver")
166 .field("strategy", &self.strategy)
167 .field("response_pending", &self.response_pending)
168 .field("terminal", &self.terminal)
169 .finish()
170 }
171}