use crate::app_work::Loadable;
use crate::app_work::StateMutator;
use crate::app_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;
use std::time::Instant;
pub type Setter<State, FinishedWorkData> =
Arc<dyn Fn(&mut State, Loadable<FinishedWorkData, eyre::Error>) + Send + Sync>;
pub struct LoadableWorkBuilder<State, FinishedWorkData>
where
FinishedWorkData: Debug + Send,
{
setter: Option<Setter<State, FinishedWorkData>>,
#[allow(clippy::type_complexity)]
on_work: Option<(
&'static Location<'static>,
Pin<Box<dyn Future<Output = eyre::Result<FinishedWorkData>> + Send>>,
)>,
is_err_if_discarded: bool,
description: String,
}
impl<State, FinishedWorkData> Default for LoadableWorkBuilder<State, FinishedWorkData>
where
FinishedWorkData: Debug + Send,
{
fn default() -> Self {
Self::new()
}
}
impl<State, FinishedWorkData> LoadableWorkBuilder<State, FinishedWorkData>
where
FinishedWorkData: 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 State, Loadable<FinishedWorkData, 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<FinishedWorkData>> + 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<
State,
impl Fn(&mut State),
impl Future<Output = eyre::Result<FieldUpdaterWorkSuccessMutator<State, FinishedWorkData>>>
+ Send,
FieldUpdaterWorkSuccessMutator<State, FinishedWorkData>,
FieldUpdaterWorkFailureMutator<State, FinishedWorkData>,
impl Fn(eyre::Error) -> FieldUpdaterWorkFailureMutator<State, FinishedWorkData> + 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 started_at = Instant::now();
let work = Work {
location,
on_enqueue: move |app: &mut State| {
(setter_for_enqueue)(app, Loadable::Loading { started_at });
},
on_work: async move {
let data = on_work.await?;
Ok(FieldUpdaterWorkSuccessMutator {
data,
setter,
started_at,
})
},
on_failure: move |err| FieldUpdaterWorkFailureMutator {
err,
setter: setter_for_failure.clone(),
started_at,
},
is_err_if_discarded: self.is_err_if_discarded,
description: self.description,
_marker: std::marker::PhantomData,
};
Ok(work)
}
}
pub struct FieldUpdaterWorkSuccessMutator<State, FinishedWorkData> {
pub data: FinishedWorkData,
pub setter: Setter<State, FinishedWorkData>,
pub started_at: Instant,
}
impl<State, FinishedWorkData> Debug for FieldUpdaterWorkSuccessMutator<State, FinishedWorkData> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FieldUpdaterWorkSuccessMutator")
.finish()
}
}
pub struct FieldUpdaterWorkFailureMutator<State, FinishedWorkData> {
pub err: eyre::Error,
pub setter: Setter<State, FinishedWorkData>,
pub started_at: Instant,
}
impl<State, FinishedWorkData> Debug for FieldUpdaterWorkFailureMutator<State, FinishedWorkData> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FieldUpdaterWorkSuccessMutator")
.finish()
}
}
impl<State: 'static, FinishedWorkData> StateMutator<State>
for FieldUpdaterWorkSuccessMutator<State, FinishedWorkData>
where
FinishedWorkData: std::fmt::Debug + Send + 'static,
{
fn mutate_state(self: Box<Self>, state: &mut State) {
(self.setter)(
state,
Loadable::Loaded {
started_at: self.started_at,
finished_at: Instant::now(),
value: self.data,
},
)
}
}
impl<State: 'static, FinishedWorkData> StateMutator<State>
for FieldUpdaterWorkFailureMutator<State, FinishedWorkData>
where
FinishedWorkData: std::fmt::Debug + Send + 'static,
{
fn mutate_state(self: Box<Self>, state: &mut State) {
(self.setter)(
state,
Loadable::Failed {
error: self.err,
started_at: self.started_at,
finished_at: Instant::now(),
},
)
}
}