use std::any::{TypeId, type_name};
use std::future::Future;
use std::marker::PhantomData;
use std::sync::Arc;
use crate::error::Result;
use crate::app::BuildState;
use crate::container::{ConstructOut, Container, pack};
use crate::deps::DepList;
use crate::error::BoxError;
use crate::error::Error;
use crate::error::user_ctor_err;
use crate::option::ModOption;
use crate::trace::provided;
use std::fmt;
pub(crate) fn provide<M, F>(ctor: F) -> Box<dyn ModOption>
where
F: ProviderFn<M> + 'static,
{
provide_dyn_vis(ctor.into_provider(), false)
}
pub(crate) fn provide_priv<M, F>(ctor: F) -> Box<dyn ModOption>
where
F: ProviderFn<M> + 'static,
{
provide_dyn_vis(ctor.into_provider(), true)
}
pub(crate) fn provide_result<M, F>(ctor: F) -> Box<dyn ModOption>
where
F: FallibleProviderFn<M> + 'static,
{
provide_dyn_vis(ctor.into_provider(), false)
}
pub(crate) fn provide_result_priv<M, F>(ctor: F) -> Box<dyn ModOption>
where
F: FallibleProviderFn<M> + 'static,
{
provide_dyn_vis(ctor.into_provider(), true)
}
pub(crate) fn provide_async<M, F>(ctor: F) -> Box<dyn ModOption>
where
F: AsyncProviderFn<M> + 'static,
{
provide_dyn_vis(ctor.into_provider(), false)
}
pub(crate) fn provide_async_priv<M, F>(ctor: F) -> Box<dyn ModOption>
where
F: AsyncProviderFn<M> + 'static,
{
provide_dyn_vis(ctor.into_provider(), true)
}
pub(crate) fn provide_result_async<M, F>(ctor: F) -> Box<dyn ModOption>
where
F: FallibleAsyncProviderFn<M> + 'static,
{
provide_dyn_vis(ctor.into_provider(), false)
}
pub(crate) fn provide_result_async_priv<M, F>(ctor: F) -> Box<dyn ModOption>
where
F: FallibleAsyncProviderFn<M> + 'static,
{
provide_dyn_vis(ctor.into_provider(), true)
}
pub(crate) fn provide_dyn(provider: DynProvider) -> Box<dyn ModOption> {
provide_dyn_vis(provider, false)
}
pub(crate) fn provide_dyn_priv(provider: DynProvider) -> Box<dyn ModOption> {
provide_dyn_vis(provider, true)
}
fn provide_dyn_vis(provider: DynProvider, private: bool) -> Box<dyn ModOption> {
Box::new(ProvideOption { provider, private })
}
struct ProvideOption {
provider: DynProvider,
private: bool,
}
impl ModOption for ProvideOption {
fn apply(self: Box<Self>, app: &mut BuildState) -> Result<()> {
let type_name = self.provider.result_name();
let constructor = self.provider.constructor_name();
let private = self.private;
let scope = app.current_scope;
app.container
.insert_provider(self.provider, scope, private)?;
provided(
type_name,
constructor,
app.container.scopes().name(scope),
private,
);
Ok(())
}
}
#[doc(hidden)]
pub trait ProviderFn<Marker>: Sized {
fn into_provider(self) -> DynProvider;
}
#[doc(hidden)]
pub trait FallibleProviderFn<Marker>: Sized {
fn into_provider(self) -> DynProvider;
}
#[doc(hidden)]
pub trait AsyncProviderFn<Marker>: Sized {
fn into_provider(self) -> DynProvider;
}
#[doc(hidden)]
pub trait FallibleAsyncProviderFn<Marker>: Sized {
fn into_provider(self) -> DynProvider;
}
#[doc(hidden)]
pub trait ProviderMarker {
type Output: Clone + Send + Sync + 'static;
}
macro_rules! impl_provider_marker_zero {
($marker:ident) => {
impl<Out> ProviderMarker for $marker<Out>
where
Out: Clone + Send + Sync + 'static,
{
type Output = Out;
}
};
}
macro_rules! impl_provider_marker_fallible_zero {
($marker:ident) => {
impl<T, ErrTy> ProviderMarker for $marker<T, ErrTy>
where
T: Clone + Send + Sync + 'static,
{
type Output = T;
}
};
}
macro_rules! impl_provider_marker_arity {
($marker:ident, $($A:ident),+) => {
impl<Out, $($A,)+> ProviderMarker for $marker<Out, $($A,)+>
where
Out: Clone + Send + Sync + 'static,
{
type Output = Out;
}
};
}
macro_rules! impl_provider_marker_fallible_arity {
($marker:ident, $($A:ident),+) => {
impl<T, ErrTy, $($A,)+> ProviderMarker for $marker<T, ErrTy, $($A,)+>
where
T: Clone + Send + Sync + 'static,
{
type Output = T;
}
};
}
type ConstructFn = Box<dyn Fn(&mut Container) -> Result<ConstructOut> + Send + Sync>;
#[doc(hidden)]
pub struct DynProvider {
result_type: TypeId,
result_name: &'static str,
constructor_name: &'static str,
alias_types: [TypeId; 1],
deps: DepList,
construct_fn: ConstructFn,
}
impl fmt::Debug for DynProvider {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DynProvider")
.field("result", &self.result_name)
.field(
"deps",
&self
.deps
.as_slice()
.iter()
.map(|(_, n)| n)
.collect::<Vec<_>>(),
)
.finish_non_exhaustive()
}
}
impl DynProvider {
#[must_use]
pub fn result_type(&self) -> TypeId {
self.result_type
}
#[must_use]
pub fn result_name(&self) -> &'static str {
self.result_name
}
#[must_use]
pub fn constructor_name(&self) -> &'static str {
self.constructor_name
}
#[must_use]
pub fn dep_types(&self) -> &[(TypeId, &'static str)] {
self.deps.as_slice()
}
pub(crate) fn alias_types(&self) -> &[TypeId] {
&self.alias_types
}
pub(crate) fn construct(&self, container: &mut Container) -> Result<ConstructOut> {
(self.construct_fn)(container)
}
pub(crate) fn new_group_virtual<T: Send + Sync + 'static>(construct_fn: ConstructFn) -> Self {
Self {
result_type: TypeId::of::<crate::Group<T>>(),
result_name: type_name::<crate::Group<T>>(),
constructor_name: "<group aggregate>",
alias_types: [TypeId::of::<Arc<crate::Group<T>>>()],
deps: DepList::empty(),
construct_fn,
}
}
}
pub(crate) fn take_once_value_provider<T: Send + Sync + 'static>(
value: T,
constructor_name: &'static str,
) -> DynProvider {
use std::sync::Mutex;
let slot = Mutex::new(Some(value));
dyn_for::<T>(
constructor_name,
DepList::empty(),
Box::new(move |_container: &mut Container| {
let value = slot
.lock()
.expect("take_once_value_provider mutex poisoned")
.take()
.expect("take_once_value_provider constructed more than once");
Ok(ready_packed(value))
}),
)
}
fn dyn_for<T: Send + Sync + 'static>(
constructor_name: &'static str,
deps: DepList,
construct: ConstructFn,
) -> DynProvider {
DynProvider {
result_type: TypeId::of::<T>(),
result_name: type_name::<T>(),
constructor_name,
alias_types: [TypeId::of::<Arc<T>>()],
deps,
construct_fn: construct,
}
}
fn ready_packed<T: Send + Sync + 'static>(value: T) -> ConstructOut {
ConstructOut::Ready(pack(value))
}
fn ctor_failed<T: ?Sized>(err: impl Into<BoxError>) -> Error {
user_ctor_err::<T>(err)
}
macro_rules! impl_use_provide_result_instead {
($guard:ident, $($A:ident),*) => {
impl<Func, T, ErrTy, $($A),*> ProviderFn<$guard<T, ErrTy, $($A),*>> for Func
where
Func: Fn($($A),*) -> std::result::Result<T, ErrTy> + Send + Sync + 'static,
T: Send + Sync + 'static,
ErrTy: Send + Sync + 'static,
$($A: Clone + Send + Sync + 'static,)*
{
fn into_provider(self) -> DynProvider {
dyn_for::<std::result::Result<T, ErrTy>>(
type_name::<Func>(),
DepList::from_array([$(crate::deps::dep::<$A>(),)*]),
Box::new(move |_container: &mut Container| {
let value = (self)($(_container.get::<$A>()?,)*);
Ok(ready_packed(value))
}),
)
}
}
};
}
macro_rules! impl_use_provide_result_async_instead {
($guard:ident, $($A:ident),*) => {
impl<Func, Fut, T, ErrTy, $($A),*> AsyncProviderFn<$guard<T, ErrTy, $($A),*>> for Func
where
Func: Fn($($A),*) -> Fut + Send + Sync + 'static,
Fut: Future<Output = std::result::Result<T, ErrTy>> + Send + 'static,
T: Send + Sync + 'static,
ErrTy: Send + Sync + 'static,
$($A: Clone + Send + Sync + 'static,)*
{
fn into_provider(self) -> DynProvider {
dyn_for::<std::result::Result<T, ErrTy>>(
type_name::<Func>(),
DepList::from_array([$(crate::deps::dep::<$A>(),)*]),
Box::new(move |_container: &mut Container| {
let future = (self)($(_container.get::<$A>()?,)*);
Ok(ConstructOut::Fut(Box::pin(async move {
Ok(pack(future.await))
})))
}),
)
}
}
};
}
macro_rules! impl_provider_fn_zero {
(
$marker:ident,
$fallible:ident,
$async_marker:ident,
$async_fallible:ident,
$use_provide_result:ident,
$use_provide_result_async:ident
) => {
#[doc(hidden)]
pub struct $marker<Out>(PhantomData<fn() -> Out>);
#[doc(hidden)]
pub struct $fallible<T, ErrTy>(PhantomData<fn() -> (T, ErrTy)>);
#[doc(hidden)]
pub struct $async_marker<Out>(PhantomData<fn() -> Out>);
#[doc(hidden)]
pub struct $async_fallible<T, ErrTy>(PhantomData<fn() -> (T, ErrTy)>);
#[doc(hidden)]
pub struct $use_provide_result<T, ErrTy>(PhantomData<fn() -> (T, ErrTy)>);
#[doc(hidden)]
pub struct $use_provide_result_async<T, ErrTy>(PhantomData<fn() -> (T, ErrTy)>);
impl_use_provide_result_instead!($use_provide_result,);
impl_use_provide_result_async_instead!($use_provide_result_async,);
impl<Func, Out> ProviderFn<$marker<Out>> for Func
where
Func: Fn() -> Out + Send + Sync + 'static,
Out: Send + Sync + 'static,
{
fn into_provider(self) -> DynProvider {
dyn_for::<Out>(
type_name::<Func>(),
DepList::empty(),
Box::new(move |_container: &mut Container| Ok(ready_packed((self)()))),
)
}
}
impl<Func, T, ErrTy> FallibleProviderFn<$fallible<T, ErrTy>> for Func
where
Func: Fn() -> std::result::Result<T, ErrTy> + Send + Sync + 'static,
T: Send + Sync + 'static,
ErrTy: Into<crate::error::BoxError> + Send + 'static,
{
fn into_provider(self) -> DynProvider {
dyn_for::<T>(
type_name::<Func>(),
DepList::empty(),
Box::new(move |_container: &mut Container| {
let value = (self)().map_err(ctor_failed::<T>)?;
Ok(ready_packed(value))
}),
)
}
}
impl<Func, Fut, Out> AsyncProviderFn<$async_marker<Out>> for Func
where
Func: Fn() -> Fut + Send + Sync + 'static,
Fut: Future<Output = Out> + Send + 'static,
Out: Send + Sync + 'static,
{
fn into_provider(self) -> DynProvider {
dyn_for::<Out>(
type_name::<Func>(),
DepList::empty(),
Box::new(move |_container: &mut Container| {
let future = (self)();
Ok(ConstructOut::Fut(Box::pin(async move {
Ok(pack(future.await))
})))
}),
)
}
}
impl<Func, Fut, T, ErrTy> FallibleAsyncProviderFn<$async_fallible<T, ErrTy>> for Func
where
Func: Fn() -> Fut + Send + Sync + 'static,
Fut: Future<Output = std::result::Result<T, ErrTy>> + Send + 'static,
T: Send + Sync + 'static,
ErrTy: Into<crate::error::BoxError> + Send + 'static,
{
fn into_provider(self) -> DynProvider {
dyn_for::<T>(
type_name::<Func>(),
DepList::empty(),
Box::new(move |_container: &mut Container| {
let future = (self)();
Ok(ConstructOut::Fut(Box::pin(async move {
let value = future.await.map_err(ctor_failed::<T>)?;
Ok(pack(value))
})))
}),
)
}
}
};
}
macro_rules! impl_provider_fn {
(
$marker:ident,
$fallible:ident,
$async_marker:ident,
$async_fallible:ident,
$use_provide_result:ident,
$use_provide_result_async:ident,
$($A:ident),+
) => {
#[doc(hidden)]
pub struct $marker <Out, $($A),+>(PhantomData<fn() -> (Out, $($A,)+)>);
#[doc(hidden)]
pub struct $fallible <T, ErrTy, $($A),+>(PhantomData<fn() -> (T, ErrTy, $($A,)+)>);
#[doc(hidden)]
pub struct $async_marker <Out, $($A),+>(PhantomData<fn() -> (Out, $($A,)+)>);
#[doc(hidden)]
pub struct $async_fallible <T, ErrTy, $($A),+>(PhantomData<fn() -> (T, ErrTy, $($A,)+)>);
#[doc(hidden)]
pub struct $use_provide_result <T, ErrTy, $($A),+>(
PhantomData<fn() -> (T, ErrTy, $($A,)+)>,
);
#[doc(hidden)]
pub struct $use_provide_result_async <T, ErrTy, $($A),+>(
PhantomData<fn() -> (T, ErrTy, $($A,)+)>,
);
impl_use_provide_result_instead!($use_provide_result, $($A),+);
impl_use_provide_result_async_instead!($use_provide_result_async, $($A),+);
impl<Func, Out, $($A),+> ProviderFn<$marker<Out, $($A),+>> for Func
where
Func: Fn($($A),+) -> Out + Send + Sync + 'static,
Out: Send + Sync + 'static,
$($A: Clone + Send + Sync + 'static,)+
{
fn into_provider(self) -> DynProvider {
dyn_for::<Out>(
type_name::<Func>(),
DepList::from_array([$(crate::deps::dep::<$A>(),)+]),
Box::new(move |container: &mut Container| {
let value = (self)(
$(container.get::<$A>()?,)+
);
Ok(ready_packed(value))
}),
)
}
}
impl<Func, T, ErrTy, $($A),+> FallibleProviderFn<$fallible<T, ErrTy, $($A),+>> for Func
where
Func: Fn($($A),+) -> std::result::Result<T, ErrTy> + Send + Sync + 'static,
T: Send + Sync + 'static,
ErrTy: Into<crate::error::BoxError> + Send + 'static,
$($A: Clone + Send + Sync + 'static,)+
{
fn into_provider(self) -> DynProvider {
dyn_for::<T>(
type_name::<Func>(),
DepList::from_array([$(crate::deps::dep::<$A>(),)+]),
Box::new(move |container: &mut Container| {
let value = (self)(
$(container.get::<$A>()?,)+
)
.map_err(ctor_failed::<T>)?;
Ok(ready_packed(value))
}),
)
}
}
impl<Func, Fut, Out, $($A),+> AsyncProviderFn<$async_marker<Out, $($A),+>> for Func
where
Func: Fn($($A),+) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Out> + Send + 'static,
Out: Send + Sync + 'static,
$($A: Clone + Send + Sync + 'static,)+
{
fn into_provider(self) -> DynProvider {
dyn_for::<Out>(
type_name::<Func>(),
DepList::from_array([$(crate::deps::dep::<$A>(),)+]),
Box::new(move |container: &mut Container| {
let future = (self)(
$(container.get::<$A>()?,)+
);
Ok(ConstructOut::Fut(Box::pin(async move {
Ok(pack(future.await))
})))
}),
)
}
}
impl<Func, Fut, T, ErrTy, $($A),+>
FallibleAsyncProviderFn<$async_fallible<T, ErrTy, $($A),+>> for Func
where
Func: Fn($($A),+) -> Fut + Send + Sync + 'static,
Fut: Future<Output = std::result::Result<T, ErrTy>> + Send + 'static,
T: Send + Sync + 'static,
ErrTy: Into<crate::error::BoxError> + Send + 'static,
$($A: Clone + Send + Sync + 'static,)+
{
fn into_provider(self) -> DynProvider {
dyn_for::<T>(
type_name::<Func>(),
DepList::from_array([$(crate::deps::dep::<$A>(),)+]),
Box::new(move |container: &mut Container| {
let future = (self)(
$(container.get::<$A>()?,)+
);
Ok(ConstructOut::Fut(Box::pin(async move {
let value = future.await.map_err(ctor_failed::<T>)?;
Ok(pack(value))
})))
}),
)
}
}
};
}
impl_provider_fn_zero!(
Provider0,
Fallible0,
AsyncProvider0,
AsyncFallible0,
UseProvideResult0,
UseProvideResultAsync0
);
impl_provider_marker_zero!(Provider0);
impl_provider_marker_zero!(AsyncProvider0);
impl_provider_marker_fallible_zero!(Fallible0);
impl_provider_marker_fallible_zero!(AsyncFallible0);
impl_provider_fn!(
Provider1,
Fallible1,
AsyncProvider1,
AsyncFallible1,
UseProvideResult1,
UseProvideResultAsync1,
A
);
impl_provider_marker_arity!(Provider1, A);
impl_provider_marker_arity!(AsyncProvider1, A);
impl_provider_marker_fallible_arity!(Fallible1, A);
impl_provider_marker_fallible_arity!(AsyncFallible1, A);
impl_provider_fn!(
Provider2,
Fallible2,
AsyncProvider2,
AsyncFallible2,
UseProvideResult2,
UseProvideResultAsync2,
A,
B
);
impl_provider_marker_arity!(Provider2, A, B);
impl_provider_marker_arity!(AsyncProvider2, A, B);
impl_provider_marker_fallible_arity!(Fallible2, A, B);
impl_provider_marker_fallible_arity!(AsyncFallible2, A, B);
impl_provider_fn!(
Provider3,
Fallible3,
AsyncProvider3,
AsyncFallible3,
UseProvideResult3,
UseProvideResultAsync3,
A,
B,
C
);
impl_provider_marker_arity!(Provider3, A, B, C);
impl_provider_marker_arity!(AsyncProvider3, A, B, C);
impl_provider_marker_fallible_arity!(Fallible3, A, B, C);
impl_provider_marker_fallible_arity!(AsyncFallible3, A, B, C);
impl_provider_fn!(
Provider4,
Fallible4,
AsyncProvider4,
AsyncFallible4,
UseProvideResult4,
UseProvideResultAsync4,
A,
B,
C,
D
);
impl_provider_marker_arity!(Provider4, A, B, C, D);
impl_provider_marker_arity!(AsyncProvider4, A, B, C, D);
impl_provider_marker_fallible_arity!(Fallible4, A, B, C, D);
impl_provider_marker_fallible_arity!(AsyncFallible4, A, B, C, D);
impl_provider_fn!(
Provider5,
Fallible5,
AsyncProvider5,
AsyncFallible5,
UseProvideResult5,
UseProvideResultAsync5,
A,
B,
C,
D,
E
);
impl_provider_marker_arity!(Provider5, A, B, C, D, E);
impl_provider_marker_arity!(AsyncProvider5, A, B, C, D, E);
impl_provider_marker_fallible_arity!(Fallible5, A, B, C, D, E);
impl_provider_marker_fallible_arity!(AsyncFallible5, A, B, C, D, E);
impl_provider_fn!(
Provider6,
Fallible6,
AsyncProvider6,
AsyncFallible6,
UseProvideResult6,
UseProvideResultAsync6,
A,
B,
C,
D,
E,
F
);
impl_provider_marker_arity!(Provider6, A, B, C, D, E, F);
impl_provider_marker_arity!(AsyncProvider6, A, B, C, D, E, F);
impl_provider_marker_fallible_arity!(Fallible6, A, B, C, D, E, F);
impl_provider_marker_fallible_arity!(AsyncFallible6, A, B, C, D, E, F);
impl_provider_fn!(
Provider7,
Fallible7,
AsyncProvider7,
AsyncFallible7,
UseProvideResult7,
UseProvideResultAsync7,
A,
B,
C,
D,
E,
F,
G
);
impl_provider_marker_arity!(Provider7, A, B, C, D, E, F, G);
impl_provider_marker_arity!(AsyncProvider7, A, B, C, D, E, F, G);
impl_provider_marker_fallible_arity!(Fallible7, A, B, C, D, E, F, G);
impl_provider_marker_fallible_arity!(AsyncFallible7, A, B, C, D, E, F, G);
impl_provider_fn!(
Provider8,
Fallible8,
AsyncProvider8,
AsyncFallible8,
UseProvideResult8,
UseProvideResultAsync8,
A,
B,
C,
D,
E,
F,
G,
H
);
impl_provider_marker_arity!(Provider8, A, B, C, D, E, F, G, H);
impl_provider_marker_arity!(AsyncProvider8, A, B, C, D, E, F, G, H);
impl_provider_marker_fallible_arity!(Fallible8, A, B, C, D, E, F, G, H);
impl_provider_marker_fallible_arity!(AsyncFallible8, A, B, C, D, E, F, G, H);