diff --git a/dynatos-loadable/src/lib.rs b/dynatos-loadable/src/lib.rs index eb49dd2..9ffe474 100644 --- a/dynatos-loadable/src/lib.rs +++ b/dynatos-loadable/src/lib.rs @@ -5,6 +5,10 @@ // Modules pub mod loadable; +pub mod loadable_signal; // Exports -pub use self::loadable::{IntoLoaded, IteratorLoadableExt, Loadable}; +pub use self::{ + loadable::{IntoLoaded, IteratorLoadableExt, Loadable}, + loadable_signal::LoadableSignal, +}; diff --git a/dynatos-loadable/src/loadable_signal.rs b/dynatos-loadable/src/loadable_signal.rs new file mode 100644 index 0000000..0e3735f --- /dev/null +++ b/dynatos-loadable/src/loadable_signal.rs @@ -0,0 +1,170 @@ +//! Loadable signal + +// Imports +use { + crate::Loadable, + dynatos_reactive::{async_signal, AsyncSignal, SignalBorrow, SignalBorrowMut, SignalUpdate, SignalWith}, + std::{ + fmt, + future::Future, + ops::{Deref, DerefMut}, + }, +}; + +/// Loadable signal. +/// +/// Wrapper around an [`AsyncSignal`]. +pub struct LoadableSignal { + /// Inner + inner: AsyncSignal, +} + +impl>, T, E> LoadableSignal { + /// Creates a new loadable signal from a future + pub fn new(fut: F) -> Self { + let inner = AsyncSignal::new(fut); + Self { inner } + } + + /// Borrows the inner value, without polling the future. + pub fn borrow_inner(&self) -> Loadable, E> + where + E: Clone, + { + let borrow = self.inner.borrow_inner(); + match borrow { + Some(borrow) => match &*borrow { + Ok(_) => Loadable::Loaded(BorrowRef(borrow)), + Err(err) => Loadable::Err(err.clone()), + }, + None => Loadable::Empty, + } + } + + /// Uses the inner value, without polling the future. + pub fn with_inner(&self, f: F2) -> O + where + F2: for<'a> FnOnce(Loadable<&'a T, E>) -> O, + E: Clone, + { + let borrow = self.borrow_inner(); + f(borrow.as_deref()) + } +} + +impl Clone for LoadableSignal { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +impl>, T, E> fmt::Debug for LoadableSignal +where + T: fmt::Debug, + E: Clone + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let loadable = self.borrow_inner(); + f.debug_struct("LoadableSignal").field("loadable", &loadable).finish() + } +} + +/// Reference type for [`SignalBorrow`] impl +#[derive(Debug)] +pub struct BorrowRef<'a, T, E>(async_signal::BorrowRef<'a, Result>); + +impl<'a, T, E> Deref for BorrowRef<'a, T, E> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + .as_ref() + .unwrap_or_else(|_| panic!("Loadable should not be an error")) + } +} + +impl> + 'static, T: 'static, E: Clone + 'static> SignalBorrow for LoadableSignal { + type Ref<'a> = Loadable, E> + where + Self: 'a; + + fn borrow(&self) -> Self::Ref<'_> { + let borrow = self.inner.borrow(); + match borrow { + Some(borrow) => match &*borrow { + Ok(_) => Loadable::Loaded(BorrowRef(borrow)), + Err(err) => Loadable::Err(err.clone()), + }, + None => Loadable::Empty, + } + } +} + +impl> + 'static, T: 'static, E: Clone + 'static> SignalWith for LoadableSignal { + type Value<'a> = Loadable<&'a T, E>; + + fn with(&self, f: F2) -> O + where + F2: for<'a> FnOnce(Self::Value<'a>) -> O, + { + let value = self.borrow(); + f(value.as_deref()) + } +} + +/// Reference type for [`SignalBorrowMut`] impl +#[derive(Debug)] +pub struct BorrowRefMut<'a, T, E>(async_signal::BorrowRefMut<'a, Result>); + +impl<'a, T, E> Deref for BorrowRefMut<'a, T, E> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + .as_ref() + .unwrap_or_else(|_| panic!("Loadable should not be an error")) + } +} + +impl<'a, T, E> DerefMut for BorrowRefMut<'a, T, E> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + .as_mut() + .unwrap_or_else(|_| panic!("Loadable should not be an error")) + } +} + +impl>, T: 'static, E: Clone + 'static> SignalBorrowMut for LoadableSignal { + type RefMut<'a> = Loadable, E> + where + Self: 'a; + + fn borrow_mut(&self) -> Self::RefMut<'_> { + let borrow = self.inner.borrow_mut(); + match borrow { + Some(borrow) => match &*borrow { + Ok(_) => Loadable::Loaded(BorrowRefMut(borrow)), + Err(err) => Loadable::Err(err.clone()), + }, + None => Loadable::Empty, + } + } +} + +/// Updates the value within the loadable signal. +impl>, T: 'static, E: Clone + 'static> SignalUpdate for LoadableSignal +where + F::Output: 'static, +{ + type Value<'a> = Loadable<&'a mut T, E>; + + fn update(&self, f: F2) -> O + where + F2: for<'a> FnOnce(Self::Value<'a>) -> O, + { + let mut value = self.borrow_mut(); + f(value.as_deref_mut()) + } +}