mirror of
https://github.com/Zenithsiz/dynatos.git
synced 2026-02-06 19:54:20 +00:00
Added dynatos-loadable.
Added `dynatos_loadable::Loadable`.
This commit is contained in:
parent
24088b11f3
commit
1e7085a4a1
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -64,6 +64,21 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dynatos-loadable"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"duplicate",
|
||||
"dynatos-reactive",
|
||||
"dynatos-util",
|
||||
"extend",
|
||||
"js-sys",
|
||||
"tracing",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dynatos-reactive"
|
||||
version = "0.1.0"
|
||||
|
||||
@ -4,6 +4,7 @@ members = [
|
||||
"dynatos",
|
||||
"dynatos-context",
|
||||
"dynatos-html",
|
||||
"dynatos-loadable",
|
||||
"dynatos-reactive",
|
||||
"dynatos-router",
|
||||
"dynatos-title",
|
||||
@ -17,6 +18,7 @@ resolver = "2"
|
||||
dynatos = { path = "dynatos" }
|
||||
dynatos-context = { path = "dynatos-context" }
|
||||
dynatos-html = { path = "dynatos-html" }
|
||||
dynatos-loadable = { path = "dynatos-loadable" }
|
||||
dynatos-reactive = { path = "dynatos-reactive" }
|
||||
dynatos-router = { path = "dynatos-router" }
|
||||
dynatos-title = { path = "dynatos-title" }
|
||||
|
||||
17
dynatos-loadable/Cargo.toml
Normal file
17
dynatos-loadable/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "dynatos-loadable"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
dynatos-reactive = { workspace = true }
|
||||
dynatos-util = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
duplicate = { workspace = true }
|
||||
extend = { workspace = true }
|
||||
js-sys = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
wasm-bindgen = { workspace = true }
|
||||
web-sys = { workspace = true }
|
||||
10
dynatos-loadable/src/lib.rs
Normal file
10
dynatos-loadable/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
||||
//! Loadable values for [`dynatos`]
|
||||
|
||||
// Features
|
||||
#![feature(try_trait_v2, lint_reasons, never_type)]
|
||||
|
||||
// Modules
|
||||
mod loadable;
|
||||
|
||||
// Exports
|
||||
pub use loadable::{IntoLoaded, Loadable};
|
||||
202
dynatos-loadable/src/loadable.rs
Normal file
202
dynatos-loadable/src/loadable.rs
Normal file
@ -0,0 +1,202 @@
|
||||
//! Loadable value
|
||||
|
||||
// Imports
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
ops::{ControlFlow, FromResidual, Try},
|
||||
};
|
||||
|
||||
/// Loadable value.
|
||||
#[derive(Debug)]
|
||||
pub enum Loadable<T, E> {
|
||||
/// Empty
|
||||
Empty,
|
||||
|
||||
/// Failed to load
|
||||
Err(E),
|
||||
|
||||
/// Loaded
|
||||
Loaded(T),
|
||||
}
|
||||
|
||||
impl<T, E> Loadable<T, E> {
|
||||
/// Creates a loadable from a result
|
||||
pub fn from_res<E2>(res: Result<T, E2>) -> Self
|
||||
where
|
||||
E: From<E2>,
|
||||
{
|
||||
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<U, F>(self, f: F) -> Loadable<U, E>
|
||||
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<U>(self, rhs: Loadable<U, E>) -> 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<U, F>(self, f: F) -> Loadable<U, E>
|
||||
where
|
||||
F: FnOnce(T) -> Loadable<U, E>,
|
||||
{
|
||||
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<T> {
|
||||
match self {
|
||||
Self::Empty => None,
|
||||
Self::Err(_err) => None,
|
||||
Self::Loaded(value) => Some(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Loadable<&T, E> {
|
||||
/// Clones the inner value
|
||||
pub fn cloned(self) -> Loadable<T, E>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
self.map(T::clone)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, E: Clone> Clone for Loadable<T, E> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Empty => Self::Empty,
|
||||
Self::Err(err) => Self::Err(err.clone()),
|
||||
Self::Loaded(value) => Self::Loaded(value.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> From<T> for Loadable<T, E> {
|
||||
fn from(value: T) -> Self {
|
||||
Self::Loaded(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Loadable<T, E> {
|
||||
type Output = T;
|
||||
type Residual = Loadable<!, E>;
|
||||
|
||||
fn from_output(output: Self::Output) -> Self {
|
||||
Self::Loaded(output)
|
||||
}
|
||||
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
match self {
|
||||
Self::Empty => ControlFlow::Break(Loadable::Empty),
|
||||
Self::Err(err) => ControlFlow::Break(Loadable::Err(err)),
|
||||
Self::Loaded(value) => ControlFlow::Continue(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E, E2> FromResidual<Loadable<!, E2>> for Loadable<T, E>
|
||||
where
|
||||
E: From<E2>,
|
||||
{
|
||||
fn from_residual(residual: Loadable<!, E2>) -> Self {
|
||||
match residual {
|
||||
Loadable::Empty => Self::Empty,
|
||||
Loadable::Err(err) => Self::Err(err.into()),
|
||||
Loadable::Loaded(never) => never,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E, E2> FromResidual<Result<Infallible, E2>> for Loadable<T, E>
|
||||
where
|
||||
E: From<E2>,
|
||||
{
|
||||
fn from_residual(residual: Result<Infallible, E2>) -> Self {
|
||||
match residual {
|
||||
Ok(never) => match never {},
|
||||
Err(err) => Self::Err(err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait to create a [`Loadable::Loaded`] from a value.
|
||||
#[extend::ext(name = IntoLoaded)]
|
||||
pub impl<T> T {
|
||||
/// Converts this `T` value into a loaded `Loadable<T>`
|
||||
fn into_loaded<E>(self) -> Loadable<T, E> {
|
||||
Loadable::Loaded(self)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user