#[cfg(not(feature = "sync"))] use std::{cell::RefCell, rc::Rc}; #[cfg(feature = "sync")] use std::sync::{Arc, RwLock}; use std::{ any::{Any, TypeId}, collections::HashMap, ops::Deref, }; use crate::context::ViewContext; /// The container stores typed resource and state objects and provides /// them to component functions. #[derive(Default, Debug)] pub struct Container { bindings: HashMap>, } impl Container { /// insert a type binding into the container. This is used to provide an /// object to functions executed by Container::call. /// /// App::insert_ressource and App::isnert_state proxies to this function. pub(crate) fn bind(&mut self, val: T) { self.bindings.insert(val.type_id(), Box::new(val)); } /// Get an object from the store by its type. This is a utility function /// to extract an object directly, instead of using the container to /// inject objects into a function's arguments. pub fn get(&self) -> Option<&T> { self.bindings .get(&TypeId::of::()) .and_then(|boxed| boxed.downcast_ref()) } } /// A wrapper for state objcets. This internally holds a reference counted /// poitner to the object and is used when injecting itno functions. #[cfg(not(feature = "sync"))] pub struct State(Rc>); #[cfg(feature = "sync")] pub struct State(Arc>); impl State { /// Create a new state wrapper. #[cfg(feature = "sync")] pub fn new(val: T) -> Self { State(Arc::new(RwLock::new(val))) } #[cfg(not(feature = "sync"))] pub fn new(val: T) -> Self { State(Rc::new(RefCell::new(val))) } /// Returns a mutable reference to the underlying state object. /// /// Example: /// ``` /// use arkham::prelude::*; /// struct MyState(i32); /// /// let state = State::new(MyState(4)); /// state.get_mut().0 = 6; /// assert_eq!(state.get().0, 6); /// ``` #[cfg(feature = "sync")] pub fn get_mut(&self) -> std::sync::RwLockWriteGuard { self.0.write().unwrap() } #[cfg(not(feature = "sync"))] pub fn get_mut(&self) -> std::cell::RefMut { RefCell::borrow_mut(&self.0) } // Returns an immutable reference to the underlying state object. /// Example: /// ``` /// use arkham::prelude::*; /// struct MyState(i32); /// /// let state = State::new(MyState(4)); /// assert_eq!(state.get().0, 4); /// ``` #[cfg(feature = "sync")] pub fn get(&self) -> std::sync::RwLockReadGuard { self.0.read().unwrap() } #[cfg(not(feature = "sync"))] pub fn get(&self) -> std::cell::Ref { RefCell::borrow(&self.0) } } impl Clone for State { fn clone(&self) -> State { State(self.0.clone()) } } impl FromContainer for State { fn from_container(container: &Container) -> Self { container.get::().expect("type not found").clone() } } /// A wrapper for resources stored within the app. This wrapper is returned /// when objects are injected into component functions and provide immutable /// access #[cfg(feature = "sync")] #[derive(Debug)] pub struct Res(Arc); #[cfg(not(feature = "sync"))] #[derive(Debug)] pub struct Res(Rc); impl Res { #[cfg(feature = "sync")] pub fn new(val: T) -> Self { Res(Arc::new(val)) } #[cfg(not(feature = "sync"))] pub fn new(val: T) -> Self { Res(Rc::new(val)) } } impl Res { pub fn get(&self) -> &T { self.0.as_ref() } } impl Clone for Res { fn clone(&self) -> Res { Res(self.0.clone()) } } #[cfg(feature = "sync")] impl Deref for Res { type Target = Arc; fn deref(&self) -> &Arc { &self.0 } } #[cfg(not(feature = "sync"))] impl Deref for Res { type Target = Rc; fn deref(&self) -> &Rc { &self.0 } } impl FromContainer for Res { fn from_container(container: &Container) -> Self { container.get::().expect("type not found").clone() } } /// Callable must be implemented for functions that can be used as component /// functions. They are given a ViewContext for the component function and /// injectable arguments. pub trait Callable { fn call(&self, view: &mut ViewContext, args: Args); } impl Callable<()> for Func where Func: Fn(&mut ViewContext), { #[inline] fn call(&self, view: &mut ViewContext, _args: ()) { (self)(view); } } /// FromContainer must be implmented for objects that can be injected into /// component functions. This includes the Res and State structs. pub trait FromContainer { fn from_container(container: &Container) -> Self; } impl FromContainer for () { #[inline] fn from_container(_container: &Container) -> Self {} } macro_rules! callable_tuple ({ $($param:ident)* } => { impl Callable<($($param,)*)> for Func where Func: Fn(&mut ViewContext, $($param),*), { #[inline] #[allow(non_snake_case)] fn call(&self, view: &mut ViewContext , ($($param,)*): ($($param,)*)) { (self)(view, $($param,)*); } } }); // callable_tuple! {} callable_tuple! { A } callable_tuple! { A B } callable_tuple! { A B C } callable_tuple! { A B C D } callable_tuple! { A B C D E } callable_tuple! { A B C D E F } callable_tuple! { A B C D E F G } callable_tuple! { A B C D E F G H } callable_tuple! { A B C D E F G H I } callable_tuple! { A B C D E F G H I J } callable_tuple! { A B C D E F G H I J K } callable_tuple! { A B C D E F G H I J K L } macro_rules! tuple_from_tm { ( $($T: ident )+ ) => { impl<$($T: FromContainer),+> FromContainer for ($($T,)+) { #[inline] fn from_container(container: &Container) -> Self { ($($T::from_container(container),)+) } } }; } tuple_from_tm! { A } tuple_from_tm! { A B } tuple_from_tm! { A B C } tuple_from_tm! { A B C D } tuple_from_tm! { A B C D E } tuple_from_tm! { A B C D E F } tuple_from_tm! { A B C D E F G } tuple_from_tm! { A B C D E F G H } tuple_from_tm! { A B C D E F G H I } tuple_from_tm! { A B C D E F G H I J } tuple_from_tm! { A B C D E F G H I J K } tuple_from_tm! { A B C D E F G H I J K L }