context changes

feature/battle
brnrs 5 years ago
parent 13015bbc04
commit 4dcd6cc826
  1. 67
      src/contexts/component.rs
  2. 115
      src/contexts/component_factory.rs
  3. 32
      src/contexts/component_map.rs
  4. 7
      src/contexts/context.rs
  5. 53
      src/contexts/factory.rs
  6. 17
      src/contexts/from_context.rs
  7. 8
      src/contexts/mod.rs
  8. 102
      src/contexts/model_i.rs
  9. 214
      src/contexts/simple.rs
  10. 5
      src/contexts/subcontext.rs
  11. 0
      src/contexts/type_map.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<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…
Cancel
Save