lxy 0.1.1

A convenient async http and RPC framework in Rust
Documentation
use std::sync::Arc;

use crate::container::Container;
use crate::error::Result;

/// Trait represents a factory that can create instances of type `T`
///
/// ```rust
/// use lxy::container::{Container, Factory};
/// struct MyService;
///
/// fn register<C: Container>(container: &mut C) {
///   let factory = || MyService;
///   container.add(factory);
/// }
/// ```
///
pub trait Factory<T, Deps = ()>: Clone + Send + Sync + Sized + 'static {
  fn create<C: Container>(self, registry: &C) -> Result<T>;
}

pub struct BoxedFactory<F, T, Deps> {
  pub factory: F,
  _marker: std::marker::PhantomData<fn(Deps) -> T>,
}

impl<F, T, Deps> BoxedFactory<F, T, Deps> {
  pub fn new(factory: F) -> Self {
    Self {
      factory,
      _marker: std::marker::PhantomData,
    }
  }
}

impl<F, T, Deps> Clone for BoxedFactory<F, T, Deps>
where
  F: Clone,
{
  fn clone(&self) -> Self {
    Self {
      factory: self.factory.clone(),
      _marker: std::marker::PhantomData,
    }
  }
}

impl<T, F> Factory<T, ()> for F
where
  F: FnOnce() -> T + Clone + Send + Sync + 'static,
  T: 'static,
{
  fn create<C: Container>(self, _: &C) -> Result<T> {
    Ok(self())
  }
}

mod private {
  pub enum FactoryMarker {}
  pub enum ValueFactoryMarker {}
  pub trait Mark<M = FactoryMarker> {}
  impl<T> Mark<FactoryMarker> for T where T: 'static {}
}

macro_rules! impl_resolvable {
    ([$($ty:ident),*], $last:ident) => {
        impl<T, F, M, $($ty,)* $last> Factory<T, (M, $($ty,)* $last)> for F
        where
            F: FnOnce($(Arc<$ty>,)* Arc<$last>) -> T + Clone + Send + Sync + 'static,
            $($ty: 'static,)*
            $last: 'static + private::Mark<M>,
        {
            #[allow(non_snake_case)]
            fn create<C: Container>(self, registry: &C) -> Result<T> {
                use crate::error::TypedError;

                $(
                    let $ty = registry.get::<$ty>()
                        .map_err(|source| {
                            super::error::DependencyResolutionFailed::error_with_source(
                                format!(
                                    "Failed to resolve dependency '{}' for type '{}'",
                                    std::any::type_name::<$ty>(),
                                    std::any::type_name::<T>()
                                ),
                                source
                            )
                        })?;
                )*

                let $last = registry.get::<$last>()
                    .map_err(|source| {
                        super::error::DependencyResolutionFailed::error_with_source(
                            format!(
                                "Failed to resolve dependency '{}' for type '{}'",
                                std::any::type_name::<$last>(),
                                std::any::type_name::<T>()
                            ),
                            source
                        )
                    })?;

                Ok(self($($ty,)* $last))
            }
        }
    };
}

all_the_tuples!(impl_resolvable);

/// Wrapper to allow registering a value directly as a factory.
/// Use `Value(x)` instead of `|| x` when you want to register a constant value.
///
/// # Note
///
/// The inner value `T` must implement `Clone` since the factory may be called multiple times.
///
/// # Example
///
/// ```rust
/// use lxy::container::{Value, RawContainer, Container};
///
/// #[derive(Clone)]
/// struct Config {}
///
/// let config = Config {};
///
/// let mut container = RawContainer::new();
/// container.add(Value(config));
///
/// let config_from_container = container.get::<Config>();
/// ```
#[derive(Clone)]
pub struct Value<T>(pub T);

pub trait ValueFactory<T> {
  fn into_value(self) -> T;
}

impl<T> ValueFactory<T> for Value<T>
where
  T: Clone,
{
  #[inline(always)]
  fn into_value(self) -> T {
    self.0
  }
}

impl<V, T> Factory<T, private::ValueFactoryMarker> for V
where
  V: ValueFactory<T> + Clone + Send + Sync + 'static,
{
  fn create<C: Container>(self, _: &C) -> Result<T> {
    Ok(self.into_value())
  }
}