use core::fmt;
use super::PaginationError;
pub trait PageStrategy {
type Request: Copy;
type Observation<'observation>;
type Boundary;
fn next_request(&self) -> Result<Self::Request, PaginationError>;
fn observe<'observation>(
&mut self,
observation: Self::Observation<'observation>,
) -> Result<Self::Boundary, PaginationError>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PagerControl {
Continue,
Cancel,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PagerStep<R> {
Request(R),
Complete,
Cancelled,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PagerDriverError {
ResponsePending,
UnexpectedObservation,
Terminal,
Strategy(PaginationError),
}
impl fmt::Display for PagerDriverError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::ResponsePending => "a pagination response is still pending",
Self::UnexpectedObservation => "pagination observation has no admitted request",
Self::Terminal => "pagination driver already reached a terminal state",
Self::Strategy(_) => "pagination strategy rejected the response",
})
}
}
impl core::error::Error for PagerDriverError {}
pub struct PagerDriver<S> {
strategy: S,
response_pending: bool,
terminal: bool,
}
impl<S> PagerDriver<S>
where
S: PageStrategy,
{
#[must_use]
pub const fn new(strategy: S) -> Self {
Self {
strategy,
response_pending: false,
terminal: false,
}
}
#[must_use]
pub const fn strategy(&self) -> &S {
&self.strategy
}
#[must_use]
pub const fn is_terminal(&self) -> bool {
self.terminal
}
pub fn next_request(
&mut self,
control: PagerControl,
) -> Result<PagerStep<S::Request>, PagerDriverError> {
if self.terminal {
return Err(PagerDriverError::Terminal);
}
if control == PagerControl::Cancel {
self.terminal = true;
self.response_pending = false;
return Ok(PagerStep::Cancelled);
}
if self.response_pending {
return Err(PagerDriverError::ResponsePending);
}
match self.strategy.next_request() {
Ok(request) => {
self.response_pending = true;
Ok(PagerStep::Request(request))
}
Err(PaginationError::Complete) => {
self.terminal = true;
Ok(PagerStep::Complete)
}
Err(error) => Err(PagerDriverError::Strategy(error)),
}
}
pub fn observe<'observation>(
&mut self,
observation: S::Observation<'observation>,
) -> Result<S::Boundary, PagerDriverError> {
if self.terminal {
return Err(PagerDriverError::Terminal);
}
if !self.response_pending {
return Err(PagerDriverError::UnexpectedObservation);
}
let boundary = self
.strategy
.observe(observation)
.map_err(PagerDriverError::Strategy)?;
self.response_pending = false;
Ok(boundary)
}
}
impl<S> fmt::Debug for PagerDriver<S>
where
S: PageStrategy + fmt::Debug,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("PagerDriver")
.field("strategy", &self.strategy)
.field("response_pending", &self.response_pending)
.field("terminal", &self.terminal)
.finish()
}
}