From 4dcd6cc826fef3e5d32fc6a8d51299822f62bb2b Mon Sep 17 00:00:00 2001 From: brnrs Date: Mon, 20 Apr 2020 20:26:06 +0200 Subject: [PATCH] context changes --- src/contexts/component.rs | 67 +++++++--- src/contexts/component_factory.rs | 115 ---------------- src/contexts/component_map.rs | 32 ----- src/contexts/context.rs | 7 + src/contexts/factory.rs | 53 ++++++++ src/contexts/from_context.rs | 17 +++ src/contexts/mod.rs | 8 +- src/contexts/model_i.rs | 102 -------------- src/contexts/simple.rs | 214 ++++++++++++++++++++++++++++++ src/contexts/subcontext.rs | 5 + src/contexts/type_map.rs | 0 11 files changed, 347 insertions(+), 273 deletions(-) delete mode 100644 src/contexts/component_factory.rs delete mode 100644 src/contexts/component_map.rs create mode 100644 src/contexts/context.rs create mode 100644 src/contexts/factory.rs create mode 100644 src/contexts/from_context.rs delete mode 100644 src/contexts/model_i.rs create mode 100644 src/contexts/simple.rs create mode 100644 src/contexts/subcontext.rs create mode 100644 src/contexts/type_map.rs diff --git a/src/contexts/component.rs b/src/contexts/component.rs index a481f2e..aaf37e8 100644 --- a/src/contexts/component.rs +++ b/src/contexts/component.rs @@ -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(Rc>); +pub struct Component(Rc>); +#[allow(dead_code)] +impl Component { + pub fn from(context: &dyn Context) -> Option> { + if let Some(any) = context.get(TypeId::of::()) { + Some(Ok(Self::downcast_clone(any))) + } else { + match Factory::::from(context) { + Some(factory) => Some( + factory + .build(context) + .map(|value| Self::register(context, value)), + ), + None => None, + } + } + } -impl Deref for Component { - type Target = RefCell; + pub fn register(context: &dyn Context, value: T) -> Component { + let component = Component(Rc::new(RefCell::new(value))); + context.set(TypeId::of::(), Rc::new(component.clone())); + component + } - fn deref(&self) -> &Self::Target { - self.0.deref() + fn downcast_clone(any: Rc) -> Self { + any.downcast_ref::().unwrap().clone() } } -#[allow(dead_code)] -impl Component { - pub fn from(t: T) -> Component { - Component(Rc::new(RefCell::new(t))) +impl Clone for Component { + fn clone(&self) -> Self { + Component(self.0.clone()) } +} - pub fn type_id() -> TypeId { - TypeId::of::() +impl Deref for Component { + type Target = RefCell; + fn deref(&self) -> &Self::Target { + self.0.deref() } +} - pub fn type_name() -> &'static str { - type_name::() - } +pub trait ComponentOverlay { + fn get_component(&self) -> Option, String>>; + fn register_component(&self, value: T) -> Component; } -impl TryFrom> for Component { - type Error = Rc; - fn try_from(rc: Rc) -> Result, Self::Error> { - Ok(Component(rc.downcast::>()?)) +impl ComponentOverlay for dyn Context + '_ { + fn get_component(&self) -> Option, String>> { + Component::from(self) + } + fn register_component(&self, value: T) -> Component { + Component::register(self, value) } } diff --git a/src/contexts/component_factory.rs b/src/contexts/component_factory.rs deleted file mode 100644 index cb3d74a..0000000 --- a/src/contexts/component_factory.rs +++ /dev/null @@ -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; -} - -trait ContextComponentFactory: ComponentFactory { - fn build_from_context(&self, context: &Context) -> Result; -} - -impl, Output = O>> - ContextComponentFactory for CF -{ - fn build_from_context(&self, context: &Context) -> Result { - let input = FromContext::from(context).ok_or_else(|| { - format!( - "Could not find retrieve component of type {} from context", - type_name::() - ) - })?; - 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 { - 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, Component), 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, Component); - type Output = C; - - fn build(&self, (comp_a, comp_b): Self::Input) -> Result { - 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)); - } -} diff --git a/src/contexts/component_map.rs b/src/contexts/component_map.rs deleted file mode 100644 index 08b9335..0000000 --- a/src/contexts/component_map.rs +++ /dev/null @@ -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>); - -#[allow(dead_code)] -impl ComponentMap { - pub fn new() -> ComponentMap { - ComponentMap(HashMap::new()) - } - - pub fn contains(&self) -> bool { - self.0.contains_key(&TypeId::of::()) - } - - pub fn get(&self) -> Option> { - self.0 - .get(&TypeId::of::()) - .cloned() - .map(|c| c.try_into().unwrap()) - } - - pub fn set(&mut self, component: T) { - self.0 - .insert(TypeId::of::(), Rc::new(RefCell::new(component))); - } -} diff --git a/src/contexts/context.rs b/src/contexts/context.rs new file mode 100644 index 0000000..bcf9151 --- /dev/null +++ b/src/contexts/context.rs @@ -0,0 +1,7 @@ +use std::any::{Any, TypeId}; +use std::rc::Rc; + +pub trait Context { + fn get(&self, type_id: TypeId) -> Option>; + fn set(&self, type_id: TypeId, component: Rc); +} diff --git a/src/contexts/factory.rs b/src/contexts/factory.rs new file mode 100644 index 0000000..1adbbb1 --- /dev/null +++ b/src/contexts/factory.rs @@ -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(Rc>); +#[allow(dead_code)] +impl Factory { + pub fn from(context: &dyn Context) -> Option { + context + .get(TypeId::of::()) + .map(|boxed| boxed.downcast_ref::().unwrap().clone()) + } + + pub fn register(context: &dyn Context, value: Box>) -> Factory { + let factory = Factory(Rc::from(value)); + context.set(TypeId::of::(), Rc::new(factory.clone())); + factory + } +} + +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 FactoryOverlay { + fn get_factory(&self) -> Option>; + fn register_factory( + &self, + from_context: Box>, + ) -> Factory; +} +impl FactoryOverlay for dyn Context { + fn get_factory(&self) -> Option> { + Factory::from(self) + } + fn register_factory( + &self, + from_context: Box>, + ) -> Factory { + Factory::register(self, from_context) + } +} diff --git a/src/contexts/from_context.rs b/src/contexts/from_context.rs new file mode 100644 index 0000000..39823a1 --- /dev/null +++ b/src/contexts/from_context.rs @@ -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; +} + +impl Result> FromContext for FN { + type Output = T; + + fn build(&self, context: &dyn Context) -> Result { + self(context) + } +} diff --git a/src/contexts/mod.rs b/src/contexts/mod.rs index 4ef008c..c5e6278 100644 --- a/src/contexts/mod.rs +++ b/src/contexts/mod.rs @@ -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; diff --git a/src/contexts/model_i.rs b/src/contexts/model_i.rs deleted file mode 100644 index d1c9144..0000000 --- a/src/contexts/model_i.rs +++ /dev/null @@ -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, - 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(&self) -> Option> { - match self.map.borrow().get() { - component @ Some(_) => component, - _ => match self.base_context { - Some(context) => context.get(), - None => None, - }, - } - } - - pub fn set(&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; -} - -impl FromContext for Component { - fn from(context: &Context) -> Option { - 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::().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::().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::().unwrap(); - - // Then - assert_eq!(*test_struct.borrow(), TestStruct("hello")); - } -} diff --git a/src/contexts/simple.rs b/src/contexts/simple.rs new file mode 100644 index 0000000..e6d3e61 --- /dev/null +++ b/src/contexts/simple.rs @@ -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>>); + +#[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> { + self.0.borrow().get(&type_id).cloned() + } + + fn set(&self, type_id: TypeId, component: Rc) { + 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> { + match self.0.get(type_id) { + any @ Some(_) => any, + None => self.1.get(type_id), + } + } + + fn set(&self, type_id: TypeId, component: Rc) { + 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::().unwrap().unwrap(); + let comp_str = context.get_component::<&'static str>().unwrap().unwrap(); + let comp_a = context.get_component::().unwrap().unwrap(); + let comp_b = context.get_component::().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::() + .unwrap_or_else(|| Err(String::from("Component A not found")))?; + let comp_b = context + .get_component::() + .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::() + .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::() + .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::() + .unwrap_or_else(|| Err(String::from("Component A not found")))?; + let comp_b = context + .get_component::() + .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::() + .unwrap_or_else(|| Err(String::from("Component C not found"))) + .unwrap(); + let comp_b = context + .get_component::() + .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::() + .unwrap_or_else(|| Err(String::from("Component A not found"))) + .unwrap(); + + // Then + assert_eq!(*comp_a.borrow(), A("Bob's your uncle")); + } +} diff --git a/src/contexts/subcontext.rs b/src/contexts/subcontext.rs new file mode 100644 index 0000000..ac1afb1 --- /dev/null +++ b/src/contexts/subcontext.rs @@ -0,0 +1,5 @@ +use super::context::Context; + +pub trait SubContext<'t>: Context { + fn subcontext(base_context: &'t dyn Context) -> Self; +} diff --git a/src/contexts/type_map.rs b/src/contexts/type_map.rs new file mode 100644 index 0000000..e69de29