Merge pull request 'v0.1.0' (#2) from develop into master

master v0.1.0
brnrs 5 years ago
commit 10ee68bfda
  1. 2
      .gitignore
  2. 2
      Cargo.toml
  3. 75
      src/component.rs
  4. 65
      src/context.rs
  5. 64
      src/error.rs
  6. 163
      src/factory.rs
  7. 11
      src/lib.rs

2
.gitignore vendored

@ -18,3 +18,5 @@ Cargo.lock
/target /target
#Cargo.lock #Cargo.lock
/.vscode

@ -1,6 +1,6 @@
[package] [package]
name = "badcontext" name = "badcontext"
version = "0.1.0-SNAPSHOT" version = "0.1.0"
authors = ["brnrs <brnrs@brnrs.pl>"] authors = ["brnrs <brnrs@brnrs.pl>"]
edition = "2018" edition = "2018"
description = "A small crate for creating Context structs that contain application components" description = "A small crate for creating Context structs that contain application components"

@ -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)] pub mod component;
mod tests { pub mod context;
#[test] pub mod error;
fn it_works() { pub mod factory;
assert_eq!(2 + 2, 4);
}
}

Loading…
Cancel
Save