use crate::ProgressEntry;
use bytes::Bytes;
use core::time::Duration;
use futures::TryStream;
pub trait PullStream<E>:
TryStream<Ok = Bytes, Error = (E, Option<Duration>)> + Send + Unpin
{
}
impl<E, T> PullStream<E> for T where
T: TryStream<Ok = Bytes, Error = (E, Option<Duration>)> + Send + Unpin
{
}
pub type PullResult<T, E> = Result<T, (E, Option<Duration>)>;
pub trait Puller: Send + Sync + Clone + 'static {
type Error: PullerError;
fn pull(
&mut self,
range: Option<&ProgressEntry>,
) -> impl Future<Output = PullResult<impl PullStream<Self::Error>, Self::Error>> + Send;
}
pub trait PullerError: std::error::Error + Send + Sync + Unpin + 'static {
fn is_irrecoverable(&self) -> bool {
false
}
}
impl PullerError for std::convert::Infallible {
fn is_irrecoverable(&self) -> bool {
#[allow(clippy::uninhabited_references)]
match *self {}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
#[derive(Debug)]
struct DefaultErr;
impl std::fmt::Display for DefaultErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("default error")
}
}
impl std::error::Error for DefaultErr {}
impl PullerError for DefaultErr {}
#[test]
fn default_is_irrecoverable_is_false() {
assert!(!DefaultErr.is_irrecoverable());
assert_eq!(format!("{DefaultErr}"), "default error");
}
#[derive(Debug)]
struct FatalErr;
impl std::fmt::Display for FatalErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("fatal error")
}
}
impl std::error::Error for FatalErr {}
impl PullerError for FatalErr {
fn is_irrecoverable(&self) -> bool {
true
}
}
#[test]
fn override_is_irrecoverable_is_true() {
assert!(FatalErr.is_irrecoverable());
assert!(!DefaultErr.is_irrecoverable());
}
#[test]
fn irrecoverable_contract_allows_dynamic_decision() {
#[derive(Debug)]
struct StatefulErr {
fatal: bool,
}
impl std::fmt::Display for StatefulErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("stateful")
}
}
impl std::error::Error for StatefulErr {}
impl PullerError for StatefulErr {
fn is_irrecoverable(&self) -> bool {
self.fatal
}
}
assert!(!StatefulErr { fatal: false }.is_irrecoverable());
assert!(StatefulErr { fatal: true }.is_irrecoverable());
}
}