parent
13015bbc04
commit
4dcd6cc826
@ -1,38 +1,63 @@ |
||||
use std::any::{type_name, Any, TypeId}; |
||||
use super::context::Context; |
||||
use super::factory::Factory; |
||||
|
||||
use std::any::{Any, TypeId}; |
||||
use std::cell::RefCell; |
||||
use std::convert::TryFrom; |
||||
use std::ops::Deref; |
||||
use std::rc::Rc; |
||||
|
||||
#[derive(Clone)] |
||||
pub struct Component<T: Any + ?Sized + 'static>(Rc<RefCell<T>>); |
||||
pub struct Component<T: Any + Sized + 'static>(Rc<RefCell<T>>); |
||||
#[allow(dead_code)] |
||||
impl<T: Any + Sized + 'static> Component<T> { |
||||
pub fn from(context: &dyn Context) -> Option<Result<Self, String>> { |
||||
if let Some(any) = context.get(TypeId::of::<Self>()) { |
||||
Some(Ok(Self::downcast_clone(any))) |
||||
} else { |
||||
match Factory::<T>::from(context) { |
||||
Some(factory) => Some( |
||||
factory |
||||
.build(context) |
||||
.map(|value| Self::register(context, value)), |
||||
), |
||||
None => None, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl<T> Deref for Component<T> { |
||||
type Target = RefCell<T>; |
||||
pub fn register(context: &dyn Context, value: T) -> Component<T> { |
||||
let component = Component(Rc::new(RefCell::new(value))); |
||||
context.set(TypeId::of::<Self>(), Rc::new(component.clone())); |
||||
component |
||||
} |
||||
|
||||
fn deref(&self) -> &Self::Target { |
||||
self.0.deref() |
||||
fn downcast_clone(any: Rc<dyn Any + 'static>) -> Self { |
||||
any.downcast_ref::<Self>().unwrap().clone() |
||||
} |
||||
} |
||||
|
||||
#[allow(dead_code)] |
||||
impl<T: Any + Sized + 'static> Component<T> { |
||||
pub fn from(t: T) -> Component<T> { |
||||
Component(Rc::new(RefCell::new(t))) |
||||
impl<T: Any + Sized + 'static> Clone for Component<T> { |
||||
fn clone(&self) -> Self { |
||||
Component(self.0.clone()) |
||||
} |
||||
} |
||||
|
||||
pub fn type_id() -> TypeId { |
||||
TypeId::of::<T>() |
||||
impl<T: Any + Sized + 'static> Deref for Component<T> { |
||||
type Target = RefCell<T>; |
||||
fn deref(&self) -> &Self::Target { |
||||
self.0.deref() |
||||
} |
||||
} |
||||
|
||||
pub fn type_name() -> &'static str { |
||||
type_name::<T>() |
||||
} |
||||
pub trait ComponentOverlay { |
||||
fn get_component<T: Any + Sized + 'static>(&self) -> Option<Result<Component<T>, String>>; |
||||
fn register_component<T: Any + Sized + 'static>(&self, value: T) -> Component<T>; |
||||
} |
||||
|
||||
impl<T: Any + Sized + 'static> TryFrom<Rc<dyn Any + 'static>> for Component<T> { |
||||
type Error = Rc<dyn Any + 'static>; |
||||
fn try_from(rc: Rc<dyn Any + 'static>) -> Result<Component<T>, Self::Error> { |
||||
Ok(Component(rc.downcast::<RefCell<T>>()?)) |
||||
impl ComponentOverlay for dyn Context + '_ { |
||||
fn get_component<T: Any + Sized + 'static>(&self) -> Option<Result<Component<T>, String>> { |
||||
Component::from(self) |
||||
} |
||||
fn register_component<T: Any + Sized + 'static>(&self, value: T) -> Component<T> { |
||||
Component::register(self, value) |
||||
} |
||||
} |
||||
|
@ -1,115 +0,0 @@ |
||||
use super::component::Component; |
||||
use super::model_i::{Context, FromContext}; |
||||
|
||||
use std::any::{type_name, Any}; |
||||
|
||||
trait ComponentFactory { |
||||
type Input; |
||||
type Output; |
||||
|
||||
fn build(&self, input: Self::Input) -> Result<Self::Output, String>; |
||||
} |
||||
|
||||
trait ContextComponentFactory: ComponentFactory { |
||||
fn build_from_context(&self, context: &Context) -> Result<Self::Output, String>; |
||||
} |
||||
|
||||
impl<T: Any + Sized + 'static, O, CF: ComponentFactory<Input = Component<T>, Output = O>> |
||||
ContextComponentFactory for CF |
||||
{ |
||||
fn build_from_context(&self, context: &Context) -> Result<Self::Output, String> { |
||||
let input = FromContext::from(context).ok_or_else(|| { |
||||
format!( |
||||
"Could not find retrieve component of type {} from context", |
||||
type_name::<T>() |
||||
) |
||||
})?; |
||||
self.build(input) |
||||
} |
||||
} |
||||
|
||||
#[macro_export] |
||||
macro_rules! derive_ccf { |
||||
($CF:ty; $( $T:ty ),* ) => { |
||||
impl ContextComponentFactory for $CF { |
||||
fn build_from_context(&self, context: &Context) -> Result<Self::Output, String> { |
||||
let input = from_context!(context; $($T),*); |
||||
self.build(input) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[macro_export] |
||||
macro_rules! from_context { |
||||
($context:expr; $( $T:ty ),* ) => { |
||||
( |
||||
$( |
||||
FromContext::from($context).ok_or_else(|| { |
||||
format!( |
||||
"Could not find retrieve component of type {} from context", |
||||
type_name::<$T>() |
||||
) |
||||
})?, |
||||
)* |
||||
) |
||||
|
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use super::*; |
||||
|
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct A(&'static str); |
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct B(u32); |
||||
|
||||
#[test] |
||||
fn test_from_context_macro() { |
||||
// Given
|
||||
fn get_input(context: &Context) -> Result<(Component<A>, Component<B>), String> { |
||||
Ok(from_context!(context; A, B)) |
||||
} |
||||
|
||||
let context = Context::new(); |
||||
context.set(A("Test")); |
||||
context.set(B(67)); |
||||
|
||||
// When
|
||||
let (comp_a, comp_b) = get_input(&context).unwrap(); |
||||
|
||||
// Then
|
||||
assert_eq!(*comp_a.borrow(), A("Test")); |
||||
assert_eq!(*comp_b.borrow(), B(67)); |
||||
} |
||||
|
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct C(&'static str, u32); |
||||
|
||||
struct CFactory; |
||||
impl ComponentFactory for CFactory { |
||||
type Input = (Component<A>, Component<B>); |
||||
type Output = C; |
||||
|
||||
fn build(&self, (comp_a, comp_b): Self::Input) -> Result<Self::Output, String> { |
||||
Ok(C(comp_a.borrow().0, comp_b.borrow().0)) |
||||
} |
||||
} |
||||
|
||||
#[test] |
||||
fn test_derive_ccf_macro() { |
||||
// Given
|
||||
derive_ccf!(CFactory; A, B); |
||||
let c_factory = CFactory; |
||||
let context = Context::new(); |
||||
context.set(A("test")); |
||||
context.set(B(67)); |
||||
|
||||
// When
|
||||
let c = c_factory.build_from_context(&context).unwrap(); |
||||
|
||||
assert_eq!(c, C("test", 67)); |
||||
} |
||||
} |
@ -1,32 +0,0 @@ |
||||
use std::any::{Any, TypeId}; |
||||
use std::cell::RefCell; |
||||
use std::collections::HashMap; |
||||
use std::convert::TryInto; |
||||
use std::rc::Rc; |
||||
|
||||
use super::component::Component; |
||||
|
||||
pub struct ComponentMap(HashMap<TypeId, Rc<dyn Any + 'static>>); |
||||
|
||||
#[allow(dead_code)] |
||||
impl ComponentMap { |
||||
pub fn new() -> ComponentMap { |
||||
ComponentMap(HashMap::new()) |
||||
} |
||||
|
||||
pub fn contains<T: Any + Sized + 'static>(&self) -> bool { |
||||
self.0.contains_key(&TypeId::of::<T>()) |
||||
} |
||||
|
||||
pub fn get<T: Any + Sized + 'static>(&self) -> Option<Component<T>> { |
||||
self.0 |
||||
.get(&TypeId::of::<T>()) |
||||
.cloned() |
||||
.map(|c| c.try_into().unwrap()) |
||||
} |
||||
|
||||
pub fn set<T: Any + Sized + 'static>(&mut self, component: T) { |
||||
self.0 |
||||
.insert(TypeId::of::<T>(), Rc::new(RefCell::new(component))); |
||||
} |
||||
} |
@ -0,0 +1,7 @@ |
||||
use std::any::{Any, TypeId}; |
||||
use std::rc::Rc; |
||||
|
||||
pub trait Context { |
||||
fn get(&self, type_id: TypeId) -> Option<Rc<dyn Any + 'static>>; |
||||
fn set(&self, type_id: TypeId, component: Rc<dyn Any + 'static>); |
||||
} |
@ -0,0 +1,53 @@ |
||||
use super::context::Context; |
||||
use super::from_context::FromContext; |
||||
|
||||
use std::any::{Any, TypeId}; |
||||
use std::ops::Deref; |
||||
use std::rc::Rc; |
||||
pub struct Factory<T: Any + Sized + 'static>(Rc<dyn FromContext<Output = T>>); |
||||
#[allow(dead_code)] |
||||
impl<T: Any + Sized + 'static> Factory<T> { |
||||
pub fn from(context: &dyn Context) -> Option<Self> { |
||||
context |
||||
.get(TypeId::of::<Self>()) |
||||
.map(|boxed| boxed.downcast_ref::<Self>().unwrap().clone()) |
||||
} |
||||
|
||||
pub fn register(context: &dyn Context, value: Box<dyn FromContext<Output = T>>) -> Factory<T> { |
||||
let factory = Factory(Rc::from(value)); |
||||
context.set(TypeId::of::<Self>(), Rc::new(factory.clone())); |
||||
factory |
||||
} |
||||
} |
||||
|
||||
impl<T: Any + Sized + 'static> Clone for Factory<T> { |
||||
fn clone(&self) -> Self { |
||||
Factory(self.0.clone()) |
||||
} |
||||
} |
||||
|
||||
impl<T: Any + Sized + 'static> Deref for Factory<T> { |
||||
type Target = dyn FromContext<Output = T>; |
||||
fn deref(&self) -> &Self::Target { |
||||
self.0.deref() |
||||
} |
||||
} |
||||
|
||||
pub trait FactoryOverlay { |
||||
fn get_factory<T: Any + Sized + 'static>(&self) -> Option<Factory<T>>; |
||||
fn register_factory<T: Any + Sized + 'static>( |
||||
&self, |
||||
from_context: Box<dyn FromContext<Output = T>>, |
||||
) -> Factory<T>; |
||||
} |
||||
impl FactoryOverlay for dyn Context { |
||||
fn get_factory<T: Any + Sized + 'static>(&self) -> Option<Factory<T>> { |
||||
Factory::from(self) |
||||
} |
||||
fn register_factory<T: Any + Sized + 'static>( |
||||
&self, |
||||
from_context: Box<dyn FromContext<Output = T>>, |
||||
) -> Factory<T> { |
||||
Factory::register(self, from_context) |
||||
} |
||||
} |
@ -0,0 +1,17 @@ |
||||
use super::context::Context; |
||||
|
||||
use std::any::Any; |
||||
|
||||
pub trait FromContext { |
||||
type Output: Any + Sized + 'static; |
||||
|
||||
fn build(&self, context: &dyn Context) -> Result<Self::Output, String>; |
||||
} |
||||
|
||||
impl<T: Any + Sized + 'static, FN: Fn(&dyn Context) -> Result<T, String>> FromContext for FN { |
||||
type Output = T; |
||||
|
||||
fn build(&self, context: &dyn Context) -> Result<Self::Output, String> { |
||||
self(context) |
||||
} |
||||
} |
@ -1,4 +1,6 @@ |
||||
mod component; |
||||
mod component_factory; |
||||
mod component_map; |
||||
mod model_i; |
||||
mod context; |
||||
mod factory; |
||||
mod from_context; |
||||
mod simple; |
||||
mod subcontext; |
||||
|
@ -1,102 +0,0 @@ |
||||
use std::any::Any; |
||||
use std::cell::RefCell; |
||||
|
||||
use super::component::*; |
||||
use super::component_map::*; |
||||
#[allow(dead_code)] |
||||
pub struct Context<'t> { |
||||
map: RefCell<ComponentMap>, |
||||
base_context: Option<&'t Context<'t>>, |
||||
} |
||||
|
||||
#[allow(dead_code)] |
||||
impl<'t> Context<'t> { |
||||
pub fn new() -> Context<'t> { |
||||
Context { |
||||
map: RefCell::new(ComponentMap::new()), |
||||
base_context: None, |
||||
} |
||||
} |
||||
|
||||
pub fn get<T: Any + Sized + 'static>(&self) -> Option<Component<T>> { |
||||
match self.map.borrow().get() { |
||||
component @ Some(_) => component, |
||||
_ => match self.base_context { |
||||
Some(context) => context.get(), |
||||
None => None, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
pub fn set<T: Any + Sized + 'static>(&self, component: T) { |
||||
self.map.borrow_mut().set(component) |
||||
} |
||||
|
||||
pub fn subcontext(&self) -> Context { |
||||
let mut context = Context::new(); |
||||
context.base_context = Some(self); |
||||
context |
||||
} |
||||
} |
||||
|
||||
pub trait FromContext: Sized { |
||||
fn from(context: &Context) -> Option<Self>; |
||||
} |
||||
|
||||
impl<T: Any + Sized + 'static> FromContext for Component<T> { |
||||
fn from(context: &Context) -> Option<Self> { |
||||
context.get() |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use super::*; |
||||
|
||||
#[test] |
||||
fn get_from_context() { |
||||
// Given
|
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct TestStruct(&'static str); |
||||
let context = Context::new(); |
||||
context.set(TestStruct("hello")); |
||||
|
||||
// When
|
||||
let test_struct = context.get::<TestStruct>().unwrap(); |
||||
|
||||
// Then
|
||||
assert_eq!(*test_struct.borrow(), TestStruct("hello")); |
||||
} |
||||
|
||||
#[test] |
||||
fn get_from_base_context() { |
||||
// Given
|
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct TestStruct(&'static str); |
||||
let context = Context::new(); |
||||
context.set(TestStruct("hello")); |
||||
let subcontext = context.subcontext(); |
||||
|
||||
// When
|
||||
let test_struct = subcontext.get::<TestStruct>().unwrap(); |
||||
|
||||
// Then
|
||||
assert_eq!(*test_struct.borrow(), TestStruct("hello")); |
||||
} |
||||
|
||||
#[test] |
||||
fn get_from_subcontext() { |
||||
// Given
|
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct TestStruct(&'static str); |
||||
let context = Context::new(); |
||||
let subcontext = context.subcontext(); |
||||
subcontext.set(TestStruct("hello")); |
||||
|
||||
// When
|
||||
let test_struct = subcontext.get::<TestStruct>().unwrap(); |
||||
|
||||
// Then
|
||||
assert_eq!(*test_struct.borrow(), TestStruct("hello")); |
||||
} |
||||
} |
@ -0,0 +1,214 @@ |
||||
use super::context::Context; |
||||
use super::subcontext::SubContext; |
||||
|
||||
use std::any::{Any, TypeId}; |
||||
use std::cell::RefCell; |
||||
use std::collections::HashMap; |
||||
use std::ops::Deref; |
||||
use std::rc::Rc; |
||||
|
||||
pub struct SimpleContext(RefCell<HashMap<TypeId, Rc<dyn Any + 'static>>>); |
||||
|
||||
#[allow(dead_code)] |
||||
impl SimpleContext { |
||||
pub fn new() -> SimpleContext { |
||||
SimpleContext(RefCell::new(HashMap::new())) |
||||
} |
||||
} |
||||
|
||||
impl Context for SimpleContext { |
||||
fn get(&self, type_id: TypeId) -> Option<Rc<dyn Any + 'static>> { |
||||
self.0.borrow().get(&type_id).cloned() |
||||
} |
||||
|
||||
fn set(&self, type_id: TypeId, component: Rc<dyn Any + 'static>) { |
||||
self.0.borrow_mut().insert(type_id, component); |
||||
} |
||||
} |
||||
|
||||
impl Deref for SimpleContext { |
||||
type Target = dyn Context; |
||||
|
||||
fn deref(&self) -> &Self::Target { |
||||
self |
||||
} |
||||
} |
||||
|
||||
pub struct SimpleSubContext<'t>(SimpleContext, &'t dyn Context); |
||||
|
||||
impl Context for SimpleSubContext<'_> { |
||||
fn get(&self, type_id: TypeId) -> Option<Rc<dyn Any + 'static>> { |
||||
match self.0.get(type_id) { |
||||
any @ Some(_) => any, |
||||
None => self.1.get(type_id), |
||||
} |
||||
} |
||||
|
||||
fn set(&self, type_id: TypeId, component: Rc<dyn Any + 'static>) { |
||||
self.0.set(type_id, component) |
||||
} |
||||
} |
||||
|
||||
impl<'t> SubContext<'t> for SimpleSubContext<'t> { |
||||
fn subcontext(base_context: &'t dyn Context) -> Self { |
||||
SimpleSubContext(SimpleContext::new(), base_context) |
||||
} |
||||
} |
||||
|
||||
impl<'t> Deref for SimpleSubContext<'t> { |
||||
type Target = dyn Context + 't; |
||||
|
||||
fn deref(&self) -> &Self::Target { |
||||
self |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use super::*; |
||||
use crate::contexts::component::*; |
||||
use crate::contexts::factory::*; |
||||
|
||||
#[test] |
||||
fn should_retrieve_components_from_context() { |
||||
// Given
|
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct A(&'static str); |
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct B(i32, i32); |
||||
|
||||
let context = SimpleContext::new(); |
||||
|
||||
context.register_component(5 as u32); |
||||
context.register_component("test123"); |
||||
context.register_component(A("some_value")); |
||||
context.register_component(B(8, 0)); |
||||
|
||||
// When
|
||||
let comp_u32 = context.get_component::<u32>().unwrap().unwrap(); |
||||
let comp_str = context.get_component::<&'static str>().unwrap().unwrap(); |
||||
let comp_a = context.get_component::<A>().unwrap().unwrap(); |
||||
let comp_b = context.get_component::<B>().unwrap().unwrap(); |
||||
|
||||
// Then
|
||||
assert_eq!(*comp_u32.borrow(), 5); |
||||
assert_eq!(*comp_str.borrow(), "test123"); |
||||
assert_eq!(*comp_a.borrow(), A("some_value")); |
||||
assert_eq!(*comp_b.borrow(), B(8, 0)); |
||||
} |
||||
|
||||
#[test] |
||||
fn should_utilize_factory_for_building_component() { |
||||
// Given
|
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct A(&'static str); |
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct B(i32, i32); |
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct C(&'static str, i32); |
||||
|
||||
let context = SimpleContext::new(); |
||||
|
||||
let c_from_context = |context: &dyn Context| { |
||||
let comp_a = context |
||||
.get_component::<A>() |
||||
.unwrap_or_else(|| Err(String::from("Component A not found")))?; |
||||
let comp_b = context |
||||
.get_component::<B>() |
||||
.unwrap_or_else(|| Err(String::from("Component B not found")))?; |
||||
|
||||
let &A(txt) = &*comp_a.borrow(); |
||||
let &B(n1, n2) = &*comp_b.borrow(); |
||||
|
||||
Ok(C(txt, n1 + n2)) |
||||
}; |
||||
|
||||
context.register_factory(Box::new(c_from_context)); |
||||
context.register_component(A("test")); |
||||
context.register_component(B(4, 8)); |
||||
|
||||
// When
|
||||
let comp_c = context |
||||
.get_component::<C>() |
||||
.unwrap_or_else(|| Err(String::from("Component C not found"))) |
||||
.unwrap(); |
||||
|
||||
// Then
|
||||
assert_eq!(*comp_c.borrow(), C("test", 12)); |
||||
} |
||||
|
||||
#[test] |
||||
fn should_utilize_factory_recursively() { |
||||
// Given
|
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct A(&'static str); |
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct B(i32, i32); |
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct C(&'static str, i32); |
||||
|
||||
let context = SimpleContext::new(); |
||||
|
||||
let b_from_context = |context: &dyn Context| { |
||||
let comp_a = context |
||||
.get_component::<A>() |
||||
.unwrap_or_else(|| Err(String::from("Component A not found")))?; |
||||
let &A(txt) = &*comp_a.borrow(); |
||||
|
||||
Ok(B( |
||||
txt.len() as i32, |
||||
txt.split_ascii_whitespace().count() as i32, |
||||
)) |
||||
}; |
||||
let c_from_context = |context: &dyn Context| { |
||||
let comp_a = context |
||||
.get_component::<A>() |
||||
.unwrap_or_else(|| Err(String::from("Component A not found")))?; |
||||
let comp_b = context |
||||
.get_component::<B>() |
||||
.unwrap_or_else(|| Err(String::from("Component B not found")))?; |
||||
let &A(txt) = &*comp_a.borrow(); |
||||
let &B(n1, n2) = &*comp_b.borrow(); |
||||
|
||||
Ok(C(txt, n1 + n2)) |
||||
}; |
||||
|
||||
context.register_factory(Box::new(b_from_context)); |
||||
context.register_factory(Box::new(c_from_context)); |
||||
context.register_component(A("test abc")); |
||||
|
||||
// When
|
||||
let comp_c = context |
||||
.get_component::<C>() |
||||
.unwrap_or_else(|| Err(String::from("Component C not found"))) |
||||
.unwrap(); |
||||
let comp_b = context |
||||
.get_component::<B>() |
||||
.unwrap_or_else(|| Err(String::from("Component B not found"))) |
||||
.unwrap(); |
||||
|
||||
// Then
|
||||
assert_eq!(*comp_b.borrow(), B(8, 2)); |
||||
assert_eq!(*comp_c.borrow(), C("test abc", 10)); |
||||
} |
||||
|
||||
#[test] |
||||
fn should_retrieve_component_from_base_context() { |
||||
// Given
|
||||
#[derive(Debug, Eq, PartialEq)] |
||||
struct A(&'static str); |
||||
let base_context = SimpleContext::new(); |
||||
let subcontext = SimpleSubContext::subcontext(&base_context); |
||||
|
||||
base_context.register_component(A("Bob's your uncle")); |
||||
|
||||
// When
|
||||
let comp_a = subcontext |
||||
.get_component::<A>() |
||||
.unwrap_or_else(|| Err(String::from("Component A not found"))) |
||||
.unwrap(); |
||||
|
||||
// Then
|
||||
assert_eq!(*comp_a.borrow(), A("Bob's your uncle")); |
||||
} |
||||
} |
@ -0,0 +1,5 @@ |
||||
use super::context::Context; |
||||
|
||||
pub trait SubContext<'t>: Context { |
||||
fn subcontext(base_context: &'t dyn Context) -> Self; |
||||
} |
Loading…
Reference in new issue