use crate::NeqAssign;
mod action;
mod error;
mod request;
mod state;
pub use self::action::*;
pub use self::error::*;
pub use self::request::*;
pub use self::state::*;
use std::future::Future;
use wasm_bindgen::__rt::core::marker::PhantomData;
pub type DidChange = bool;
pub type AcquireFetch<T> = Fetch<(), T>;
pub type ModifyFetch<T> = Fetch<T, T>;
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Fetch<REQ, RES> {
request: REQ,
response: FetchState<RES>,
}
impl<REQ: PartialEq, RES> Fetch<REQ, RES> {
pub fn set_req(&mut self, request: REQ) -> DidChange {
self.request.neq_assign(request)
}
}
impl<REQ: Default, RES: PartialEq> Fetch<REQ, RES> {
pub fn set_fetched(&mut self, res: RES) -> DidChange {
let will_change = match &self.response {
FetchState::Fetched(old_res) => &res == old_res,
_ => true,
};
self.response = std::mem::take(&mut self.response).fetched(res);
will_change
}
pub fn apply(&mut self, action: FetchAction<RES>) -> DidChange {
match action {
FetchAction::NotFetching => self.set_not_fetching(),
FetchAction::Fetching => self.set_fetching(),
FetchAction::Fetched(res) => self.set_fetched(res),
FetchAction::Failed(err) => self.set_failed(err),
}
}
}
impl<REQ, RES> Fetch<REQ, RES> {
pub fn new(request: REQ) -> Self {
Self {
request,
response: Default::default(),
}
}
pub fn set_not_fetching(&mut self) -> DidChange {
let will_change = self
.response
.discriminant_differs(&FetchState::NotFetching(None));
self.response = std::mem::take(&mut self.response).not_fetching();
will_change
}
pub fn set_fetching(&mut self) -> DidChange {
let will_change = self
.response
.discriminant_differs(&FetchState::Fetching(None));
self.response = std::mem::take(&mut self.response).fetching();
will_change
}
pub fn set_failed(&mut self, err: FetchError) -> DidChange {
let will_change = match &self.response {
FetchState::Failed(_, old_err) => &err == old_err,
_ => true,
};
self.response = std::mem::take(&mut self.response).failed(err);
will_change
}
pub fn fetch_convert<T: FetchRequest, Msg>(
&self,
to_request: impl Fn(&Self) -> &T,
to_msg: impl Fn(FetchAction<T::ResponseBody>) -> Msg,
) -> impl Future<Output = Msg> {
let request: &T = to_request(self);
let request = create_request(request);
let req_type: PhantomData<T> = PhantomData;
async move {
let fetch_state = match fetch_resource(request, req_type).await {
Ok(response) => FetchAction::Fetched(response),
Err(err) => FetchAction::Failed(err),
};
to_msg(fetch_state)
}
}
pub fn map<NewRes, F: Fn(Fetch<REQ, RES>) -> Fetch<REQ, NewRes>>(
self,
f: F,
) -> Fetch<REQ, NewRes> {
f(self)
}
pub fn unwrap(self) -> RES {
self.res().expect("No response body is present.")
}
pub fn res(self) -> Option<RES> {
match self.response {
FetchState::NotFetching(res) => res,
FetchState::Fetching(res) => res,
FetchState::Fetched(res) => Some(res),
FetchState::Failed(res, _) => res,
}
}
pub fn req(self) -> REQ {
self.request
}
pub fn state(self) -> FetchState<RES> {
self.response
}
pub fn as_ref(&self) -> Fetch<&REQ, &RES> {
let response = match &self.response {
FetchState::NotFetching(res) => FetchState::NotFetching(res.as_ref()),
FetchState::Fetching(res) => FetchState::Fetching(res.as_ref()),
FetchState::Fetched(res) => FetchState::Fetched(res),
FetchState::Failed(res, err) => FetchState::Failed(res.as_ref(), err.clone()),
};
Fetch {
request: &self.request,
response,
}
}
pub fn as_mut(&mut self) -> Fetch<&mut REQ, &mut RES> {
let response = match &mut self.response {
FetchState::NotFetching(res) => FetchState::NotFetching(res.as_mut()),
FetchState::Fetching(res) => FetchState::Fetching(res.as_mut()),
FetchState::Fetched(res) => FetchState::Fetched(res),
FetchState::Failed(res, err) => FetchState::Failed(res.as_mut(), err.clone()),
};
Fetch {
request: &mut self.request,
response,
}
}
}
impl<REQ: FetchRequest> Fetch<REQ, REQ::ResponseBody> {
pub fn fetch<Msg>(
&self,
to_msg: impl Fn(FetchAction<REQ::ResponseBody>) -> Msg,
) -> impl Future<Output = Msg> {
let request = self.as_ref().req();
let request = create_request(request);
let req_type: PhantomData<REQ> = PhantomData;
async move {
let fetch_state = match fetch_resource(request, req_type).await {
Ok(response) => FetchAction::Fetched(response),
Err(err) => FetchAction::Failed(err),
};
to_msg(fetch_state)
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::sync::Arc;
#[test]
fn setting_fetching_state_doesnt_change_strong_count() {
let data: Arc<i32> = Arc::new(22);
let cloned_data: Arc<i32> = data.clone();
assert_eq!(Arc::strong_count(&data), 2);
let mut fs: Fetch<Arc<i32>, ()> = Fetch::new(cloned_data);
fs.set_fetching();
assert_eq!(Arc::strong_count(&data), 2);
assert_eq!(FetchState::Fetching(None), fs.response)
}
#[test]
fn setting_fetched_state() {
let mut fs = Fetch {
request: (),
response: FetchState::Fetching(None),
};
assert!(fs.set_fetched("SomeValue".to_string()));
assert_eq!(fs.response, FetchState::Fetched("SomeValue".to_string()));
}
#[test]
fn setting_fetching_from_fetched() {
let mut fs = Fetch {
request: (),
response: FetchState::Fetched("Lorem".to_string()),
};
assert!(fs.set_fetching());
assert_eq!(fs.response, FetchState::Fetching(Some("Lorem".to_string())));
}
}