diff --git a/src/component.rs b/src/component.rs new file mode 100644 index 0000000..4d923a5 --- /dev/null +++ b/src/component.rs @@ -0,0 +1,75 @@ +use std::cell::RefCell; +use std::ops::Deref; +use std::rc::Rc; + +use super::context::Context; +use super::error::{AlreadyRegisteredError, Error, NotRegisteredError}; + +pub struct Component(Rc>); + +impl Component { + pub fn from(t: T) -> Self { + Component(Rc::new(RefCell::new(t))) + } +} + +impl Deref for Component { + type Target = RefCell; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Clone for Component { + fn clone(&self) -> Self { + Component(self.0.clone()) + } +} + +pub trait ContextComponentExtension { + fn get_component(&self) -> Option>; + + fn register_component( + &mut self, + t: T, + ) -> Result, (AlreadyRegisteredError, T)>; + + fn replace_component(&self, t: T) -> Result<(Component, T), (Error, T)>; +} + +impl ContextComponentExtension for Context { + fn get_component(&self) -> Option> { + self.get::>().cloned() + } + + fn register_component( + &mut self, + t: T, + ) -> Result, (AlreadyRegisteredError, T)> { + if self.get::().is_none() { + let component = Component::from(t); + self.set(component.clone()); + Ok(component) + } else { + Err((AlreadyRegisteredError::of_type::>(), t)) + } + } + + fn replace_component(&self, mut t: T) -> Result<(Component, T), (Error, T)> { + if let Some(component) = self.get_component::() { + match component.try_borrow_mut() { + Err(borrow_mut_error) => return Err((Error::Borrowed(borrow_mut_error), t)), + Ok(mut ref_mut) => { + std::mem::swap(&mut *ref_mut, &mut t); + Ok((component.clone(), t)) + } + } + } else { + Err(( + Error::NotRegistered(NotRegisteredError::of_type::>()), + t, + )) + } + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..150334d --- /dev/null +++ b/src/error.rs @@ -0,0 +1,64 @@ +use std::any::{type_name, TypeId}; +use std::cell::{BorrowError, BorrowMutError}; +use std::fmt::{Debug, Formatter, Result}; + +#[derive(Debug)] +pub enum Error { + AlreadyRegistered(AlreadyRegisteredError), + NotRegistered(NotRegisteredError), + BorrowedMutably(BorrowError), + Borrowed(BorrowMutError), + General(String), +} + +pub struct AlreadyRegisteredError(TypeId, &'static str); + +impl AlreadyRegisteredError { + pub fn of_type() -> Self { + AlreadyRegisteredError(TypeId::of::(), type_name::()) + } + + pub fn get_msg(&self) -> String { + format!("Element of type {} already registered", self.1) + } + + pub fn get_type_name(&self) -> &'static str { + self.1 + } + + pub fn get_type_id(&self) -> &TypeId { + &self.0 + } +} + +impl Debug for AlreadyRegisteredError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.write_str(&self.get_msg()) + } +} + +pub struct NotRegisteredError(TypeId, &'static str); + +impl NotRegisteredError { + pub fn of_type() -> Self { + NotRegisteredError(TypeId::of::(), type_name::()) + } + + pub fn get_msg(&self) -> String { + format!("Element of type {} not registered", self.1) + } + + pub fn get_type_name(&self) -> &'static str { + self.1 + } + + pub fn get_type_id(&self) -> &TypeId { + &self.0 + } +} + +impl Debug for NotRegisteredError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.write_str(&self.get_msg()) + } +} diff --git a/src/factory.rs b/src/factory.rs new file mode 100644 index 0000000..72f1012 --- /dev/null +++ b/src/factory.rs @@ -0,0 +1,163 @@ +use std::any::type_name; +use std::fmt::{Debug, Formatter, Result as FmtResult}; +use std::ops::Deref; +use std::rc::Rc; + +use super::component::{Component, ContextComponentExtension}; +use super::context::Context; +use super::error::{AlreadyRegisteredError, Error, NotRegisteredError}; + +pub trait FromContext { + fn from_context(&self, context: &mut Context) -> Result; +} + +impl Result> FromContext for FN { + fn from_context(&self, context: &mut Context) -> Result { + self(context) + } +} + +pub struct Factory(Rc>); + +impl Debug for Factory { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "Factory for component of type {}", type_name::()) + } +} + +impl Factory { + pub fn from + 'static>(from_context: FC) -> Self { + Factory(Rc::new(from_context)) + } +} + +impl Clone for Factory { + fn clone(&self) -> Self { + Factory(self.0.clone()) + } +} + +impl Deref for Factory { + type Target = dyn FromContext; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +pub trait ContextFactoryExtension { + fn get_factory(&self) -> Option>; + + fn register_factory( + &mut self, + factory: Factory, + ) -> Result<(), (AlreadyRegisteredError, Factory)>; + + fn replace_factory( + &mut self, + factory: Factory, + ) -> Result, (NotRegisteredError, Factory)>; + + fn build(&mut self) -> Result; + + fn build_and_register_component(&mut self) -> Result, Error>; + + fn get_or_build_component(&mut self) -> Result, Error>; +} + +impl ContextFactoryExtension for Context { + fn get_factory(&self) -> Option> { + self.get().cloned() + } + + fn register_factory( + &mut self, + factory: Factory, + ) -> Result<(), (AlreadyRegisteredError, Factory)> { + if self.get::>().is_none() { + self.set(factory); + Ok(()) + } else { + Err((AlreadyRegisteredError::of_type::>(), factory)) + } + } + + fn replace_factory( + &mut self, + mut factory: Factory, + ) -> Result, (NotRegisteredError, Factory)> { + match self.get_mut::>() { + Some(mut_ref) => { + std::mem::swap(mut_ref, &mut factory); + Ok(factory) + } + None => Err((NotRegisteredError::of_type::>(), factory)), + } + } + + fn build(&mut self) -> Result { + match self.get_factory::() { + Some(factory) => factory.from_context(self), + None => Err(Error::NotRegistered(NotRegisteredError::of_type::< + Factory, + >())), + } + } + + fn build_and_register_component(&mut self) -> Result, Error> { + if self.get_component::().is_some() { + return Err(Error::AlreadyRegistered(AlreadyRegisteredError::of_type::< + Component, + >())); + } + match self.build::() { + Ok(t) => self + .register_component(t) + .map_err(|(error, _)| Error::AlreadyRegistered(error)), + Err(error) => Err(error), + } + } + + fn get_or_build_component(&mut self) -> Result, Error> { + if let Some(component) = self.get_component::() { + Ok(component) + } else { + self.build_and_register_component() + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_factory() { + // Given structs + #[derive(Debug)] + struct Config { + initial_count: u32, + }; + #[derive(Debug)] + struct Counter(u32); + // and context + let mut context = Context::default(); + // and factories registered in context + let config_factory = Factory::from(|_: &mut Context| Ok(Config { initial_count: 5 })); + context.register_factory(config_factory).unwrap(); + let counter_factory = Factory::from(|context: &mut Context| { + let config_component = context.get_or_build_component::()?; + let initial_count = config_component.borrow().initial_count; + Ok(Counter(initial_count)) + }); + context.register_factory(counter_factory).unwrap(); + + // When + let counter_component = context.get_or_build_component::().unwrap(); + let config_component = context.get_component::().unwrap(); + + // Then + assert_eq!(counter_component.borrow().0, 5); + assert_eq!(config_component.borrow().initial_count, 5); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9efb2ab..c784ccc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,4 @@ +pub mod component; pub mod context; +pub mod error; +pub mod factory;