//! Loadable value // Imports use std::{ convert::Infallible, ops::{ControlFlow, FromResidual, Try}, }; /// Loadable value. #[derive(Clone, Copy, Debug)] pub enum Loadable { /// Empty Empty, /// Failed to load Err(E), /// Loaded Loaded(T), } impl Loadable { /// Creates a loadable from a result pub fn from_res(res: Result) -> Self where E: From, { match res { Ok(value) => Self::Loaded(value), Err(err) => Self::Err(err.into()), } } /// Returns if the loadable is empty. #[must_use] pub fn is_empty(&self) -> bool { matches!(self, Self::Empty) } /// Returns if the loadable is loaded. /// /// This means it's either an error or a value #[must_use] pub fn is_loaded(&self) -> bool { !self.is_empty() } /// Returns this loadable's value by reference. pub fn as_ref(&self) -> Loadable<&T, E> where E: Clone, { match self { Self::Empty => Loadable::Empty, Self::Err(err) => Loadable::Err(err.clone()), Self::Loaded(value) => Loadable::Loaded(value), } } /// Returns this loadable's value by mutable reference pub fn as_mut(&mut self) -> Loadable<&mut T, E> where E: Clone, { match self { Self::Empty => Loadable::Empty, Self::Err(err) => Loadable::Err(err.clone()), Self::Loaded(value) => Loadable::Loaded(value), } } /// Maps this loadable's value pub fn map(self, f: F) -> Loadable where F: FnOnce(T) -> U, { match self { Self::Empty => Loadable::Empty, Self::Err(err) => Loadable::Err(err), Self::Loaded(value) => Loadable::Loaded(f(value)), } } /// Zips two loadable. /// /// If is empty, the result will be empty. /// If any is errored, the result will be an error. pub fn zip(self, rhs: Loadable) -> Loadable<(T, U), E> { match (self, rhs) { // If there's an error, propagate (Self::Err(err), _) | (_, Loadable::Err(err)) => Loadable::Err(err), // Otherwise, if we have both values, return loaded (Self::Loaded(lhs), Loadable::Loaded(rhs)) => Loadable::Loaded((lhs, rhs)), // Otherwise, we're empty _ => Loadable::Empty, } } /// Chains this loadable with another if it's loaded /// /// If any operation returns empty or error, it will be propagated pub fn and_then(self, f: F) -> Loadable where F: FnOnce(T) -> Loadable, { match self { Self::Empty => Loadable::Empty, Self::Err(err) => Loadable::Err(err), Self::Loaded(value) => f(value), } } /// Converts this to an option. /// /// Maps `Loadable::Loaded` to `Some` and the rest to `None`. pub fn loaded(self) -> Option { match self { Self::Empty => None, Self::Err(_err) => None, Self::Loaded(value) => Some(value), } } } impl Loadable<&T, E> { /// Clones the inner value pub fn cloned(self) -> Loadable where T: Clone, { self.map(T::clone) } } impl From for Loadable { fn from(value: T) -> Self { Self::Loaded(value) } } impl Try for Loadable { type Output = T; type Residual = Loadable; fn from_output(output: Self::Output) -> Self { Self::Loaded(output) } fn branch(self) -> ControlFlow { match self { Self::Empty => ControlFlow::Break(Loadable::Empty), Self::Err(err) => ControlFlow::Break(Loadable::Err(err)), Self::Loaded(value) => ControlFlow::Continue(value), } } } impl FromResidual> for Loadable where E: From, { fn from_residual(residual: Loadable) -> Self { match residual { Loadable::Empty => Self::Empty, Loadable::Err(err) => Self::Err(err.into()), Loadable::Loaded(never) => never, } } } impl FromResidual> for Loadable where E: From, { fn from_residual(residual: Result) -> Self { match residual { Ok(never) => match never {}, Err(err) => Self::Err(err.into()), } } } /// Collects an iterator of `Loadable` into a `Loadable`, /// where `C` is a collection of `T`s. /// /// If any empty, or error loadables are found, this immediately short-circuits /// and returns them impl FromIterator> for Loadable where C: Default + Extend, { fn from_iter>>(iter: I) -> Self { let mut collection = C::default(); for item in iter.into_iter() { // If we find any empty, or errors, return them immediately let item = match item { Loadable::Empty => return Self::Empty, Loadable::Err(err) => return Self::Err(err), Loadable::Loaded(value) => value, }; collection.extend_one(item); } Self::Loaded(collection) } } /// Extension trait for iterators of `Loadable` #[extend::ext(name = IteratorLoadableExt)] pub impl I where I: Iterator>, { /// Flattens an iterator of `Loadable` to `Loadable`, where `T: IntoIterator` fn flatten_loaded(self) -> FlattenLoaded where T: IntoIterator, { FlattenLoaded { inner: self, value_it: None, } } /// Finds the position of a value in an iterator of `Loadable`. fn position_loaded(self, mut pred: F) -> Loadable, E> where F: FnMut(T) -> bool, { for (item_idx, item) in self.enumerate() { let item = item?; if pred(item) { return Loadable::Loaded(Some(item_idx)); } } Loadable::Loaded(None) } /// [`Iterator::scan`]-like adaptor fn scan_loaded(self, init: St, f: F) -> ScanLoaded where F: FnMut(&mut St, T) -> Option, { ScanLoaded { inner: self, f, state: init, } } } /// Iterator returned by [`IteratorLoadableExt::flatten_loaded`] // TODO: Impl `Clone, Copy, Debug` with the correct bounds. #[derive(Clone, Copy, Debug)] pub struct FlattenLoaded where I: Iterator>, T: IntoIterator, { /// Inner iterator inner: I, /// Current value iterator value_it: Option, } impl Iterator for FlattenLoaded where I: Iterator>, T: IntoIterator, { type Item = Loadable; fn next(&mut self) -> Option { // Loop until we find the next value loop { // If we have a value iterator, try to yield it first if let Some(it) = &mut self.value_it { match it.next() { // If there was still a value, yield it Some(value) => return Some(Loadable::Loaded(value)), // Otherwise, get rid of the iterator None => self.value_it = None, } } // If the inner value didn't have anything, try to get the next value match self.inner.next()? { // If empty, or error, return them Loadable::Empty => return Some(Loadable::Empty), Loadable::Err(err) => return Some(Loadable::Err(err)), // On loaded, set the value iterator and try to extract it again Loadable::Loaded(iter) => self.value_it = Some(iter.into_iter()), } } } } /// Iterator returned by [`IteratorLoadableExt::scan_loaded`] #[derive(Clone, Copy, Debug)] pub struct ScanLoaded { /// Inner iterator inner: I, /// Function f: F, /// State state: St, } impl Iterator for ScanLoaded where I: Iterator>, F: FnMut(&mut St, T) -> Option, { type Item = Loadable; fn next(&mut self) -> Option { let value = match self.inner.next()? { Loadable::Empty => return Some(Loadable::Empty), Loadable::Err(err) => return Some(Loadable::Err(err)), Loadable::Loaded(value) => value, }; (self.f)(&mut self.state, value).map(Loadable::Loaded) } } /// Extension trait to create a [`Loadable::Loaded`] from a value. #[extend::ext(name = IntoLoaded)] pub impl T { /// Converts this `T` value into a loaded `Loadable` fn into_loaded(self) -> Loadable { Loadable::Loaded(self) } }