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::cell::RefCell; |
||||||
use std::convert::TryFrom; |
|
||||||
use std::ops::Deref; |
use std::ops::Deref; |
||||||
use std::rc::Rc; |
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> { |
pub fn register(context: &dyn Context, value: T) -> Component<T> { |
||||||
type Target = RefCell<T>; |
let component = Component(Rc::new(RefCell::new(value))); |
||||||
|
context.set(TypeId::of::<Self>(), Rc::new(component.clone())); |
||||||
|
component |
||||||
|
} |
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { |
fn downcast_clone(any: Rc<dyn Any + 'static>) -> Self { |
||||||
self.0.deref() |
any.downcast_ref::<Self>().unwrap().clone() |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
#[allow(dead_code)] |
impl<T: Any + Sized + 'static> Clone for Component<T> { |
||||||
impl<T: Any + Sized + 'static> Component<T> { |
fn clone(&self) -> Self { |
||||||
pub fn from(t: T) -> Component<T> { |
Component(self.0.clone()) |
||||||
Component(Rc::new(RefCell::new(t))) |
|
||||||
} |
} |
||||||
|
|
||||||
pub fn type_id() -> TypeId { |
|
||||||
TypeId::of::<T>() |
|
||||||
} |
} |
||||||
|
|
||||||
pub fn type_name() -> &'static str { |
impl<T: Any + Sized + 'static> Deref for Component<T> { |
||||||
type_name::<T>() |
type Target = RefCell<T>; |
||||||
|
fn deref(&self) -> &Self::Target { |
||||||
|
self.0.deref() |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
|
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> { |
impl ComponentOverlay for dyn Context + '_ { |
||||||
type Error = Rc<dyn Any + 'static>; |
fn get_component<T: Any + Sized + 'static>(&self) -> Option<Result<Component<T>, String>> { |
||||||
fn try_from(rc: Rc<dyn Any + 'static>) -> Result<Component<T>, Self::Error> { |
Component::from(self) |
||||||
Ok(Component(rc.downcast::<RefCell<T>>()?)) |
} |
||||||
|
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; |
||||||
mod component_factory; |
mod context; |
||||||
mod component_map; |
mod factory; |
||||||
mod model_i; |
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