use std::any::{TypeId, type_name};
use cfg_block::cfg_block;
use crate::provider::Provider;
use crate::types::{AnyDyn, ErasedProvider, SyncBounds};
cfg_block! {
#[cfg(feature = "validation")] {
pub struct DeclaredDependency {
pub type_id: fn() -> TypeId,
pub type_name: &'static str,
}
inventory::collect!(DeclaredDependency);
}
}
struct RegistrationEntry {
type_id: TypeId,
provider: Box<ErasedProvider>,
}
#[inline]
fn registrations_contain_type_id<'a>(
registrations: impl IntoIterator<Item = &'a RegistrationEntry>,
type_id: TypeId,
) -> bool {
registrations
.into_iter()
.any(|entry| entry.type_id == type_id)
}
pub struct Container {
registrations: Box<[RegistrationEntry]>,
}
cfg_block! {
#[cfg(feature = "debug")] {
use std::fmt;
impl fmt::Debug for Container {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Container")
.field("providers", &self.provider_count())
.finish()
}
}
impl fmt::Display for Container {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Container ({} providers)",
self.provider_count()
)
}
}
}
}
pub struct ContainerBuilder {
registrations: Vec<RegistrationEntry>,
}
impl Default for ContainerBuilder {
fn default() -> Self {
Self::new()
}
}
impl ContainerBuilder {
#[must_use]
pub fn new() -> Self {
Self::with_capacity(0)
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
registrations: Vec::with_capacity(capacity),
}
}
#[inline]
fn contains_type_id(&self, type_id: TypeId) -> bool {
registrations_contain_type_id(self.registrations.iter(), type_id)
}
#[must_use]
pub fn contains<T: 'static>(&self) -> bool {
self.contains_type_id(TypeId::of::<T>())
}
#[must_use]
pub fn contains_provider<P>(&self) -> bool
where
P: Provider + SyncBounds,
{
self.contains::<P::Output>()
}
#[must_use]
pub fn provider<P>(self, provider: P) -> Self
where
P: Provider + SyncBounds,
{
let type_id = TypeId::of::<P::Output>();
assert!(
!self.contains_type_id(type_id),
"provider already registered for `{}`",
type_name::<P::Output>()
);
let provider =
move |container: &Container| -> Box<AnyDyn> { Box::new(provider.provide(container)) };
let mut registrations = self.registrations;
registrations.push(RegistrationEntry {
type_id,
provider: Box::new(provider),
});
Self { registrations }
}
#[must_use]
pub fn build(self) -> Container {
Container {
registrations: self.registrations.into_boxed_slice(),
}
}
}
impl Container {
#[inline]
fn provider_for(&self, type_id: TypeId) -> Option<&ErasedProvider> {
self.registrations
.iter()
.rev()
.find(|entry| entry.type_id == type_id)
.map(|entry| entry.provider.as_ref())
}
#[inline]
fn contains_type_id(&self, type_id: TypeId) -> bool {
registrations_contain_type_id(self.registrations.iter(), type_id)
}
#[inline]
fn cast_owned_unchecked<T: SyncBounds>(owned_erased: Box<AnyDyn>) -> T {
debug_assert!((*owned_erased).is::<T>());
let ptr = Box::into_raw(owned_erased).cast::<T>();
unsafe {
*Box::from_raw(ptr)
}
}
#[must_use]
pub fn builder() -> ContainerBuilder {
ContainerBuilder::new()
}
#[must_use]
pub fn builder_with_capacity(capacity: usize) -> ContainerBuilder {
ContainerBuilder::with_capacity(capacity)
}
#[must_use]
pub fn get<T: SyncBounds>(&self) -> T {
self.try_get().expect("dependency not registered")
}
#[must_use]
pub fn try_get<T: SyncBounds>(&self) -> Option<T> {
let boxed = (self.provider_for(TypeId::of::<T>())?)(self);
Some(Self::cast_owned_unchecked::<T>(boxed))
}
#[must_use]
pub fn contains<T: 'static>(&self) -> bool {
self.contains_type_id(TypeId::of::<T>())
}
#[cfg(feature = "validation")]
pub fn validate(&self) {
let mut missing: Vec<&'static str> = Vec::new();
for dep in inventory::iter::<DeclaredDependency> {
let type_id = (dep.type_id)();
let registered = self.contains_type_id(type_id);
if !registered {
missing.push(dep.type_name);
}
}
if !missing.is_empty() {
panic!(
"Container is missing {} declared dependenc{}: [{}]",
missing.len(),
if missing.len() == 1 { "y" } else { "ies" },
missing.join(", ")
);
}
}
#[must_use]
pub fn provider_count(&self) -> usize {
self.registrations.len()
}
}