badcontext 0.1.0

A small crate for creating Context structs that contain application components
Documentation
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);
    }
}