use alloc::{boxed::Box, vec::Vec};
use core::future::Future;
use parking_lot::RwLock;
use tracing::{debug, error, trace};
use super::{
registry::{InstantiatorData, Registry},
service::Service as _,
};
#[cfg(feature = "thread_safe")]
use crate::lock::PerTypeLocks;
use crate::{
any::TypeInfo,
async_impl::registry::RegistryWithSync,
cache::{Cache, Resolved},
container::{BoxedContainerInner as BoxedSyncContainerInner, Container as SyncContainer, ContainerInner as SyncContainerInner},
context::Context,
errors::{InstantiatorErrorKind, ResolveErrorKind, ScopeErrorKind, ScopeWithErrorKind},
lock::PerTypeSharedLocks,
registry::Registry as SyncRegistry,
scope::{Scope, ScopeData, ScopeDataWithChildScopesData},
utils::thread_safety::{RcThreadSafety, SendSafety, SyncSafety},
};
#[derive(Clone)]
pub struct Container {
inner: RcThreadSafety<ContainerInner>,
sync: SyncContainer,
per_type_locks: PerTypeSharedLocks,
}
impl Container {
#[must_use]
pub fn new(RegistryWithSync { registry, sync }: RegistryWithSync) -> Self {
let scope_with_child_scopes = registry.get_scope_with_child_scopes();
let registry = RcThreadSafety::new(registry);
let sync_registry = RcThreadSafety::new(sync);
let mut sync_container = BoxedSyncContainerInner {
cache: Cache::new(),
context: Context::new(),
registry: sync_registry.clone(),
scope_data: scope_with_child_scopes.scope_data.expect("scopes len (is 0) should be > 0"),
child_scopes_data: scope_with_child_scopes.child_scopes_data.clone(),
parent: None,
close_parent: false,
#[cfg(feature = "thread_safe")]
per_type_locks: PerTypeLocks::default(),
};
let mut container = BoxedContainerInner {
cache: Cache::new(),
context: Context::new(),
registry: registry.clone(),
scope_data: scope_with_child_scopes.scope_data.unwrap(),
child_scopes_data: scope_with_child_scopes.child_scopes_data.clone(),
parent: None,
close_parent: false,
};
let mut child = scope_with_child_scopes.child();
let mut scope_data = child.scope_data.expect("scopes len (is 1) should be > 1");
let mut search_next = container.scope_data.is_skipped_by_default;
while search_next {
search_next = scope_data.is_skipped_by_default;
sync_container = sync_container.init_child(sync_registry.clone(), scope_data, child.child_scopes_data.clone(), true);
container = container.init_child(registry.clone(), scope_data, child.child_scopes_data.clone(), true);
if search_next {
child = child.child();
scope_data = child.scope_data.expect("last scope can't be skipped by default");
} else {
break;
}
}
(container, sync_container).into()
}
#[must_use]
#[allow(clippy::needless_pass_by_value)]
pub fn new_with_start_scope<S: Scope + Clone>(RegistryWithSync { registry, sync }: RegistryWithSync, scope: S) -> Self {
let scope_with_child_scopes = registry.get_scope_with_child_scopes();
let registry = RcThreadSafety::new(registry);
let sync_registry = RcThreadSafety::new(sync);
let mut sync_container = BoxedSyncContainerInner {
cache: Cache::new(),
context: Context::new(),
registry: sync_registry.clone(),
scope_data: scope_with_child_scopes.scope_data.expect("scopes len (is 0) should be > 0"),
child_scopes_data: scope_with_child_scopes.child_scopes_data.clone(),
parent: None,
close_parent: false,
#[cfg(feature = "thread_safe")]
per_type_locks: PerTypeLocks::default(),
};
let mut container = BoxedContainerInner {
cache: Cache::new(),
context: Context::new(),
registry: registry.clone(),
scope_data: scope_with_child_scopes.scope_data.unwrap(),
child_scopes_data: scope_with_child_scopes.child_scopes_data.clone(),
parent: None,
close_parent: false,
};
let priority = scope.priority();
if container.scope_data.priority == priority {
return (container, sync_container).into();
}
let mut child = scope_with_child_scopes.child();
let mut scope_data = child.scope_data.expect("last scope can't be with another priority");
let mut search_next = container.scope_data.priority != priority;
while search_next {
search_next = scope_data.priority != priority;
sync_container = sync_container.init_child(sync_registry.clone(), scope_data, child.child_scopes_data.clone(), true);
container = container.init_child(registry.clone(), scope_data, child.child_scopes_data.clone(), true);
if search_next {
child = child.child();
scope_data = child.scope_data.expect("last scope can't be skipped by default");
} else {
break;
}
}
(container, sync_container).into()
}
#[inline]
#[must_use]
pub fn enter(self) -> ChildContainerBuilder {
ChildContainerBuilder { container: self }
}
#[inline]
pub fn enter_build(self) -> Result<Container, ScopeErrorKind> {
self.enter().build()
}
#[inline]
#[allow(clippy::missing_errors_doc, clippy::multiple_bound_locations, clippy::missing_panics_doc)]
pub fn get<Dep: SendSafety + SyncSafety + 'static>(
&self,
) -> impl Future<Output = Result<RcThreadSafety<Dep>, ResolveErrorKind>> + SendSafety + '_ {
let type_info = TypeInfo::of::<Dep>();
let dep_name = type_info.name;
let scope_name = self.inner.scope_data.name;
let fut = async move {
if let Some(dependency) = { self.inner.cache.read().get(&type_info) } {
debug!("Found in cache");
return Ok(dependency);
}
debug!("Not found in cache");
let Some(InstantiatorData {
instantiator,
finalizer,
config,
scope_data,
..
}) = self.inner.registry.get(&type_info)
else {
debug!("No instantiator found, trying sync container");
return self.sync.get();
};
let current_priority = self.inner.scope_data.priority;
let dep_priority = scope_data.priority;
if current_priority > dep_priority {
let mut parent = self
.inner
.parent
.as_ref()
.expect("parent should exist for lower-priority dependency");
while parent.scope_data.priority != dep_priority {
parent = parent.parent.as_ref().expect("parent with target priority should exist");
}
return match (Self {
inner: parent.clone(),
sync: self.sync.clone(),
per_type_locks: self.per_type_locks.clone(),
})
.get::<Dep>()
.await
{
Ok(dependency) => {
self.inner.cache.write().insert_rc(type_info, dependency.clone());
Ok(dependency)
}
Err(err) => Err(err),
};
}
if dep_priority > current_priority {
let err = ResolveErrorKind::NoAccessible {
expected_scope_data: *scope_data,
actual_scope_data: self.inner.scope_data,
};
error!(dependency = dep_name, scope = scope_name, error = %err, "Failed to resolve dependency");
return Err(err);
}
trace!("Lock instantiator call");
let inst_call_lock = self.per_type_locks.get(type_info.id);
let _guard = inst_call_lock.lock().await;
if let Some(dependency) = { self.inner.cache.read().get(&type_info) } {
debug!("Found in cache after lock");
return Ok(dependency);
}
match instantiator.clone().call(self.clone()).await {
Ok(dependency) => match dependency.downcast::<Dep>() {
Ok(dependency) => {
let dependency = RcThreadSafety::new(*dependency);
let cache_provides = config.cache_provides;
let has_finalizer = finalizer.is_some();
if cache_provides || has_finalizer {
let mut cache = self.inner.cache.write();
if cache_provides {
cache.insert_rc(type_info.clone(), dependency.clone());
debug!("Cached");
}
if has_finalizer {
cache.push_resolved(Resolved {
type_info,
dependency: dependency.clone(),
});
debug!("Pushed to resolved set");
}
}
Ok(dependency)
}
Err(incorrect_type) => {
let err = ResolveErrorKind::IncorrectType {
expected: type_info,
actual: TypeInfo::of_val(&*incorrect_type),
};
error!(dependency = dep_name, scope = scope_name, error = %err, "Failed to resolve dependency");
Err(err)
}
},
Err(InstantiatorErrorKind::Deps(err)) => {
error!(dependency = dep_name, scope = scope_name, error = %err, "Failed to resolve dependency");
Err(ResolveErrorKind::Instantiator(InstantiatorErrorKind::Deps(Box::new(err))))
}
Err(InstantiatorErrorKind::Factory(err)) => {
error!(dependency = dep_name, scope = scope_name, error = %err, "Failed to resolve dependency");
Err(ResolveErrorKind::Instantiator(InstantiatorErrorKind::Factory(err)))
}
}
};
Box::pin(fut)
}
#[inline]
#[allow(clippy::missing_errors_doc, clippy::multiple_bound_locations, clippy::missing_panics_doc)]
pub fn get_transient<Dep: 'static>(&self) -> impl Future<Output = Result<Dep, ResolveErrorKind>> + SendSafety + '_ {
let type_info = TypeInfo::of::<Dep>();
let dep_name = type_info.name;
let scope_name = self.inner.scope_data.name;
let fut = async move {
let Some(InstantiatorData {
instantiator, scope_data, ..
}) = self.inner.registry.get(&type_info)
else {
debug!("No instantiator found, trying sync container");
return self.sync.get_transient();
};
let current_priority = self.inner.scope_data.priority;
let dep_priority = scope_data.priority;
if current_priority > dep_priority {
let mut parent = self
.inner
.parent
.as_ref()
.expect("parent should exist for lower-priority dependency");
while parent.scope_data.priority != dep_priority {
parent = parent.parent.as_ref().expect("parent with target priority should exist");
}
return (Self {
inner: parent.clone(),
sync: self.sync.clone(),
per_type_locks: self.per_type_locks.clone(),
})
.get_transient()
.await;
}
if dep_priority > current_priority {
let err = ResolveErrorKind::NoAccessible {
expected_scope_data: *scope_data,
actual_scope_data: self.inner.scope_data,
};
error!(dependency = dep_name, scope = scope_name, error = %err, "Failed to resolve transient dependency");
return Err(err);
}
match instantiator.clone().call(self.clone()).await {
Ok(dependency) => match dependency.downcast::<Dep>() {
Ok(dependency) => Ok(*dependency),
Err(incorrect_type) => {
let err = ResolveErrorKind::IncorrectType {
expected: type_info,
actual: TypeInfo::of_val(&*incorrect_type),
};
error!(dependency = dep_name, scope = scope_name, error = %err, "Failed to resolve transient dependency");
Err(err)
}
},
Err(InstantiatorErrorKind::Deps(err)) => {
error!(dependency = dep_name, scope = scope_name, error = %err, "Failed to resolve transient dependency");
Err(ResolveErrorKind::Instantiator(InstantiatorErrorKind::Deps(Box::new(err))))
}
Err(InstantiatorErrorKind::Factory(err)) => {
error!(dependency = dep_name, scope = scope_name, error = %err, "Failed to resolve transient dependency");
Err(ResolveErrorKind::Instantiator(InstantiatorErrorKind::Factory(err)))
}
}
};
Box::pin(fut)
}
pub fn close(&self) -> impl Future<Output = ()> + SendSafety + '_ {
let fut = async move {
self.inner.close().await;
self.sync.inner.close_with_parent_flag(false);
let mut inner_parent = self.inner.parent.as_ref();
let mut sync_parent = self.sync.inner.parent.as_ref();
let mut close_parent = self.inner.close_parent;
while close_parent {
match (inner_parent, sync_parent) {
(Some(container), Some(sync_container)) => {
sync_container.inner.close_with_parent_flag(false);
container.close().await;
close_parent = container.close_parent;
inner_parent = container.parent.as_ref();
sync_parent = sync_container.inner.parent.as_ref();
}
(None, None) => break,
_ => unreachable!(),
}
}
};
Box::pin(fut)
}
}
impl Container {
#[allow(clippy::too_many_arguments)]
#[must_use]
fn init_child_with_context(
self,
sync_container: SyncContainer,
context: Context,
registry: RcThreadSafety<Registry>,
sync_registry: RcThreadSafety<SyncRegistry>,
scope_data: ScopeData,
child_scopes_data: Vec<ScopeData>,
close_parent: bool,
) -> Self {
let mut cache = self.inner.cache.write().child();
cache.extend_context(&context);
let mut sync_cache = self.sync.inner.cache.write().child();
let sync_context = context.clone();
sync_cache.extend_context(&sync_context);
Self {
inner: RcThreadSafety::new(ContainerInner {
cache: RwLock::new(cache),
context,
registry,
scope_data,
child_scopes_data: child_scopes_data.clone(),
parent: Some(self.inner),
close_parent,
}),
sync: SyncContainer {
#[cfg(feature = "thread_safe")]
per_type_locks: self.sync.per_type_locks.clone(),
inner: RcThreadSafety::new(SyncContainerInner {
cache: RwLock::new(sync_cache),
context: sync_context,
registry: sync_registry,
scope_data,
child_scopes_data,
parent: Some(sync_container),
close_parent,
}),
},
per_type_locks: self.per_type_locks.clone(),
}
}
#[must_use]
fn init_child(
self,
sync_container: SyncContainer,
registry: RcThreadSafety<Registry>,
sync_registry: RcThreadSafety<SyncRegistry>,
scope_data: ScopeData,
child_scopes_data: Vec<ScopeData>,
close_parent: bool,
) -> Self {
let mut cache = self.inner.cache.write().child();
let context = self.inner.context.clone();
cache.extend_context(&context);
let mut sync_cache = self.sync.inner.cache.write().child();
let sync_context = self.sync.inner.context.clone();
sync_cache.extend_context(&sync_context);
Self {
inner: RcThreadSafety::new(ContainerInner {
cache: RwLock::new(cache),
context,
registry,
scope_data,
child_scopes_data: child_scopes_data.clone(),
parent: Some(self.inner),
close_parent,
}),
sync: SyncContainer {
#[cfg(feature = "thread_safe")]
per_type_locks: self.sync.per_type_locks.clone(),
inner: RcThreadSafety::new(SyncContainerInner {
cache: RwLock::new(sync_cache),
context: sync_context,
registry: sync_registry,
scope_data,
child_scopes_data,
parent: Some(sync_container),
close_parent,
}),
},
per_type_locks: self.per_type_locks.clone(),
}
}
}
pub struct ChildContainerBuilder {
container: Container,
}
impl ChildContainerBuilder {
#[inline]
#[must_use]
pub fn with_scope<S: Scope>(self, scope: S) -> ChildContainerWithScope<S> {
ChildContainerWithScope {
container: self.container,
scope,
}
}
#[inline]
#[must_use]
pub fn with_context(self, context: Context) -> ChildContainerWithContext {
ChildContainerWithContext {
container: self.container,
context,
}
}
pub fn build(self) -> Result<Container, ScopeErrorKind> {
use ScopeErrorKind::{NoChildRegistries, NoNonSkippedRegistries};
let scope_with_child_scopes = self.container.inner.get_scope_with_child_scopes().child();
let registry = self.container.inner.registry.clone();
let sync_registry = self.container.sync.inner.registry.clone();
let sync_container = self.container.sync.clone();
let mut child = self.container.init_child(
sync_container,
registry,
sync_registry,
scope_with_child_scopes.scope_data.ok_or(NoChildRegistries)?,
scope_with_child_scopes.child_scopes_data,
false,
);
while child.inner.scope_data.is_skipped_by_default {
let scope_with_child_scopes = child.inner.get_scope_with_child_scopes().child();
let registry = child.inner.registry.clone();
let sync_registry = child.sync.inner.registry.clone();
let sync_container = child.sync.clone();
child = child.init_child(
sync_container,
registry,
sync_registry,
scope_with_child_scopes.scope_data.ok_or(NoNonSkippedRegistries)?,
scope_with_child_scopes.child_scopes_data,
true,
);
}
Ok(child)
}
}
pub struct ChildContainerWithScope<S> {
container: Container,
scope: S,
}
impl<S> ChildContainerWithScope<S>
where
S: Scope,
{
#[inline]
#[must_use]
pub fn with_context(self, context: Context) -> ChildContainerWithScopeAndContext<S> {
ChildContainerWithScopeAndContext {
container: self.container,
scope: self.scope,
context,
}
}
pub fn build(self) -> Result<Container, ScopeWithErrorKind> {
use ScopeWithErrorKind::{NoChildRegistries, NoChildRegistriesWithScope};
let scope_with_child_scopes = self.container.inner.get_scope_with_child_scopes().child();
let registry = self.container.inner.registry.clone();
let sync_registry = self.container.sync.inner.registry.clone();
let sync_container = self.container.sync.clone();
let priority = self.scope.priority();
let mut child = self.container.init_child(
sync_container,
registry,
sync_registry,
scope_with_child_scopes.scope_data.ok_or(NoChildRegistries)?,
scope_with_child_scopes.child_scopes_data,
false,
);
while child.inner.scope_data.priority != priority {
let scope_with_child_scopes = child.inner.get_scope_with_child_scopes().child();
let registry = child.inner.registry.clone();
let sync_registry = child.sync.inner.registry.clone();
let sync_container = child.sync.clone();
child = child.init_child(
sync_container,
registry,
sync_registry,
scope_with_child_scopes.scope_data.ok_or(NoChildRegistriesWithScope {
name: self.scope.name(),
priority,
})?,
scope_with_child_scopes.child_scopes_data,
true,
);
}
Ok(child)
}
}
pub struct ChildContainerWithContext {
container: Container,
context: Context,
}
impl ChildContainerWithContext {
#[inline]
#[must_use]
pub fn with_scope<S: Scope>(self, scope: S) -> ChildContainerWithScopeAndContext<S> {
ChildContainerWithScopeAndContext {
container: self.container,
scope,
context: self.context,
}
}
pub fn build(self) -> Result<Container, ScopeErrorKind> {
use ScopeErrorKind::{NoChildRegistries, NoNonSkippedRegistries};
let scope_with_child_scopes = self.container.inner.get_scope_with_child_scopes().child();
let context = self.context.clone();
let registry = self.container.inner.registry.clone();
let sync_registry = self.container.sync.inner.registry.clone();
let sync_container = self.container.sync.clone();
let mut child = self.container.init_child_with_context(
sync_container,
context,
registry,
sync_registry,
scope_with_child_scopes.scope_data.ok_or(NoChildRegistries)?,
scope_with_child_scopes.child_scopes_data,
false,
);
while child.inner.scope_data.is_skipped_by_default {
let scope_with_child_scopes = child.inner.get_scope_with_child_scopes().child();
let context = self.context.clone();
let registry = child.inner.registry.clone();
let sync_registry = child.sync.inner.registry.clone();
let sync_container = child.sync.clone();
child = child.init_child_with_context(
sync_container,
context,
registry,
sync_registry,
scope_with_child_scopes.scope_data.ok_or(NoNonSkippedRegistries)?,
scope_with_child_scopes.child_scopes_data,
true,
);
}
Ok(child)
}
}
pub struct ChildContainerWithScopeAndContext<S> {
container: Container,
scope: S,
context: Context,
}
impl<S> ChildContainerWithScopeAndContext<S>
where
S: Scope,
{
pub fn build(self) -> Result<Container, ScopeWithErrorKind> {
use ScopeWithErrorKind::{NoChildRegistries, NoChildRegistriesWithScope};
let scope_with_child_scopes = self.container.inner.get_scope_with_child_scopes().child();
let context = self.context.clone();
let registry = self.container.inner.registry.clone();
let sync_registry = self.container.sync.inner.registry.clone();
let sync_container = self.container.sync.clone();
let priority = self.scope.priority();
let mut child = self.container.init_child_with_context(
sync_container,
context,
registry,
sync_registry,
scope_with_child_scopes.scope_data.ok_or(NoChildRegistries)?,
scope_with_child_scopes.child_scopes_data,
false,
);
while child.inner.scope_data.priority != priority {
let scope_with_child_scopes = child.inner.get_scope_with_child_scopes().child();
let context = self.context.clone();
let registry = child.inner.registry.clone();
let sync_registry = child.sync.inner.registry.clone();
let sync_container = child.sync.clone();
child = child.init_child_with_context(
sync_container,
context,
registry,
sync_registry,
scope_with_child_scopes.scope_data.ok_or(NoChildRegistriesWithScope {
name: self.scope.name(),
priority,
})?,
scope_with_child_scopes.child_scopes_data,
true,
);
}
Ok(child)
}
}
struct BoxedContainerInner {
cache: Cache,
context: Context,
registry: RcThreadSafety<Registry>,
scope_data: ScopeData,
child_scopes_data: Vec<ScopeData>,
parent: Option<Box<BoxedContainerInner>>,
close_parent: bool,
}
impl BoxedContainerInner {
#[must_use]
fn init_child(
self,
registry: RcThreadSafety<Registry>,
scope_data: ScopeData,
child_scopes_data: Vec<ScopeData>,
close_parent: bool,
) -> Self {
let mut cache = self.cache.child();
let context = self.context.clone();
cache.extend_context(&context);
Self {
parent: Some(Box::new(self)),
cache,
context,
registry,
scope_data,
child_scopes_data,
close_parent,
}
}
}
impl From<BoxedContainerInner> for ContainerInner {
fn from(
BoxedContainerInner {
cache,
context,
registry,
scope_data,
child_scopes_data,
parent,
close_parent,
}: BoxedContainerInner,
) -> Self {
Self {
parent: parent.map(|parent| RcThreadSafety::new((*parent).into())),
cache: RwLock::new(cache),
context,
registry,
scope_data,
child_scopes_data,
close_parent,
}
}
}
impl From<(BoxedContainerInner, BoxedSyncContainerInner)> for Container {
fn from((inner, sync): (BoxedContainerInner, BoxedSyncContainerInner)) -> Self {
Self {
inner: RcThreadSafety::new(inner.into()),
sync: sync.into(),
per_type_locks: PerTypeSharedLocks::default(),
}
}
}
struct ContainerInner {
cache: RwLock<Cache>,
context: Context,
registry: RcThreadSafety<Registry>,
scope_data: ScopeData,
child_scopes_data: Vec<ScopeData>,
parent: Option<RcThreadSafety<ContainerInner>>,
close_parent: bool,
}
impl ContainerInner {
#[inline]
pub(crate) fn get_scope_with_child_scopes(&self) -> ScopeDataWithChildScopesData {
ScopeDataWithChildScopesData::new(self.scope_data, self.child_scopes_data.clone())
}
#[allow(clippy::missing_panics_doc)]
fn close(&self) -> impl Future<Output = ()> + SendSafety + '_ {
let mut resolved_set = { self.cache.write().take_resolved_set() };
Box::pin(async move {
while let Some(Resolved { type_info, dependency }) = resolved_set.0.pop_back() {
let InstantiatorData { finalizer, .. } = self
.registry
.get(&type_info)
.expect("Instantiator should be present for resolved type");
if let Some(finalizer) = finalizer {
let _ = finalizer.clone().call(dependency).await;
debug!(%type_info, "Finalizer called");
}
}
#[allow(clippy::assigning_clones)]
{
self.cache.write().map = self.context.map.clone();
}
})
}
}
#[allow(dead_code)]
#[cfg(test)]
mod tests {
extern crate std;
use super::{Container, ContainerInner};
use crate::{
async_registry, registry,
scope::DefaultScope::*,
utils::thread_safety::{RcThreadSafety, SendSafety, SyncSafety},
Inject, InjectTransient, ResolveErrorKind, Scope,
};
use alloc::{
format,
string::{String, ToString as _},
};
use core::sync::atomic::{AtomicU8, Ordering};
use tracing::debug;
use tracing_test::traced_test;
struct Request1;
struct Request2(RcThreadSafety<Request1>);
struct Request3(RcThreadSafety<Request1>, RcThreadSafety<Request2>);
#[tokio::test]
#[traced_test]
async fn test_scoped_get() {
struct A(RcThreadSafety<B>, RcThreadSafety<C>);
struct B(i32);
struct C(RcThreadSafety<CA>);
struct CA(RcThreadSafety<CAA>);
struct CAA(RcThreadSafety<CAAA>);
struct CAAA(RcThreadSafety<CAAAA>);
struct CAAAA(RcThreadSafety<CAAAAA>);
struct CAAAAA;
let app_container = Container::new(async_registry! {
scope(Runtime) [
provide(async || Ok(CAAAAA)),
],
scope(App) [
provide(async |Inject(caaaaa): Inject<CAAAAA>| Ok(CAAAA(caaaaa))),
provide(async || Ok(B(2))),
],
scope(Session) [
provide(async |Inject(caaaa): Inject<CAAAA>| Ok(CAAA(caaaa))),
],
scope(Request) [
provide(async |Inject(caaa): Inject<CAAA>| Ok(CAA(caaa))),
provide(async |Inject(caa): Inject<CAA>| Ok(CA(caa))),
],
scope(Action) [
provide(async |Inject(ca): Inject<CA>| Ok(C(ca))),
],
scope(Step) [
provide(async |Inject(b): Inject<B>, Inject(c): Inject<C>| Ok(A(b, c))),
],
});
let request_container = app_container.clone().enter_build().unwrap();
let action_container = request_container.clone().enter_build().unwrap();
let step_container = action_container.clone().enter_build().unwrap();
let _ = step_container.get::<A>().await.unwrap();
let _ = step_container.get::<CAAAAA>().await.unwrap();
let _ = step_container.get::<CAAAA>().await.unwrap();
let _ = step_container.get::<CAAA>().await.unwrap();
let _ = step_container.get::<CAA>().await.unwrap();
let _ = step_container.get::<CA>().await.unwrap();
let _ = step_container.get::<C>().await.unwrap();
let _ = step_container.get::<B>().await.unwrap();
}
#[tokio::test]
#[traced_test]
async fn test_async_scoped_get() {
struct A(RcThreadSafety<B>, RcThreadSafety<C>);
struct B(i32);
struct C(RcThreadSafety<CA>);
struct CA(RcThreadSafety<CAA>);
struct CAA(RcThreadSafety<CAAA>);
struct CAAA(RcThreadSafety<CAAAA>);
struct CAAAA(RcThreadSafety<CAAAAA>);
struct CAAAAA;
let app_container = Container::new(async_registry! {
scope(Runtime) [
provide(async || Ok(CAAAAA)),
],
scope(App) [
provide(async |Inject(caaaaa): Inject<CAAAAA>| Ok(CAAAA(caaaaa))),
provide(async || Ok(B(2))),
],
scope(Session) [
provide(async |Inject(caaaa): Inject<CAAAA>| Ok(CAAA(caaaa))),
],
scope(Request) [
provide(async |Inject(caaa): Inject<CAAA>| Ok(CAA(caaa))),
provide(async |Inject(caa): Inject<CAA>| Ok(CA(caa))),
],
scope(Action) [
provide(async |Inject(ca): Inject<CA>| Ok(C(ca))),
],
scope(Step) [
provide(async |Inject(b): Inject<B>, Inject(c): Inject<C>| Ok(A(b, c))),
],
});
let request_container = app_container.clone().enter_build().unwrap();
let action_container = request_container.clone().enter_build().unwrap();
let step_container = action_container.clone().enter_build().unwrap();
let _ = step_container.get::<A>().await.unwrap();
let _ = step_container.get::<CAAAAA>().await.unwrap();
let _ = step_container.get::<CAAAA>().await.unwrap();
let _ = step_container.get::<CAAA>().await.unwrap();
let _ = step_container.get::<CAA>().await.unwrap();
let _ = step_container.get::<CA>().await.unwrap();
let _ = step_container.get::<C>().await.unwrap();
let _ = step_container.get::<B>().await.unwrap();
}
struct RequestTransient1;
struct RequestTransient2(RequestTransient1);
struct RequestTransient3(RequestTransient1, RequestTransient2);
#[tokio::test]
#[traced_test]
async fn test_transient_get() {
let app_container = Container::new(async_registry! {
extend(registry! {
scope(App) [
provide(|| Ok(RequestTransient1)),
],
scope(Request) [
provide(|InjectTransient(req): InjectTransient<RequestTransient1>| Ok(RequestTransient2(req))),
provide(|InjectTransient(req_1): InjectTransient<RequestTransient1>, InjectTransient(req_2): InjectTransient<RequestTransient2>| {
Ok(RequestTransient3(req_1, req_2))
}),
]
})
});
let request_container = app_container.clone().enter().with_scope(Request).build().unwrap();
assert!(app_container.get_transient::<RequestTransient1>().await.is_ok());
assert!(matches!(
app_container.get_transient::<RequestTransient2>().await,
Err(ResolveErrorKind::NoAccessible {
expected_scope_data: _,
actual_scope_data: _,
}),
));
assert!(matches!(
app_container.get_transient::<RequestTransient3>().await,
Err(ResolveErrorKind::NoAccessible {
expected_scope_data: _,
actual_scope_data: _,
}),
));
assert!(request_container.get_transient::<RequestTransient1>().await.is_ok());
assert!(request_container.get_transient::<RequestTransient2>().await.is_ok());
assert!(request_container.get_transient::<RequestTransient3>().await.is_ok());
}
#[tokio::test]
#[traced_test]
async fn test_async_transient_get() {
let app_container = Container::new(async_registry! {
scope(App) [
provide(async || Ok(RequestTransient1)),
],
scope(Request) [
provide(async |InjectTransient(req): InjectTransient<RequestTransient1>| Ok(RequestTransient2(req))),
provide(async |InjectTransient(req_1): InjectTransient<RequestTransient1>, InjectTransient(req_2): InjectTransient<RequestTransient2>| {
Ok(RequestTransient3(req_1, req_2))
}),
]
});
let request_container = app_container.clone().enter().with_scope(Request).build().unwrap();
assert!(app_container.get_transient::<RequestTransient1>().await.is_ok());
assert!(matches!(
app_container.get_transient::<RequestTransient2>().await,
Err(ResolveErrorKind::NoAccessible {
expected_scope_data: _,
actual_scope_data: _,
}),
));
assert!(matches!(
app_container.get_transient::<RequestTransient3>().await,
Err(ResolveErrorKind::NoAccessible {
expected_scope_data: _,
actual_scope_data: _,
}),
));
assert!(request_container.get_transient::<RequestTransient1>().await.is_ok());
assert!(request_container.get_transient::<RequestTransient2>().await.is_ok());
assert!(request_container.get_transient::<RequestTransient3>().await.is_ok());
}
#[tokio::test]
#[traced_test]
async fn test_scope_hierarchy() {
let app_container = Container::new(async_registry! {
scope(Runtime) [
provide(async || Ok(())),
],
scope(App) [
provide(async || Ok(((), ()))),
],
scope(Session) [
provide(async || Ok(((), (), ()))),
],
scope(Request) [
provide(async || Ok(((), (), (), ()))),
],
scope(Action) [
provide(async || Ok(((), (), (), (), ()))),
],
scope(Step) [
provide(async || Ok(((), (), (), (), (), ()))),
],
});
let request_container = app_container.clone().enter_build().unwrap();
let action_container = request_container.clone().enter_build().unwrap();
let step_container = action_container.clone().enter_build().unwrap();
let app_container_inner = app_container.inner;
let request_container_inner = request_container.inner;
let action_container_inner = action_container.inner;
let step_container_inner = step_container.inner;
assert_eq!(app_container_inner.parent.as_ref().unwrap().scope_data.priority, Runtime.priority());
assert_eq!(app_container_inner.child_scopes_data.len(), 4);
assert_eq!(app_container_inner.scope_data.priority, App.priority());
assert_eq!(
request_container_inner.parent.as_ref().unwrap().scope_data.priority,
Session.priority()
);
assert_eq!(request_container_inner.child_scopes_data.len(), 2);
assert_eq!(request_container_inner.scope_data.priority, Request.priority());
assert_eq!(
request_container_inner.scope_data.priority,
app_container_inner.child_scopes_data[1].priority
);
assert_eq!(
action_container_inner.scope_data.priority,
request_container_inner.child_scopes_data[0].priority
);
assert_eq!(action_container_inner.child_scopes_data.len(), 1);
assert_eq!(action_container_inner.scope_data.priority, Action.priority());
assert_eq!(step_container_inner.child_scopes_data.len(), 0);
assert_eq!(step_container_inner.scope_data.priority, Step.priority());
assert_eq!(
step_container_inner.scope_data.priority,
action_container_inner.child_scopes_data[0].priority
);
}
#[tokio::test]
#[traced_test]
async fn test_scope_with_hierarchy() {
let runtime_container = Container::new_with_start_scope(
async_registry! {
scope(Runtime) [
provide(async || Ok(())),
],
scope(App) [
provide(async || Ok(((), ()))),
],
scope(Session) [
provide(async || Ok(((), (), ()))),
],
scope(Request) [
provide(async || Ok(((), (), (), ()))),
],
scope(Action) [
provide(async || Ok(((), (), (), (), ()))),
],
scope(Step) [
provide(async || Ok(((), (), (), (), (), ()))),
],
},
Runtime,
);
let app_container = runtime_container.clone().enter().with_scope(App).build().unwrap();
let session_container = runtime_container.clone().enter().with_scope(Session).build().unwrap();
let request_container = app_container.clone().enter().with_scope(Request).build().unwrap();
let action_container = request_container.clone().enter().with_scope(Action).build().unwrap();
let step_container = action_container.clone().enter().with_scope(Step).build().unwrap();
let runtime_container_inner = runtime_container.inner;
let app_container_inner = app_container.inner;
let session_container_inner = session_container.inner;
let request_container_inner = request_container.inner;
let action_container_inner = action_container.inner;
let step_container_inner = step_container.inner;
assert!(runtime_container_inner.parent.is_none());
assert_eq!(runtime_container_inner.child_scopes_data.len(), 5);
assert_eq!(runtime_container_inner.scope_data.priority, Runtime.priority());
assert_eq!(
app_container_inner.scope_data.priority,
runtime_container_inner.child_scopes_data[0].priority
);
assert_eq!(app_container_inner.child_scopes_data.len(), 4);
assert_eq!(app_container_inner.scope_data.priority, App.priority());
assert_eq!(
session_container_inner.scope_data.priority,
app_container_inner.child_scopes_data[0].priority
);
assert_eq!(session_container_inner.child_scopes_data.len(), 3);
assert_eq!(session_container_inner.scope_data.priority, Session.priority());
assert_eq!(
request_container_inner.scope_data.priority,
session_container_inner.child_scopes_data[0].priority
);
assert_eq!(request_container_inner.child_scopes_data.len(), 2);
assert_eq!(request_container_inner.scope_data.priority, Request.priority());
assert_eq!(
action_container_inner.scope_data.priority,
request_container_inner.child_scopes_data[0].priority
);
assert_eq!(action_container_inner.child_scopes_data.len(), 1);
assert_eq!(action_container_inner.scope_data.priority, Action.priority());
assert_eq!(
step_container_inner.scope_data.priority,
action_container_inner.child_scopes_data[0].priority
);
assert_eq!(step_container_inner.child_scopes_data.len(), 0);
assert_eq!(step_container_inner.scope_data.priority, Step.priority());
}
#[tokio::test]
#[traced_test]
async fn test_close_for_unresolved() {
let finalizer_1_request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_2_request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_3_request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_4_request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let app_container = Container::new(async_registry! {
scope(Session) [
provide(
async || Ok(((), (), ())),
finalizer = {
let finalizer_3_request_call_count = finalizer_3_request_call_count.clone();
move |_: RcThreadSafety<((), (), ())>| {
let finalizer_3_request_call_count = finalizer_3_request_call_count.clone();
async move {
finalizer_3_request_call_count.fetch_add(1, Ordering::SeqCst);
}
}
}
),
],
extend(registry! {
scope(Runtime) [
provide(
|| Ok(()),
finalizer = {
let finalizer_1_request_call_count = finalizer_1_request_call_count.clone();
move |_: RcThreadSafety<()>| {
finalizer_1_request_call_count.fetch_add(1, Ordering::SeqCst);
}
}
),
],
scope(App) [
provide(
|| Ok(((), ())),
finalizer = {
let finalizer_2_request_call_count = finalizer_2_request_call_count.clone();
move |_: RcThreadSafety<((), ())>| {
finalizer_2_request_call_count.fetch_add(1, Ordering::SeqCst);
}
}
),
],
scope(Request) [
provide(
|| Ok(((), (), (), ())),
finalizer = {
let finalizer_4_request_call_count = finalizer_4_request_call_count.clone();
move |_: RcThreadSafety<((), (), (), ())>| {
finalizer_4_request_call_count.fetch_add(1, Ordering::SeqCst);
}
}
),
],
}),
});
let session_container = app_container.clone().enter().with_scope(Session).build().unwrap();
let request_container = app_container.clone().enter().with_scope(Request).build().unwrap();
session_container.close().await;
request_container.close().await;
app_container.close().await;
assert_eq!(finalizer_1_request_call_count.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_2_request_call_count.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_3_request_call_count.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_4_request_call_count.load(Ordering::SeqCst), 0);
}
#[tokio::test]
#[traced_test]
async fn test_close_for_resolved() {
let request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_1_request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_1_request_call_position = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_2_request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_2_request_call_position = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_3_request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_3_request_call_position = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_4_request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_4_request_call_position = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_5_request_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_5_request_call_position = RcThreadSafety::new(AtomicU8::new(0));
let app_container = Container::new(async_registry! {
scope(App) [
provide(
async || Ok(((), (), ())),
finalizer = {
let request_call_count = request_call_count.clone();
let finalizer_3_request_call_position = finalizer_3_request_call_position.clone();
let finalizer_3_request_call_count = finalizer_3_request_call_count.clone();
move |_: RcThreadSafety<((), (), ())>| {
let request_call_count = request_call_count.clone();
let finalizer_3_request_call_position = finalizer_3_request_call_position.clone();
let finalizer_3_request_call_count = finalizer_3_request_call_count.clone();
async move {
request_call_count.fetch_add(1, Ordering::SeqCst);
finalizer_3_request_call_position.store(request_call_count.load(Ordering::SeqCst), Ordering::SeqCst);
finalizer_3_request_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Finalizer 3 called");
}
}
}
),
],
extend(registry! {
scope(Runtime) [
provide(
|| Ok(()),
finalizer = {
let request_call_count = request_call_count.clone();
let finalizer_1_request_call_position = finalizer_1_request_call_position.clone();
let finalizer_1_request_call_count = finalizer_1_request_call_count.clone();
move |_: RcThreadSafety<()>| {
request_call_count.fetch_add(1, Ordering::SeqCst);
finalizer_1_request_call_position.store(request_call_count.load(Ordering::SeqCst), Ordering::SeqCst);
finalizer_1_request_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Finalizer 1 called");
}
}
),
],
scope(App) [
provide(
|| Ok(((), ())),
finalizer = {
let request_call_count = request_call_count.clone();
let finalizer_2_request_call_position = finalizer_2_request_call_position.clone();
let finalizer_2_request_call_count = finalizer_2_request_call_count.clone();
move |_: RcThreadSafety<((), ())>| {
request_call_count.fetch_add(1, Ordering::SeqCst);
finalizer_2_request_call_position.store(request_call_count.load(Ordering::SeqCst), Ordering::SeqCst);
finalizer_2_request_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Finalizer 2 called");
}
}
),
],
scope(Request) [
provide(
|| Ok(((), (), (), ())),
finalizer = {
let request_call_count = request_call_count.clone();
let finalizer_4_request_call_position = finalizer_4_request_call_position.clone();
let finalizer_4_request_call_count = finalizer_4_request_call_count.clone();
move |_: RcThreadSafety<((), (), (), ())>| {
request_call_count.fetch_add(1, Ordering::SeqCst);
finalizer_4_request_call_position.store(request_call_count.load(Ordering::SeqCst), Ordering::SeqCst);
finalizer_4_request_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Finalizer 4 called");
}
}
),
provide(
|| Ok(((), (), (), (), ())),
finalizer = {
let request_call_count = request_call_count.clone();
let finalizer_5_request_call_position = finalizer_5_request_call_position.clone();
let finalizer_5_request_call_count = finalizer_5_request_call_count.clone();
move |_: RcThreadSafety<((), (), (), (), ())>| {
request_call_count.fetch_add(1, Ordering::SeqCst);
finalizer_5_request_call_position.store(request_call_count.load(Ordering::SeqCst), Ordering::SeqCst);
finalizer_5_request_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Finalizer 5 called");
}
}
),
],
})
});
let request_container = app_container.clone().enter().with_scope(Request).build().unwrap();
let _ = request_container.get::<()>().await.unwrap();
let _ = request_container.get::<((), ())>().await.unwrap();
let _ = request_container.get::<((), (), ())>().await.unwrap();
let _ = request_container.get::<((), (), (), (), ())>().await.unwrap();
let _ = request_container.get::<((), (), (), ())>().await.unwrap();
let runtime_container_resolved_set_count = app_container
.sync
.inner
.parent
.as_ref()
.unwrap()
.inner
.cache
.read()
.resolved
.0
.len();
let app_container_resolved_set_count =
app_container.sync.inner.cache.read().resolved.0.len() + app_container.inner.cache.read().resolved.0.len();
let request_container_resolved_set_count = request_container.sync.inner.cache.read().resolved.0.len();
request_container.close().await;
assert_eq!(runtime_container_resolved_set_count, 1);
assert_eq!(app_container_resolved_set_count, 2);
assert_eq!(request_container_resolved_set_count, 2);
assert_eq!(finalizer_1_request_call_count.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_1_request_call_position.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_2_request_call_count.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_2_request_call_position.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_3_request_call_count.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_3_request_call_position.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_4_request_call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_4_request_call_position.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_5_request_call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_5_request_call_position.load(Ordering::SeqCst), 2);
app_container.close().await;
assert_eq!(finalizer_1_request_call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_1_request_call_position.load(Ordering::SeqCst), 5);
assert_eq!(finalizer_2_request_call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_2_request_call_position.load(Ordering::SeqCst), 4);
assert_eq!(finalizer_3_request_call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_3_request_call_position.load(Ordering::SeqCst), 3);
assert_eq!(finalizer_4_request_call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_4_request_call_position.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_5_request_call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_5_request_call_position.load(Ordering::SeqCst), 2);
}
#[tokio::test]
#[traced_test]
async fn test_close_on_drop() {
let call_count = RcThreadSafety::new(AtomicU8::new(0));
let drop_call_count = RcThreadSafety::new(AtomicU8::new(0));
let drop_call_position = RcThreadSafety::new(AtomicU8::new(0));
let instantiator_call_count = RcThreadSafety::new(AtomicU8::new(0));
let instantiator_call_position = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_1_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_1_call_position = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_2_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_2_call_position = RcThreadSafety::new(AtomicU8::new(0));
struct Type1;
struct Type2(RcThreadSafety<Type1>);
struct DropWrapper<T> {
val: T,
call_count: RcThreadSafety<AtomicU8>,
drop_call_count: RcThreadSafety<AtomicU8>,
drop_call_position: RcThreadSafety<AtomicU8>,
}
impl<T> Drop for DropWrapper<T> {
fn drop(&mut self) {
self.call_count.fetch_add(1, Ordering::SeqCst);
self.drop_call_count.fetch_add(1, Ordering::SeqCst);
self.drop_call_position
.store(self.call_count.load(Ordering::SeqCst), Ordering::SeqCst);
debug!("Drop called");
}
}
let app_container = Container::new(async_registry! {
extend(registry! {
scope(App) [
provide(
|| Ok(Type1),
finalizer = {
let call_count = call_count.clone();
let finalizer_1_call_count = finalizer_1_call_count.clone();
let finalizer_1_call_position = finalizer_1_call_position.clone();
move |_: RcThreadSafety<Type1>| {
call_count.fetch_add(1, Ordering::SeqCst);
finalizer_1_call_position.store(call_count.load(Ordering::SeqCst), Ordering::SeqCst);
finalizer_1_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Finalizer 1 called");
}
}
),
provide(
|Inject(type_1): Inject<Type1>| Ok(Type2(type_1)),
finalizer = {
let call_count = call_count.clone();
let finalizer_2_call_count = finalizer_2_call_count.clone();
let finalizer_2_call_position = finalizer_2_call_position.clone();
move |_: RcThreadSafety<Type2>| {
call_count.fetch_add(1, Ordering::SeqCst);
finalizer_2_call_position.store(call_count.load(Ordering::SeqCst), Ordering::SeqCst);
finalizer_2_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Finalizer 2 called");
}
}
),
]
}),
});
let request_container = app_container.enter_build().unwrap();
DropWrapper {
val: request_container,
call_count: call_count.clone(),
drop_call_count: drop_call_count.clone(),
drop_call_position: drop_call_position.clone(),
}
.val
.get::<Type2>()
.await
.unwrap();
instantiator_call_position.store(call_count.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
instantiator_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Instantiator called");
assert_eq!(call_count.load(Ordering::SeqCst), 3);
assert_eq!(finalizer_1_call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_1_call_position.load(Ordering::SeqCst), 3);
assert_eq!(finalizer_2_call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_2_call_position.load(Ordering::SeqCst), 2);
assert_eq!(instantiator_call_count.load(Ordering::SeqCst), 1);
assert_eq!(instantiator_call_position.load(Ordering::SeqCst), 4);
assert_eq!(drop_call_count.load(Ordering::SeqCst), 1);
assert_eq!(drop_call_position.load(Ordering::SeqCst), 1);
}
#[tokio::test]
#[traced_test]
async fn test_async_close_on_drop() {
let call_count = RcThreadSafety::new(AtomicU8::new(0));
let drop_call_count = RcThreadSafety::new(AtomicU8::new(0));
let drop_call_position = RcThreadSafety::new(AtomicU8::new(0));
let instantiator_call_count = RcThreadSafety::new(AtomicU8::new(0));
let instantiator_call_position = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_1_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_1_call_position = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_2_call_count = RcThreadSafety::new(AtomicU8::new(0));
let finalizer_2_call_position = RcThreadSafety::new(AtomicU8::new(0));
struct Type1;
struct Type2(RcThreadSafety<Type1>);
struct DropWrapper<T> {
val: T,
call_count: RcThreadSafety<AtomicU8>,
drop_call_count: RcThreadSafety<AtomicU8>,
drop_call_position: RcThreadSafety<AtomicU8>,
}
impl<T> Drop for DropWrapper<T> {
fn drop(&mut self) {
self.call_count.fetch_add(1, Ordering::SeqCst);
self.drop_call_count.fetch_add(1, Ordering::SeqCst);
self.drop_call_position
.store(self.call_count.load(Ordering::SeqCst), Ordering::SeqCst);
debug!("Drop called");
}
}
let app_container = Container::new(async_registry! {
scope(App) [
provide(
async || Ok(Type1),
finalizer = {
let call_count = call_count.clone();
let finalizer_1_call_count = finalizer_1_call_count.clone();
let finalizer_1_call_position = finalizer_1_call_position.clone();
move |_: RcThreadSafety<Type1>| {
let call_count = call_count.clone();
let finalizer_1_call_count = finalizer_1_call_count.clone();
let finalizer_1_call_position = finalizer_1_call_position.clone();
async move {
call_count.fetch_add(1, Ordering::SeqCst);
finalizer_1_call_position.store(call_count.load(Ordering::SeqCst), Ordering::SeqCst);
finalizer_1_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Finalizer 1 called");
}
}
}
),
provide(
async |Inject(type_1): Inject<Type1>| Ok(Type2(type_1)),
finalizer = {
let call_count = call_count.clone();
let finalizer_2_call_count = finalizer_2_call_count.clone();
let finalizer_2_call_position = finalizer_2_call_position.clone();
move |_: RcThreadSafety<Type2>| {
let call_count = call_count.clone();
let finalizer_2_call_count = finalizer_2_call_count.clone();
let finalizer_2_call_position = finalizer_2_call_position.clone();
async move {
call_count.fetch_add(1, Ordering::SeqCst);
finalizer_2_call_position.store(call_count.load(Ordering::SeqCst), Ordering::SeqCst);
finalizer_2_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Finalizer 2 called");
}
}
}
),
]
});
let request_container = app_container.enter_build().unwrap();
DropWrapper {
val: request_container,
call_count: call_count.clone(),
drop_call_count: drop_call_count.clone(),
drop_call_position: drop_call_position.clone(),
}
.val
.get::<Type2>()
.await
.unwrap();
instantiator_call_position.store(call_count.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
instantiator_call_count.fetch_add(1, Ordering::SeqCst);
debug!("Instantiator called");
assert_eq!(call_count.load(Ordering::SeqCst), 1);
assert_eq!(finalizer_1_call_count.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_1_call_position.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_2_call_count.load(Ordering::SeqCst), 0);
assert_eq!(finalizer_2_call_position.load(Ordering::SeqCst), 0);
assert_eq!(instantiator_call_count.load(Ordering::SeqCst), 1);
assert_eq!(instantiator_call_position.load(Ordering::SeqCst), 2);
assert_eq!(drop_call_count.load(Ordering::SeqCst), 1);
assert_eq!(drop_call_position.load(Ordering::SeqCst), 1);
}
#[tokio::test]
#[traced_test]
async fn test_thread_safe() {
struct Request1 {
#[cfg(not(feature = "thread_safe"))]
_phantom: core::marker::PhantomData<*const ()>,
}
fn impl_bounds<T: SendSafety + SyncSafety + 'static>() {}
impl_bounds::<(Container, ContainerInner)>();
#[allow(unused_variables)]
let app_container = Container::new(async_registry! {
scope(App) [
provide(async || Ok(RequestTransient1)),
],
});
#[cfg(feature = "thread_safe")]
tokio::spawn(async move {
let request1 = app_container.get_transient::<RequestTransient1>().await;
let request2 = app_container.get::<Request1>().await;
assert!(request1.is_ok());
assert!(request2.is_ok());
});
}
}