use alloc::{collections::BTreeMap, vec, vec::Vec};
use core::any::TypeId;
use crate::{
async_impl::{
finalizer::{boxed_finalizer_factory as boxed_async_finalizer_factory, BoxedCloneFinalizer, Finalizer as AsyncFinalizer},
instantiator::{
boxed_container_instantiator, boxed_instantiator as boxed_async_instantiator_factory,
BoxedCloneInstantiator as BoxedCloneAsyncInstantiator, Instantiator as AsyncInstantiator,
},
Container,
},
dependency_resolver::DependencyResolver,
errors::{InstantiateErrorKind, ResolveErrorKind},
finalizer::{boxed_finalizer_factory, BoxedCloneFinalizer as BoxedCloneSyncFinalizer, Finalizer},
instantiator::{
boxed_container_instantiator as boxed_sync_container_instantiator, boxed_instantiator, BoxedCloneInstantiator, Config, Instantiator,
},
registry::{InstantiatorInnerData as SyncInstantiatorInnerData, ScopedRegistry as ScopedSyncRegistry},
scope::{Scope, ScopeInnerData},
utils::thread_safety::{SendSafety, SyncSafety},
Container as SyncContainer, DefaultScope, Scopes as ScopesTrait,
};
#[derive(Clone)]
pub(crate) enum BoxedInstantiator {
Sync(BoxedCloneInstantiator<ResolveErrorKind, InstantiateErrorKind>),
Async(BoxedCloneAsyncInstantiator<ResolveErrorKind, InstantiateErrorKind>),
}
pub(crate) struct InstantiatorData<S> {
instantiator: BoxedInstantiator,
config: Config,
scope: S,
}
pub struct RegistryBuilder<Scope> {
instantiators: BTreeMap<TypeId, InstantiatorData<Scope>>,
finalizers: BTreeMap<TypeId, BoxedCloneFinalizer>,
sync_finalizers: BTreeMap<TypeId, BoxedCloneSyncFinalizer>,
scopes: Vec<Scope>,
}
impl Default for RegistryBuilder<DefaultScope> {
fn default() -> Self {
Self::new()
}
}
impl RegistryBuilder<DefaultScope> {
#[inline]
#[must_use]
pub fn new() -> Self {
Self {
instantiators: BTreeMap::new(),
finalizers: BTreeMap::new(),
sync_finalizers: BTreeMap::new(),
scopes: Vec::from(DefaultScope::all()),
}
}
}
impl<Scope> RegistryBuilder<Scope> {
#[inline]
#[must_use]
pub fn new_with_scopes<Scopes, const N: usize>() -> Self
where
Scopes: ScopesTrait<N, Scope = Scope>,
{
Self {
instantiators: BTreeMap::new(),
finalizers: BTreeMap::new(),
sync_finalizers: BTreeMap::new(),
scopes: Vec::from(Scopes::all()),
}
}
}
impl<S> RegistryBuilder<S> {
#[inline]
#[must_use]
pub fn provide<Inst, Deps>(mut self, instantiator: Inst, scope: S) -> Self
where
Inst: Instantiator<Deps, Error = InstantiateErrorKind> + SendSafety + SyncSafety,
Deps: DependencyResolver<Error = ResolveErrorKind>,
{
self.add_instantiator::<Inst::Provides>(BoxedInstantiator::Sync(boxed_instantiator(instantiator)), scope);
self
}
#[inline]
#[must_use]
pub fn provide_with_config<Inst, Deps>(mut self, instantiator: Inst, config: Config, scope: S) -> Self
where
Inst: Instantiator<Deps, Error = InstantiateErrorKind> + SendSafety + SyncSafety,
Deps: DependencyResolver<Error = ResolveErrorKind>,
{
self.add_instantiator_with_config::<Inst::Provides>(BoxedInstantiator::Sync(boxed_instantiator(instantiator)), config, scope);
self
}
#[inline]
#[must_use]
pub fn provide_async<Inst, Deps>(mut self, instantiator: Inst, scope: S) -> Self
where
Inst: AsyncInstantiator<Deps, Error = InstantiateErrorKind> + SendSafety + SyncSafety,
Deps: DependencyResolver<Error = ResolveErrorKind>,
{
self.add_instantiator::<Inst::Provides>(BoxedInstantiator::Async(boxed_async_instantiator_factory(instantiator)), scope);
self
}
#[inline]
#[must_use]
pub fn provide_async_with_config<Inst, Deps>(mut self, instantiator: Inst, config: Config, scope: S) -> Self
where
Inst: AsyncInstantiator<Deps, Error = InstantiateErrorKind> + SendSafety + SyncSafety,
Deps: DependencyResolver<Error = ResolveErrorKind>,
{
self.add_instantiator_with_config::<Inst::Provides>(
BoxedInstantiator::Async(boxed_async_instantiator_factory(instantiator)),
config,
scope,
);
self
}
#[inline]
#[must_use]
pub fn add_finalizer<Dep: SendSafety + SyncSafety + 'static>(
mut self,
finalizer: impl Finalizer<Dep> + SendSafety + SyncSafety,
) -> Self {
self.sync_finalizers.insert(TypeId::of::<Dep>(), boxed_finalizer_factory(finalizer));
self
}
#[inline]
#[must_use]
pub fn add_async_finalizer<Dep: SendSafety + SyncSafety + 'static>(
mut self,
finalizer: impl AsyncFinalizer<Dep> + SendSafety + SyncSafety,
) -> Self {
self.finalizers
.insert(TypeId::of::<Dep>(), boxed_async_finalizer_factory(finalizer));
self
}
}
impl<S> RegistryBuilder<S> {
#[inline]
pub(crate) fn add_instantiator<Dep: 'static>(&mut self, instantiator: BoxedInstantiator, scope: S) -> Option<InstantiatorData<S>> {
self.add_instantiator_with_config::<Dep>(instantiator, Config::default(), scope)
}
#[inline]
pub(crate) fn add_instantiator_with_config<Dep: 'static>(
&mut self,
instantiator: BoxedInstantiator,
config: Config,
scope: S,
) -> Option<InstantiatorData<S>> {
self.instantiators.insert(
TypeId::of::<Dep>(),
InstantiatorData {
instantiator,
config,
scope,
},
)
}
}
impl<S> RegistryBuilder<S>
where
S: Scope + Clone,
{
pub(crate) fn build(mut self) -> (Vec<ScopedRegistry>, Vec<ScopedSyncRegistry>) {
use alloc::collections::btree_map::Entry::{Occupied, Vacant};
let mut scopes_instantiators: BTreeMap<S, Vec<(TypeId, InstantiatorInnerData)>> =
self.scopes.clone().into_iter().map(|scope| (scope, Vec::new())).collect();
let mut scopes_sync_instantiators: BTreeMap<S, Vec<(TypeId, SyncInstantiatorInnerData)>> =
self.scopes.into_iter().map(|scope| (scope, Vec::new())).collect();
for (
type_id,
InstantiatorData {
instantiator,
config,
scope,
},
) in self.instantiators
{
match instantiator {
BoxedInstantiator::Sync(instantiator) => match scopes_sync_instantiators.entry(scope) {
Vacant(entry) => {
entry.insert(vec![(
type_id,
SyncInstantiatorInnerData {
instantiator,
finalizer: self.sync_finalizers.remove(&type_id),
config,
},
)]);
}
Occupied(entry) => {
entry.into_mut().push((
type_id,
SyncInstantiatorInnerData {
instantiator,
finalizer: self.sync_finalizers.remove(&type_id),
config,
},
));
}
},
BoxedInstantiator::Async(instantiator) => match scopes_instantiators.entry(scope) {
Vacant(entry) => {
entry.insert(vec![(
type_id,
InstantiatorInnerData {
instantiator,
finalizer: self.finalizers.remove(&type_id),
config,
},
)]);
}
Occupied(entry) => {
entry.into_mut().push((
type_id,
InstantiatorInnerData {
instantiator,
finalizer: self.finalizers.remove(&type_id),
config,
},
));
}
},
}
}
let container_type_id = TypeId::of::<Container>();
let container_instantiator_data = InstantiatorInnerData {
instantiator: boxed_container_instantiator(),
finalizer: None,
config: Config { cache_provides: true },
};
let mut registries = Vec::with_capacity(scopes_instantiators.len());
for (scope, instantiators) in scopes_instantiators {
let mut instantiators = BTreeMap::from_iter(instantiators);
instantiators.insert(container_type_id, container_instantiator_data.clone());
registries.push(ScopedRegistry {
scope: ScopeInnerData {
priority: scope.priority(),
is_skipped_by_default: scope.is_skipped_by_default(),
},
instantiators,
});
}
let container_type_id = TypeId::of::<SyncContainer>();
let container_instantiator_data = SyncInstantiatorInnerData {
instantiator: boxed_sync_container_instantiator(),
finalizer: None,
config: Config { cache_provides: true },
};
let mut sync_registries = Vec::with_capacity(scopes_sync_instantiators.len());
for (scope, instantiators) in scopes_sync_instantiators {
let mut instantiators = BTreeMap::from_iter(instantiators);
instantiators.insert(container_type_id, container_instantiator_data.clone());
sync_registries.push(ScopedSyncRegistry {
scope: ScopeInnerData {
priority: scope.priority(),
is_skipped_by_default: scope.is_skipped_by_default(),
},
instantiators,
});
}
(registries, sync_registries)
}
}
#[derive(Clone)]
pub(crate) struct InstantiatorInnerData {
pub(crate) instantiator: BoxedCloneAsyncInstantiator<ResolveErrorKind, InstantiateErrorKind>,
pub(crate) finalizer: Option<BoxedCloneFinalizer>,
pub(crate) config: Config,
}
pub(crate) struct ScopedRegistry {
pub(crate) scope: ScopeInnerData,
instantiators: BTreeMap<TypeId, InstantiatorInnerData>,
}
impl ScopedRegistry {
#[inline]
pub(crate) fn get_instantiator(&self, type_id: &TypeId) -> Option<BoxedCloneAsyncInstantiator<ResolveErrorKind, InstantiateErrorKind>> {
self.instantiators.get(type_id).map(|data| data.instantiator.clone())
}
#[inline]
pub(crate) fn get_instantiator_data(&self, type_id: &TypeId) -> Option<InstantiatorInnerData> {
self.instantiators.get(type_id).cloned()
}
}
#[cfg(test)]
mod tests {
use super::{BoxedCloneInstantiator, InstantiateErrorKind, RegistryBuilder, ResolveErrorKind};
use crate::{
scope::DefaultScope::{self, *},
utils::thread_safety::RcThreadSafety,
Scopes,
};
use core::any::TypeId;
#[test]
fn test_build_empty() {
let (registries, sync_registries) = RegistryBuilder::<DefaultScope>::new().build();
assert!(!registries.is_empty());
assert!(!sync_registries.is_empty());
}
#[test]
fn test_build_equal_provides() {
let (registries, sync_registries) = RegistryBuilder::new()
.provide(|| Ok(()), Runtime)
.provide(|| Ok(()), Runtime)
.provide(|| Ok(()), App)
.provide(|| Ok(()), App)
.provide_async(async || Ok(((), ())), Runtime)
.provide_async(async || Ok(((), ())), Runtime)
.provide_async(async || Ok(((), ())), App)
.provide_async(async || Ok(((), ())), App)
.build();
assert_eq!(registries.len(), DefaultScope::all().len());
assert_eq!(sync_registries.len(), DefaultScope::all().len());
for registry in registries {
if registry.scope.priority == 1 {
assert_eq!(registry.instantiators.len(), 2);
} else {
assert_eq!(registry.instantiators.len(), 1);
}
}
for registry in sync_registries {
if registry.scope.priority == 1 {
assert_eq!(registry.instantiators.len(), 2);
} else {
assert_eq!(registry.instantiators.len(), 1);
}
}
}
#[test]
fn test_build_several_scopes() {
let (registries, sync_registries) = RegistryBuilder::new()
.provide(|| Ok(1i8), Runtime)
.provide(|| Ok(1i16), Runtime)
.provide(|| Ok(1i32), App)
.provide(|| Ok(1i64), App)
.provide_async(async || Ok(1u8), Runtime)
.provide_async(async || Ok(1u16), Runtime)
.provide_async(async || Ok(1u32), App)
.provide_async(async || Ok(1u64), App)
.build();
assert_eq!(registries.len(), DefaultScope::all().len());
assert_eq!(sync_registries.len(), DefaultScope::all().len());
for registry in registries {
if registry.scope.priority == 0 || registry.scope.priority == 1 {
assert_eq!(registry.instantiators.len(), 3);
} else {
assert_eq!(registry.instantiators.len(), 1);
}
}
for registry in sync_registries {
if registry.scope.priority == 0 || registry.scope.priority == 1 {
assert_eq!(registry.instantiators.len(), 3);
} else {
assert_eq!(registry.instantiators.len(), 1);
}
}
}
#[test]
fn test_add_finalizer() {
let (registries, sync_registries) = RegistryBuilder::new()
.provide(|| Ok(1i8), Runtime)
.provide(|| Ok(1i16), Runtime)
.provide(|| Ok(1i32), App)
.provide(|| Ok(1i64), App)
.provide_async(async || Ok(1u8), Runtime)
.provide_async(async || Ok(1u16), Runtime)
.provide_async(async || Ok(1u32), App)
.provide_async(async || Ok(1u64), App)
.add_finalizer(|_: RcThreadSafety<i8>| {})
.add_finalizer(|_: RcThreadSafety<i32>| {})
.add_async_finalizer(async |_: RcThreadSafety<u8>| {})
.add_async_finalizer(async |_: RcThreadSafety<u32>| {})
.build();
let i8_type_id = TypeId::of::<i8>();
let i16_type_id = TypeId::of::<i16>();
let i32_type_id = TypeId::of::<i32>();
let i64_type_id = TypeId::of::<i64>();
let u8_type_id = TypeId::of::<u8>();
let u16_type_id = TypeId::of::<u16>();
let u32_type_id = TypeId::of::<u32>();
let u64_type_id = TypeId::of::<u64>();
for registry in registries {
if let Some(data) = registry.instantiators.get(&u8_type_id) {
assert!(data.finalizer.is_some());
continue;
}
if let Some(data) = registry.instantiators.get(&u16_type_id) {
assert!(data.finalizer.is_none());
continue;
}
if let Some(data) = registry.instantiators.get(&u32_type_id) {
assert!(data.finalizer.is_some());
continue;
}
if let Some(data) = registry.instantiators.get(&u64_type_id) {
assert!(data.finalizer.is_none());
}
}
for registry in sync_registries {
if let Some(data) = registry.instantiators.get(&i8_type_id) {
assert!(data.finalizer.is_some());
continue;
}
if let Some(data) = registry.instantiators.get(&i16_type_id) {
assert!(data.finalizer.is_none());
continue;
}
if let Some(data) = registry.instantiators.get(&i32_type_id) {
assert!(data.finalizer.is_some());
continue;
}
if let Some(data) = registry.instantiators.get(&i64_type_id) {
assert!(data.finalizer.is_none());
}
}
}
#[test]
fn test_bounds() {
fn impl_bounds<T: Send>() {}
impl_bounds::<(BoxedCloneInstantiator<ResolveErrorKind, InstantiateErrorKind>,)>();
}
}