use crate::app::MyApp;
use crate::loadable::Loadable;
use crate::state_mutator::StateMutator;
use crate::work::Work;
use eyre::bail;
use std::fmt::Debug;
use std::fmt::Display;
use std::future::Future;
use std::panic::Location;
use std::pin::Pin;
use std::sync::Arc;
pub type Setter<T> = Arc<dyn Fn(&mut MyApp, Loadable<T, eyre::Error>) + Send + Sync>;
pub struct LoadableWorkBuilder<T>
where
T: Debug + Send,
{
setter: Option<Setter<T>>,
#[allow(clippy::type_complexity)]
on_work: Option<(
&'static Location<'static>,
Pin<Box<dyn Future<Output = eyre::Result<T>> + Send>>,
)>,
is_err_if_discarded: bool,
description: String,
}
impl<T> Default for LoadableWorkBuilder<T>
where
T: Debug + Send,
{
fn default() -> Self {
Self::new()
}
}
impl<T> LoadableWorkBuilder<T>
where
T: Debug + Send,
{
pub fn new() -> Self {
Self {
setter: None,
on_work: None,
is_err_if_discarded: false,
description: String::new(),
}
}
pub fn description(mut self, description: impl Display) -> Self {
self.description = description.to_string();
self
}
pub fn is_err_if_discarded(mut self, is_err_if_discarded: bool) -> Self {
self.is_err_if_discarded = is_err_if_discarded;
self
}
pub fn setter<G>(mut self, setter: G) -> Self
where
G: Fn(&mut MyApp, Loadable<T, eyre::Error>) + Send + Sync + 'static,
{
self.setter = Some(Arc::new(setter));
self
}
#[track_caller]
pub fn work<F>(mut self, future: F) -> Self
where
F: Future<Output = eyre::Result<T>> + Send + 'static,
{
self.on_work = Some((std::panic::Location::caller(), Box::pin(future)));
self
}
#[allow(clippy::type_complexity)]
pub fn build(
self,
) -> eyre::Result<
Work<
impl Fn(&mut MyApp),
impl Future<Output = eyre::Result<FieldUpdaterWorkSuccessMutator<T>>> + Send,
FieldUpdaterWorkSuccessMutator<T>,
FieldUpdaterWorkFailureMutator<T>,
impl Fn(eyre::Error) -> FieldUpdaterWorkFailureMutator<T> + Send,
>,
> {
let Some(setter) = self.setter else {
bail!("Setter was not set!");
};
let Some((location, on_work)) = self.on_work else {
bail!("Work future was not set!");
};
let setter_for_enqueue = setter.clone();
let setter_for_failure = setter.clone();
let work = Work {
location,
on_enqueue: move |app: &mut MyApp| {
(setter_for_enqueue)(app, Loadable::Loading);
},
on_work: async move {
let data = on_work.await?;
Ok(FieldUpdaterWorkSuccessMutator { data, setter })
},
on_failure: move |err| FieldUpdaterWorkFailureMutator {
err,
setter: setter_for_failure.clone(),
},
is_err_if_discarded: self.is_err_if_discarded,
description: self.description,
};
Ok(work)
}
}
pub struct FieldUpdaterWorkSuccessMutator<T> {
pub data: T,
pub setter: Setter<T>,
}
impl<T> Debug for FieldUpdaterWorkSuccessMutator<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FieldUpdaterWorkSuccessMutator")
.finish()
}
}
pub struct FieldUpdaterWorkFailureMutator<T> {
pub err: eyre::Error,
pub setter: Setter<T>,
}
impl<T> Debug for FieldUpdaterWorkFailureMutator<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FieldUpdaterWorkSuccessMutator")
.finish()
}
}
impl<T> StateMutator for FieldUpdaterWorkSuccessMutator<T>
where
T: std::fmt::Debug + Send + 'static,
{
fn mutate_state(self: Box<Self>, state: &mut MyApp) {
(self.setter)(state, Loadable::Loaded(self.data))
}
}
impl<T> StateMutator for FieldUpdaterWorkFailureMutator<T>
where
T: std::fmt::Debug + Send + 'static,
{
fn mutate_state(self: Box<Self>, state: &mut MyApp) {
(self.setter)(state, Loadable::Failed(self.err))
}
}