use failure::Error; use std::cell::RefCell; use std::fmt; use std::rc::{Rc, Weak}; use crate::api; use crate::utils::callbacks; // -------------------- callbacks -------------------- /// Callback registration. /// /// Callback will be unregistered when this is dropped. #[derive(Default, Debug)] pub struct CallbackRegistration(callbacks::CallbackRegistration<()>); #[derive(Debug)] pub(super) struct Callbacks(callbacks::Callbacks<()>); impl Callbacks { pub(super) fn new() -> Self { Self(callbacks::Callbacks::new()) } pub(super) fn register(&self, callback: yew::Callback<()>) -> CallbackRegistration { CallbackRegistration(self.0.register(callback)) } pub(super) fn emit(&self) { self.0.emit(()); } } impl Drop for Callbacks { fn drop(&mut self) { // final emit to let all know we're gone self.emit(); } } // -------------------- model data wrapper -------------------- #[derive(Debug)] pub(super) struct ModelBase { callbacks: Callbacks, service: Rc>, } impl Clone for ModelBase { fn clone(&self) -> Self { Self { callbacks: Callbacks::new(), // always fresh callbacks service: self.service.clone(), } } } impl From<()> for ModelBase { fn from(_: ()) -> Self { Self { callbacks: Callbacks::new(), service: Rc::new(api::APIService::new_no_message()), } } } impl From<&ModelHandle> for ModelBase { fn from(model: &ModelHandle) -> Self { model.inner.base.clone() } } #[derive(Debug)] struct Inner { base: ModelBase, result: RefCell>>>, fetch: RefCell>, custom: T, } mod hidden { pub trait Model: Sized + std::fmt::Debug { type FetchData: std::fmt::Debug; const NAME: &'static str; fn refresh(sr: &super::ModelHandle) -> Option; } } pub(super) use self::hidden::Model; pub struct ModelHandle { inner: Rc>, } impl ModelHandle { pub(super) fn new(s: T, b: B) -> Self where B: Into, { let inner = Rc::new(Inner { base: b.into(), result: Default::default(), fetch: Default::default(), custom: s, }); Self { inner, } } pub(super) fn service(&self) -> &api::APIService<()> { &self.inner.base.service } pub(super) fn data(&self) -> &T { &self.inner.custom } #[allow(unused)] pub(super) fn merge_received(&self, result: Result, load: L, merge: M) where T::FetchData: Clone, M: FnOnce(&mut T::FetchData, I), L: FnOnce(I) -> T::FetchData, { let mut data = self.inner.result.borrow_mut(); self.inner.fetch.replace(None); // result: RefCell>>>, match result { Ok(v) => { if let Some(data) = &mut *data { let data = Rc::make_mut(data); if let Ok(data) = data { // merge with existing data merge(data, v); } else { // overwrite error *data = Ok(load(v)); } } else { // first reply *data = Some(Rc::new(Ok(load(v)))); } }, Err(e) => { if data.is_none() { // first reply, remember error *data = Some(Rc::new(Err(format!("{}", e)))); } }, } drop(data); // release lock before notify self.inner.base.callbacks.emit(); } pub(super) fn set_received(&self, result: Result) { let mut data = self.inner.result.borrow_mut(); self.inner.fetch.replace(None); // result: RefCell>>>, match result { Ok(v) => { *data = Some(Rc::new(Ok(v))); }, Err(e) => { if data.is_none() { // first reply, remember error *data = Some(Rc::new(Err(format!("{}", e)))); } }, } drop(data); // release lock before notify self.inner.base.callbacks.emit(); } #[allow(unused)] pub(super) fn downgrade(&self) -> ModelWeakHandle { ModelWeakHandle { inner: Rc::downgrade(&self.inner), } } /// Refresh data pub fn refresh(&self) { let mut fetch = self.inner.fetch.borrow_mut(); if fetch.is_some() { return; } // pending request *fetch = T::refresh(self); } /// Whether a request is pending (doesn't mean no previous data is available) pub fn is_pending(&self) -> bool { let fetch = self.inner.fetch.borrow(); fetch.is_some() } /// Get data. /// /// If all fetches so far returned in an error returns the first error; /// if no data was received yet (and no error was returned) an initial /// refresh will be triggered. pub fn get(&self) -> Option>> { let result = self.inner.result.borrow(); if result.is_none() { self.refresh(); } result.clone() } /// Register a callback to be called when data is refreshed. pub fn register(&self, callback: yew::Callback<()>) -> CallbackRegistration { self.inner.base.callbacks.register(callback) } } impl Clone for ModelHandle { fn clone(&self) -> Self { Self { inner: self.inner.clone(), } } } impl From<&'_ ModelHandle> for ModelHandle { fn from(r: &'_ ModelHandle) -> Self { Self { inner: r.inner.clone(), } } } impl PartialEq for ModelHandle { fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.inner, &other.inner) } } impl Eq for ModelHandle { } impl fmt::Debug for ModelHandle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("ModelHandle<")?; f.write_str(T::NAME)?; f.write_str("(")?; self.inner.fmt(f)?; f.write_str(")")?; Ok(()) } } #[allow(unused)] pub struct ModelWeakHandle { inner: Weak>, } impl ModelWeakHandle { #[allow(unused)] pub(super) fn upgrade(&self) -> Option> { Some(ModelHandle { inner: self.inner.upgrade()?, }) } } impl fmt::Debug for ModelWeakHandle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("ModelWeakHandle<")?; f.write_str(T::NAME)?; f.write_str(">") } }