feature/basic-functionality #1
Merged
brnrs
merged 2 commits from feature/basic-functionality
into develop
5 years ago
@ -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)] |
||||
mod tests { |
||||
#[test] |
||||
fn it_works() { |
||||
assert_eq!(2 + 2, 4); |
||||
} |
||||
} |
||||
pub mod component; |
||||
pub mod context; |
||||
pub mod error; |
||||
pub mod factory; |
||||
|
Loading…
Reference in new issue