use std::{any::Any, borrow::Cow, rc::Rc};
use crate::{BoxFuture, Color, Context, Definition, FutureExt, Key, Scope};
pub trait DefaultProvider {
type Type;
fn provider() -> Provider<Self::Type>;
}
pub(crate) enum Constructor<T> {
#[allow(clippy::type_complexity)]
Async(Rc<dyn for<'a> Fn(&'a mut Context) -> BoxFuture<'a, T>>),
Sync(Rc<dyn Fn(&mut Context) -> T>),
None,
}
impl<T> Clone for Constructor<T> {
fn clone(&self) -> Self {
match self {
Self::Async(c) => Self::Async(Rc::clone(c)),
Self::Sync(c) => Self::Sync(Rc::clone(c)),
Self::None => Self::None,
}
}
}
#[derive(Clone)]
pub enum EagerCreateFunction {
Async(for<'a> fn(&'a mut Context, Cow<'static, str>) -> BoxFuture<'a, ()>),
Sync(fn(&mut Context, Cow<'static, str>)),
None,
}
pub struct Provider<T> {
definition: Definition,
eager_create: bool,
condition: Option<fn(&Context) -> bool>,
constructor: Constructor<T>,
clone_instance: Option<fn(&T) -> T>,
eager_create_function: EagerCreateFunction,
binding_providers: Option<Vec<DynProvider>>,
binding_definitions: Option<Vec<Definition>>,
}
impl<T> Provider<T> {
pub fn definition(&self) -> &Definition {
&self.definition
}
pub fn eager_create(&self) -> bool {
self.eager_create
}
pub fn binding_definitions(&self) -> Option<&Vec<Definition>> {
self.binding_definitions.as_ref()
}
pub fn condition(&self) -> Option<fn(&Context) -> bool> {
self.condition
}
pub(crate) fn constructor(&self) -> Constructor<T> {
self.constructor.clone()
}
pub(crate) fn clone_instance(&self) -> Option<fn(&T) -> T> {
self.clone_instance
}
}
impl<T: 'static> Provider<T> {
pub(crate) fn with_name(
name: Cow<'static, str>,
scope: Scope,
eager_create: bool,
condition: Option<fn(&Context) -> bool>,
constructor: Constructor<T>,
clone_instance: Option<fn(&T) -> T>,
eager_create_function: EagerCreateFunction,
) -> Self {
let definition = Definition::new::<T>(
name,
scope,
Some(match constructor {
Constructor::Async(_) => Color::Async,
Constructor::Sync(_) => Color::Sync,
Constructor::None => unreachable!(),
}),
condition.is_some(),
);
Provider {
definition,
eager_create,
condition,
constructor,
clone_instance,
eager_create_function,
binding_providers: None,
binding_definitions: None,
}
}
pub(crate) fn with_definition(
definition: Definition,
eager_create: bool,
condition: Option<fn(&Context) -> bool>,
constructor: Constructor<T>,
clone_instance: Option<fn(&T) -> T>,
eager_create_function: EagerCreateFunction,
) -> Self {
Provider {
definition,
eager_create,
condition,
constructor,
clone_instance,
eager_create_function,
binding_providers: None,
binding_definitions: None,
}
}
pub(crate) fn never_construct(name: Cow<'static, str>, scope: Scope) -> Self {
Provider {
definition: Definition::new::<T>(name, scope, None, false),
eager_create: false,
condition: None,
constructor: Constructor::None,
clone_instance: None,
eager_create_function: EagerCreateFunction::None,
binding_providers: None,
binding_definitions: None,
}
}
}
pub struct DynProvider {
definition: Definition,
eager_create: bool,
condition: Option<fn(&Context) -> bool>,
eager_create_function: EagerCreateFunction,
binding_providers: Option<Vec<DynProvider>>,
binding_definitions: Option<Vec<Definition>>,
origin: Box<dyn Any>,
}
impl DynProvider {
pub fn definition(&self) -> &Definition {
&self.definition
}
pub fn eager_create(&self) -> bool {
self.eager_create
}
pub fn binding_definitions(&self) -> Option<&Vec<Definition>> {
self.binding_definitions.as_ref()
}
pub fn as_provider<T: 'static>(&self) -> Option<&Provider<T>> {
self.origin.downcast_ref::<Provider<T>>()
}
pub fn condition(&self) -> Option<fn(&Context) -> bool> {
self.condition
}
pub(crate) fn key(&self) -> &Key {
&self.definition.key
}
pub(crate) fn eager_create_function(&self) -> EagerCreateFunction {
self.eager_create_function.clone()
}
pub(crate) fn binding_providers(&mut self) -> Option<Vec<DynProvider>> {
self.binding_providers.take()
}
}
impl<T: 'static> From<Provider<T>> for DynProvider {
fn from(mut value: Provider<T>) -> Self {
Self {
definition: value.definition.clone(),
eager_create: value.eager_create,
condition: value.condition,
eager_create_function: value.eager_create_function.clone(),
binding_providers: value.binding_providers.take(),
binding_definitions: value.binding_definitions.clone(),
origin: Box::new(value),
}
}
}
fn sync_constructor<T, U, F>(name: Cow<'static, str>, transform: F) -> Rc<dyn Fn(&mut Context) -> U>
where
T: 'static,
F: Fn(T) -> U + 'static,
{
let constructor = move |cx: &mut Context| {
let instance = cx.resolve_with_name(name.clone());
transform(instance)
};
Rc::new(constructor)
}
fn sync_eager_create_function<T: 'static>() -> fn(&mut Context, Cow<'static, str>) {
|cx, name| {
cx.just_create::<T>(name);
}
}
#[allow(clippy::type_complexity)]
fn async_constructor<T, U, F>(
name: Cow<'static, str>,
transform: F,
) -> Rc<dyn for<'a> Fn(&'a mut Context) -> BoxFuture<'a, U>>
where
T: 'static,
F: Fn(T) -> U + 'static + Clone,
{
fn helper<'a, F, T, U>(
cx: &'a mut Context,
name: Cow<'static, str>,
transform: F,
) -> BoxFuture<'a, U>
where
T: 'static,
F: Fn(T) -> U + 'static,
{
async move {
let instance = cx.resolve_with_name_async(name).await;
transform(instance)
}
.boxed()
}
Rc::new(move |cx| helper(cx, name.clone(), transform.clone()))
}
fn async_eager_create_function<T: 'static>(
) -> for<'a> fn(&'a mut Context, Cow<'static, str>) -> BoxFuture<'a, ()> {
|cx, name| {
async {
cx.just_create_async::<T>(name).await;
}
.boxed()
}
}
macro_rules! define_provider_common {
(
$provider:ident,
$function:ident,
$clone_instance:expr,
$(+ $bound:ident)*
) => {
#[doc = concat!("Use the [`", stringify!($function), "`] function to create this provider.")]
pub struct $provider<T> {
constructor: Constructor<T>,
name: Cow<'static, str>,
eager_create: bool,
condition: Option<fn(&Context) -> bool>,
bind_closures: Vec<Box<dyn FnOnce(Definition, bool, Option<fn(&Context) -> bool>) -> DynProvider>>,
}
impl<T> $provider<T> {
pub fn name<N>(mut self, name: N) -> Self
where
N: Into<Cow<'static, str>>,
{
self.name = name.into();
self
}
pub fn eager_create(mut self, eager_create: bool) -> Self {
self.eager_create = eager_create;
self
}
pub fn condition(mut self, condition: Option<fn(&Context) -> bool>) -> Self {
self.condition = condition;
self
}
}
impl<T: 'static $(+ $bound)*> From<$provider<T>> for DynProvider {
fn from(value: $provider<T>) -> Self {
DynProvider::from(Provider::from(value))
}
}
};
}
macro_rules! define_provider_sync {
(
$provider:ident,
$scope:expr,
$function:ident,
$clone_instance:expr,
$(+ $bound:ident)*
) => {
#[doc = concat!("create a [`", stringify!($provider), "`] instance")]
#[doc = concat!("use rudi::{", stringify!($function), ", ", stringify!($provider), "};")]
#[doc = concat!(" let _: ", stringify!($provider), "<A> = ", stringify!($function), "(|cx| A(cx.resolve()));")]
pub fn $function<T, C>(constructor: C) -> $provider<T>
where
C: Fn(&mut Context) -> T + 'static,
{
$provider {
constructor: Constructor::Sync(Rc::new(constructor)),
name: Cow::Borrowed(""),
eager_create: false,
condition: None,
bind_closures: Vec::new(),
}
}
impl<T: 'static> $provider<T> {
#[doc = concat!("use rudi::{", stringify!($function), ", Provider, ", stringify!($provider), "};")]
#[doc = concat!(" let p: ", stringify!($provider), "<A> = ", stringify!($function), "(|cx| A(cx.resolve()))")]
pub fn bind<U, F>(mut self, transform: F) -> Self
where
U: 'static $(+ $bound)*,
F: Fn(T) -> U + 'static,
{
let bind_closure = |definition: Definition, eager_create: bool, condition: Option<fn(&Context) -> bool>| {
let name = definition.key.name.clone();
Provider::with_definition(
definition.bind::<U>(),
eager_create,
condition,
Constructor::Sync(sync_constructor(name, transform)),
$clone_instance,
EagerCreateFunction::Sync(
sync_eager_create_function::<U>()
),
)
.into()
};
let bind_closure = Box::new(bind_closure);
self.bind_closures.push(bind_closure);
self
}
}
impl<T: 'static $(+ $bound)*> From<$provider<T>> for Provider<T> {
fn from(value: $provider<T>) -> Self {
let $provider {
constructor,
name,
eager_create,
condition,
bind_closures,
} = value;
let mut provider = Provider::with_name(
name,
$scope,
eager_create,
condition,
constructor,
$clone_instance,
EagerCreateFunction::Sync(
sync_eager_create_function::<T>()
),
);
if bind_closures.is_empty() {
return provider;
}
let definition = &provider.definition;
let (definitions, providers) = bind_closures.into_iter()
.map(|bind_closure| {
let provider = bind_closure(definition.clone(), eager_create, condition);
(provider.definition.clone(), provider)
})
.unzip();
provider.binding_definitions = Some(definitions);
provider.binding_providers = Some(providers);
provider
}
}
};
}
macro_rules! define_provider_async {
(
$provider:ident,
$scope:expr,
$function:ident,
$clone_instance:expr,
$(+ $bound:ident)*
) => {
#[doc = concat!("Create a [`", stringify!($provider), "`] instance")]
#[doc = concat!("use rudi::{", stringify!($function), ", FutureExt, ", stringify!($provider), "};")]
#[doc = concat!(" let _: ", stringify!($provider), "<A> =")]
#[doc = concat!(" ", stringify!($function), "(|cx| async { A(cx.resolve_async().await) }.boxed());")]
pub fn $function<T, C>(constructor: C) -> $provider<T>
where
C: for<'a> Fn(&'a mut Context) -> BoxFuture<'a, T> + 'static,
{
$provider {
constructor: Constructor::Async(Rc::new(constructor)),
name: Cow::Borrowed(""),
eager_create: false,
condition: None,
bind_closures: Vec::new(),
}
}
impl<T: 'static> $provider<T> {
#[doc = concat!("use rudi::{", stringify!($function), ", FutureExt, Provider, ", stringify!($provider), "};")]
#[doc = concat!(" let p: ", stringify!($provider), "<A> =")]
#[doc = concat!(" ", stringify!($function), "(|cx| async { A(cx.resolve_async().await) }.boxed())")]
pub fn bind<U, F>(mut self, transform: F) -> Self
where
U: 'static $(+ $bound)*,
F: Fn(T) -> U + 'static + Clone,
{
let bind_closure = |definition: Definition, eager_create: bool, condition: Option<fn(&Context) -> bool>| {
let name = definition.key.name.clone();
Provider::with_definition(
definition.bind::<U>(),
eager_create,
condition,
Constructor::Async(async_constructor(name, transform)),
$clone_instance,
EagerCreateFunction::Async(
async_eager_create_function::<U>()
),
)
.into()
};
let bind_closure = Box::new(bind_closure);
self.bind_closures.push(bind_closure);
self
}
}
impl<T: 'static $(+ $bound)*> From<$provider<T>> for Provider<T> {
fn from(value: $provider<T>) -> Self {
let $provider {
constructor,
name,
eager_create,
condition,
bind_closures,
} = value;
let mut provider = Provider::with_name(
name,
$scope,
eager_create,
condition,
constructor,
$clone_instance,
EagerCreateFunction::Async(
async_eager_create_function::<T>()
),
);
if bind_closures.is_empty() {
return provider;
}
let definition = &provider.definition;
let (definitions, providers) = bind_closures.into_iter()
.map(|bind_closure| {
let provider = bind_closure(definition.clone(), eager_create, condition);
(provider.definition.clone(), provider)
})
.unzip();
provider.binding_definitions = Some(definitions);
provider.binding_providers = Some(providers);
provider
}
}
};
}
define_provider_common!(SingletonProvider, singleton, Some(Clone::clone), + Clone);
define_provider_common!(TransientProvider, transient, None,);
define_provider_common!(SingleOwnerProvider, single_owner, None,);
define_provider_common!(SingletonAsyncProvider, singleton_async, Some(Clone::clone), + Clone);
define_provider_common!(TransientAsyncProvider, transient_async, None,);
define_provider_common!(SingleOwnerAsyncProvider, single_owner_async, None,);
define_provider_sync!(SingletonProvider, Scope::Singleton, singleton, Some(Clone::clone), + Clone);
define_provider_sync!(TransientProvider, Scope::Transient, transient, None,);
define_provider_sync!(SingleOwnerProvider, Scope::SingleOwner, single_owner, None,);
define_provider_async!(SingletonAsyncProvider, Scope::Singleton, singleton_async, Some(Clone::clone), + Clone);
define_provider_async!(
TransientAsyncProvider,
Scope::Transient,
transient_async,
None,
);
define_provider_async!(
SingleOwnerAsyncProvider,
Scope::SingleOwner,
single_owner_async,
None,
);