use crate::container::{ConstructFn, Container, Erased};
use crate::graph::Graph;
use crate::key::Key;
use crate::{
BoxError, BuildError, BuildIssue, CastFrom, Dependency, Lifetime, Resolver, Service,
ServiceProvider,
};
use std::any::{Any, type_name};
use std::fmt;
use std::sync::Arc;
#[derive(Debug, Default)]
pub struct ServiceCollection {
registrations: Vec<Registration>,
validation: Validation,
}
#[derive(Debug, Default)]
struct Validation {
dependencies: bool,
scopes: bool,
}
pub(crate) struct Registration {
pub(crate) key: Key,
pub(crate) implementation: &'static str,
pub(crate) lifetime: Lifetime,
pub(crate) source: Source,
pub(crate) dependencies: Option<fn() -> Vec<Dependency>>,
pub(crate) cast: Box<dyn Any + Send + Sync>,
pub(crate) slot: usize,
}
impl fmt::Debug for Registration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Registration")
.field("service", &self.key.name)
.field("implementation", &self.implementation)
.field("lifetime", &self.lifetime)
.finish_non_exhaustive()
}
}
pub(crate) enum Source {
Construct(ConstructFn),
Instance(Erased),
}
impl ServiceCollection {
pub fn new() -> Self {
Self::default()
}
pub fn add_singleton<T: Service>(&mut self) -> &mut Self {
self.add::<T>(Lifetime::Singleton)
}
pub fn add_scoped<T: Service>(&mut self) -> &mut Self {
self.add::<T>(Lifetime::Scoped)
}
pub fn add_transient<T: Service>(&mut self) -> &mut Self {
self.add::<T>(Lifetime::Transient)
}
pub fn add_singleton_as<I, T>(&mut self) -> &mut Self
where
I: ?Sized + CastFrom<T> + Send + Sync + 'static,
T: Service,
{
self.add_service::<I, T>(Lifetime::Singleton, trait_cast::<I, T>())
}
pub fn add_scoped_as<I, T>(&mut self) -> &mut Self
where
I: ?Sized + CastFrom<T> + Send + Sync + 'static,
T: Service,
{
self.add_service::<I, T>(Lifetime::Scoped, trait_cast::<I, T>())
}
pub fn add_transient_as<I, T>(&mut self) -> &mut Self
where
I: ?Sized + CastFrom<T> + Send + Sync + 'static,
T: Service,
{
self.add_service::<I, T>(Lifetime::Transient, trait_cast::<I, T>())
}
pub fn contains<T: ?Sized + 'static>(&self) -> bool {
let key = Key::of::<T>();
self.registrations
.iter()
.any(|registration| registration.key == key)
}
pub fn try_add_singleton<T: Service>(&mut self) -> &mut Self {
if self.contains::<T>() {
return self;
}
self.add_singleton::<T>()
}
pub fn try_add_scoped<T: Service>(&mut self) -> &mut Self {
if self.contains::<T>() {
return self;
}
self.add_scoped::<T>()
}
pub fn try_add_transient<T: Service>(&mut self) -> &mut Self {
if self.contains::<T>() {
return self;
}
self.add_transient::<T>()
}
pub fn try_add_singleton_as<I, T>(&mut self) -> &mut Self
where
I: ?Sized + CastFrom<T> + Send + Sync + 'static,
T: Service,
{
if self.contains::<I>() {
return self;
}
self.add_singleton_as::<I, T>()
}
pub fn try_add_scoped_as<I, T>(&mut self) -> &mut Self
where
I: ?Sized + CastFrom<T> + Send + Sync + 'static,
T: Service,
{
if self.contains::<I>() {
return self;
}
self.add_scoped_as::<I, T>()
}
pub fn try_add_transient_as<I, T>(&mut self) -> &mut Self
where
I: ?Sized + CastFrom<T> + Send + Sync + 'static,
T: Service,
{
if self.contains::<I>() {
return self;
}
self.add_transient_as::<I, T>()
}
pub fn add_singleton_with<T, F>(&mut self, factory: F) -> &mut Self
where
T: Send + Sync + 'static,
F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
{
self.add_factory(Lifetime::Singleton, factory)
}
pub fn add_scoped_with<T, F>(&mut self, factory: F) -> &mut Self
where
T: Send + Sync + 'static,
F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
{
self.add_factory(Lifetime::Scoped, factory)
}
pub fn add_transient_with<T, F>(&mut self, factory: F) -> &mut Self
where
T: Send + Sync + 'static,
F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
{
self.add_factory(Lifetime::Transient, factory)
}
pub fn add_singleton_instance<T: Send + Sync + 'static>(&mut self, instance: T) -> &mut Self {
self.push_unchecked::<T>(Lifetime::Singleton, Source::Instance(Arc::new(instance)))
}
pub fn add_singleton_as_with<I, F>(&mut self, factory: F) -> &mut Self
where
I: ?Sized + Send + Sync + 'static,
F: Fn(&Resolver<'_>) -> Result<Arc<I>, BoxError> + Send + Sync + 'static,
{
self.add_factory_as(Lifetime::Singleton, factory)
}
pub fn add_scoped_as_with<I, F>(&mut self, factory: F) -> &mut Self
where
I: ?Sized + Send + Sync + 'static,
F: Fn(&Resolver<'_>) -> Result<Arc<I>, BoxError> + Send + Sync + 'static,
{
self.add_factory_as(Lifetime::Scoped, factory)
}
pub fn add_transient_as_with<I, F>(&mut self, factory: F) -> &mut Self
where
I: ?Sized + Send + Sync + 'static,
F: Fn(&Resolver<'_>) -> Result<Arc<I>, BoxError> + Send + Sync + 'static,
{
self.add_factory_as(Lifetime::Transient, factory)
}
pub fn add_singleton_instance_as<I>(&mut self, instance: Arc<I>) -> &mut Self
where
I: ?Sized + Send + Sync + 'static,
{
self.push_trait_object::<I>(Lifetime::Singleton, Source::Instance(Arc::new(instance)))
}
pub fn validate_dependencies(&mut self) -> &mut Self {
self.validation.dependencies = true;
self
}
pub fn validate_scopes(&mut self) -> &mut Self {
self.validation.scopes = true;
self
}
pub fn build(self) -> Result<ServiceProvider, BuildError> {
let graph = Graph::new(&self.registrations);
let mut issues: Vec<BuildIssue> = graph
.cycles()
.into_iter()
.map(|path| BuildIssue::Cycle { path })
.collect();
if self.validation.dependencies {
issues.extend(
graph
.missing()
.into_iter()
.map(|path| BuildIssue::MissingDependency { path }),
);
}
if self.validation.scopes {
issues.extend(
graph
.captive()
.into_iter()
.map(|path| BuildIssue::CaptiveDependency { path }),
);
}
if !issues.is_empty() {
return Err(BuildError::new(issues));
}
Ok(ServiceProvider::new(Container::new(self.registrations)))
}
fn add<T: Service>(&mut self, lifetime: Lifetime) -> &mut Self {
self.add_service::<T, T>(lifetime, concrete_cast::<T>())
}
fn add_service<I: ?Sized + 'static, T: Service>(
&mut self,
lifetime: Lifetime,
cast: Cast,
) -> &mut Self {
let construct: ConstructFn =
Arc::new(|resolver| T::construct(resolver).map(|service| Arc::new(service) as Erased));
self.push(Registration {
key: Key::of::<I>(),
implementation: type_name::<T>(),
lifetime,
source: Source::Construct(construct),
dependencies: Some(T::dependencies),
cast,
slot: 0,
})
}
fn add_factory<T, F>(&mut self, lifetime: Lifetime, factory: F) -> &mut Self
where
T: Send + Sync + 'static,
F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
{
self.push_unchecked::<T>(lifetime, Source::Construct(factory_construct(factory)))
}
fn push_unchecked<T: Send + Sync + 'static>(
&mut self,
lifetime: Lifetime,
source: Source,
) -> &mut Self {
let key = Key::of::<T>();
self.push(Registration {
key,
implementation: key.name,
lifetime,
source,
dependencies: None,
cast: concrete_cast::<T>(),
slot: 0,
})
}
fn add_factory_as<I, F>(&mut self, lifetime: Lifetime, factory: F) -> &mut Self
where
I: ?Sized + Send + Sync + 'static,
F: Fn(&Resolver<'_>) -> Result<Arc<I>, BoxError> + Send + Sync + 'static,
{
self.push_trait_object::<I>(lifetime, Source::Construct(factory_construct(factory)))
}
fn push_trait_object<I>(&mut self, lifetime: Lifetime, source: Source) -> &mut Self
where
I: ?Sized + Send + Sync + 'static,
{
let key = Key::of::<I>();
let cast: fn(Erased) -> Option<Arc<I>> = |instance| {
instance
.downcast::<Arc<I>>()
.ok()
.map(|arc| Arc::clone(arc.as_ref()))
};
self.push(Registration {
key,
implementation: key.name,
lifetime,
source,
dependencies: None,
cast: Box::new(cast),
slot: 0,
})
}
fn push(&mut self, registration: Registration) -> &mut Self {
self.registrations.push(registration);
self
}
}
fn factory_construct<T, F>(factory: F) -> ConstructFn
where
T: Send + Sync + 'static,
F: Fn(&Resolver<'_>) -> Result<T, BoxError> + Send + Sync + 'static,
{
Arc::new(move |resolver| match factory(resolver) {
Ok(service) => Ok(Arc::new(service) as Erased),
Err(error) => Err(resolver.failure(error)),
})
}
type Cast = Box<dyn Any + Send + Sync>;
fn trait_cast<I, T>() -> Cast
where
I: ?Sized + CastFrom<T> + 'static,
T: Send + Sync + 'static,
{
let cast: fn(Erased) -> Option<Arc<I>> = |instance| instance.downcast::<T>().ok().map(I::cast);
Box::new(cast)
}
fn concrete_cast<T: Send + Sync + 'static>() -> Cast {
let cast: fn(Erased) -> Option<Arc<T>> = |instance| instance.downcast().ok();
Box::new(cast)
}