Merge pull request 'feature/basic-functionality' (#1) from feature/basic-functionality into develop
commit
692f3859fb
@ -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<T: 'static>(Rc<RefCell<T>>); |
||||||
|
|
||||||
|
impl<T: 'static> Component<T> { |
||||||
|
pub fn from(t: T) -> Self { |
||||||
|
Component(Rc::new(RefCell::new(t))) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: 'static> Deref for Component<T> { |
||||||
|
type Target = RefCell<T>; |
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { |
||||||
|
&self.0 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: 'static> Clone for Component<T> { |
||||||
|
fn clone(&self) -> Self { |
||||||
|
Component(self.0.clone()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub trait ContextComponentExtension { |
||||||
|
fn get_component<T: 'static>(&self) -> Option<Component<T>>; |
||||||
|
|
||||||
|
fn register_component<T: 'static>( |
||||||
|
&mut self, |
||||||
|
t: T, |
||||||
|
) -> Result<Component<T>, (AlreadyRegisteredError, T)>; |
||||||
|
|
||||||
|
fn replace_component<T: 'static>(&self, t: T) -> Result<(Component<T>, T), (Error, T)>; |
||||||
|
} |
||||||
|
|
||||||
|
impl ContextComponentExtension for Context { |
||||||
|
fn get_component<T: 'static>(&self) -> Option<Component<T>> { |
||||||
|
self.get::<Component<T>>().cloned() |
||||||
|
} |
||||||
|
|
||||||
|
fn register_component<T: 'static>( |
||||||
|
&mut self, |
||||||
|
t: T, |
||||||
|
) -> Result<Component<T>, (AlreadyRegisteredError, T)> { |
||||||
|
if self.get::<T>().is_none() { |
||||||
|
let component = Component::from(t); |
||||||
|
self.set(component.clone()); |
||||||
|
Ok(component) |
||||||
|
} else { |
||||||
|
Err((AlreadyRegisteredError::of_type::<Component<T>>(), t)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn replace_component<T: 'static>(&self, mut t: T) -> Result<(Component<T>, T), (Error, T)> { |
||||||
|
if let Some(component) = self.get_component::<T>() { |
||||||
|
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::<Component<T>>()), |
||||||
|
t, |
||||||
|
)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
use std::any::{Any, TypeId}; |
||||||
|
use std::collections::HashMap; |
||||||
|
|
||||||
|
#[derive(Default)] |
||||||
|
pub struct Context { |
||||||
|
container: HashMap<TypeId, Box<dyn Any>>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Context { |
||||||
|
pub fn get<T: 'static>(&self) -> Option<&T> { |
||||||
|
self.container |
||||||
|
.get(&TypeId::of::<T>()) |
||||||
|
.map(|boxed| boxed.downcast_ref().unwrap()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> { |
||||||
|
self.container |
||||||
|
.get_mut(&TypeId::of::<T>()) |
||||||
|
.map(|boxed| boxed.downcast_mut().unwrap()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn set<T: 'static>(&mut self, t: T) { |
||||||
|
self.container.insert(TypeId::of::<T>(), Box::from(t)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use super::*; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_set_and_get() { |
||||||
|
// Given Context
|
||||||
|
let mut context = Context::default(); |
||||||
|
// And values
|
||||||
|
context.set(1_u32); |
||||||
|
context.set(String::from("text")); |
||||||
|
|
||||||
|
// When
|
||||||
|
let num: &u32 = context.get().unwrap(); |
||||||
|
let text: &String = context.get().unwrap(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(*num, 1); |
||||||
|
assert_eq!(text, "text"); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_set_and_get_mut() { |
||||||
|
// Given Context
|
||||||
|
let mut context = Context::default(); |
||||||
|
// And values
|
||||||
|
context.set(1_u32); |
||||||
|
|
||||||
|
// When
|
||||||
|
{ |
||||||
|
let num: &mut u32 = context.get_mut().unwrap(); |
||||||
|
*num = 2; |
||||||
|
} |
||||||
|
let num: &u32 = context.get().unwrap(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(*num, 2); |
||||||
|
} |
||||||
|
} |
@ -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<T: 'static>() -> Self { |
||||||
|
AlreadyRegisteredError(TypeId::of::<T>(), type_name::<T>()) |
||||||
|
} |
||||||
|
|
||||||
|
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<T: 'static>() -> Self { |
||||||
|
NotRegisteredError(TypeId::of::<T>(), type_name::<T>()) |
||||||
|
} |
||||||
|
|
||||||
|
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()) |
||||||
|
} |
||||||
|
} |
@ -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<T: 'static> { |
||||||
|
fn from_context(&self, context: &mut Context) -> Result<T, Error>; |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: 'static, FN: Fn(&mut Context) -> Result<T, Error>> FromContext<T> for FN { |
||||||
|
fn from_context(&self, context: &mut Context) -> Result<T, Error> { |
||||||
|
self(context) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct Factory<T: 'static>(Rc<dyn FromContext<T>>); |
||||||
|
|
||||||
|
impl<T: 'static> Debug for Factory<T> { |
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { |
||||||
|
write!(f, "Factory for component of type {}", type_name::<T>()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: 'static> Factory<T> { |
||||||
|
pub fn from<FC: FromContext<T> + 'static>(from_context: FC) -> Self { |
||||||
|
Factory(Rc::new(from_context)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: 'static> Clone for Factory<T> { |
||||||
|
fn clone(&self) -> Self { |
||||||
|
Factory(self.0.clone()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: 'static> Deref for Factory<T> { |
||||||
|
type Target = dyn FromContext<T>; |
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { |
||||||
|
self.0.deref() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub trait ContextFactoryExtension { |
||||||
|
fn get_factory<T: 'static>(&self) -> Option<Factory<T>>; |
||||||
|
|
||||||
|
fn register_factory<T: 'static>( |
||||||
|
&mut self, |
||||||
|
factory: Factory<T>, |
||||||
|
) -> Result<(), (AlreadyRegisteredError, Factory<T>)>; |
||||||
|
|
||||||
|
fn replace_factory<T: 'static>( |
||||||
|
&mut self, |
||||||
|
factory: Factory<T>, |
||||||
|
) -> Result<Factory<T>, (NotRegisteredError, Factory<T>)>; |
||||||
|
|
||||||
|
fn build<T: 'static>(&mut self) -> Result<T, Error>; |
||||||
|
|
||||||
|
fn build_and_register_component<T: 'static>(&mut self) -> Result<Component<T>, Error>; |
||||||
|
|
||||||
|
fn get_or_build_component<T: 'static>(&mut self) -> Result<Component<T>, Error>; |
||||||
|
} |
||||||
|
|
||||||
|
impl ContextFactoryExtension for Context { |
||||||
|
fn get_factory<T: 'static>(&self) -> Option<Factory<T>> { |
||||||
|
self.get().cloned() |
||||||
|
} |
||||||
|
|
||||||
|
fn register_factory<T: 'static>( |
||||||
|
&mut self, |
||||||
|
factory: Factory<T>, |
||||||
|
) -> Result<(), (AlreadyRegisteredError, Factory<T>)> { |
||||||
|
if self.get::<Factory<T>>().is_none() { |
||||||
|
self.set(factory); |
||||||
|
Ok(()) |
||||||
|
} else { |
||||||
|
Err((AlreadyRegisteredError::of_type::<Factory<T>>(), factory)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn replace_factory<T: 'static>( |
||||||
|
&mut self, |
||||||
|
mut factory: Factory<T>, |
||||||
|
) -> Result<Factory<T>, (NotRegisteredError, Factory<T>)> { |
||||||
|
match self.get_mut::<Factory<T>>() { |
||||||
|
Some(mut_ref) => { |
||||||
|
std::mem::swap(mut_ref, &mut factory); |
||||||
|
Ok(factory) |
||||||
|
} |
||||||
|
None => Err((NotRegisteredError::of_type::<Factory<T>>(), factory)), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn build<T: 'static>(&mut self) -> Result<T, Error> { |
||||||
|
match self.get_factory::<T>() { |
||||||
|
Some(factory) => factory.from_context(self), |
||||||
|
None => Err(Error::NotRegistered(NotRegisteredError::of_type::< |
||||||
|
Factory<T>, |
||||||
|
>())), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn build_and_register_component<T: 'static>(&mut self) -> Result<Component<T>, Error> { |
||||||
|
if self.get_component::<T>().is_some() { |
||||||
|
return Err(Error::AlreadyRegistered(AlreadyRegisteredError::of_type::< |
||||||
|
Component<T>, |
||||||
|
>())); |
||||||
|
} |
||||||
|
match self.build::<T>() { |
||||||
|
Ok(t) => self |
||||||
|
.register_component(t) |
||||||
|
.map_err(|(error, _)| Error::AlreadyRegistered(error)), |
||||||
|
Err(error) => Err(error), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn get_or_build_component<T: 'static>(&mut self) -> Result<Component<T>, Error> { |
||||||
|
if let Some(component) = self.get_component::<T>() { |
||||||
|
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::<Config>()?; |
||||||
|
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::<Counter>().unwrap(); |
||||||
|
let config_component = context.get_component::<Config>().unwrap(); |
||||||
|
|
||||||
|
// Then
|
||||||
|
assert_eq!(counter_component.borrow().0, 5); |
||||||
|
assert_eq!(config_component.borrow().initial_count, 5); |
||||||
|
} |
||||||
|
} |
@ -1,7 +1,4 @@ |
|||||||
#[cfg(test)] |
pub mod component; |
||||||
mod tests { |
pub mod context; |
||||||
#[test] |
pub mod error; |
||||||
fn it_works() { |
pub mod factory; |
||||||
assert_eq!(2 + 2, 4); |
|
||||||
} |
|
||||||
} |
|
||||||
|
Loading…
Reference in new issue