use std::any::{Any, TypeId};
use std::sync::{Arc, Mutex};
use crate::injection_context::InjectionContext;
use crate::*;
pub trait Builder: Send + Sync {
fn instance_type(&self) -> TypeInfo;
fn scope_type(&self) -> TypeInfo;
fn interfaces(&self, clb: &mut dyn FnMut(&TypeInfo) -> bool);
fn dependencies(&self, clb: &mut dyn FnMut(&DependencyInfo) -> bool);
fn metadata<'a>(&'a self, clb: &mut dyn FnMut(&'a dyn std::any::Any) -> bool);
fn get_any(
&self,
cat: &Catalog,
ctx: &InjectionContext,
) -> Result<Arc<dyn Any + Send + Sync>, InjectionError>;
}
pub trait BuilderExt {
fn interfaces_get_all(&self) -> Vec<TypeInfo>;
fn interfaces_contain<Iface: 'static>(&self) -> bool;
fn interfaces_contain_type_id(&self, type_id: &TypeId) -> bool;
fn dependencies_get_all(&self) -> Vec<DependencyInfo>;
fn metadata_get_first<Meta: 'static>(&self) -> Option<&Meta>;
fn metadata_find_first<Meta: 'static>(&self, pred: impl Fn(&Meta) -> bool) -> Option<&Meta>;
fn metadata_get_all<Meta: 'static>(&self) -> Vec<&Meta>;
fn metadata_find_all<Meta: 'static>(&self, pred: impl Fn(&Meta) -> bool) -> Vec<&Meta>;
fn metadata_contains<Meta: 'static>(&self, pred: impl Fn(&Meta) -> bool) -> bool;
}
impl<T: Builder + ?Sized> BuilderExt for T {
fn interfaces_get_all(&self) -> Vec<TypeInfo> {
let mut ret = Vec::new();
self.interfaces(&mut |i| {
ret.push(*i);
true
});
ret
}
fn interfaces_contain<Iface: 'static>(&self) -> bool {
let type_id = TypeId::of::<Iface>();
self.interfaces_contain_type_id(&type_id)
}
fn interfaces_contain_type_id(&self, type_id: &TypeId) -> bool {
let mut ret = false;
self.interfaces(&mut |i| {
if i.id == *type_id {
ret = true;
return false;
}
true
});
ret
}
fn dependencies_get_all(&self) -> Vec<DependencyInfo> {
let mut ret = Vec::new();
self.dependencies(&mut |i| {
ret.push(*i);
true
});
ret
}
fn metadata_get_first<Meta: 'static>(&self) -> Option<&Meta> {
let mut ret: Option<&Meta> = None;
self.metadata(&mut |m| {
if let Some(v) = m.downcast_ref::<Meta>() {
ret = Some(v);
return false;
}
true
});
ret
}
fn metadata_find_first<Meta: 'static>(&self, pred: impl Fn(&Meta) -> bool) -> Option<&Meta> {
let mut ret: Option<&Meta> = None;
self.metadata(&mut |m| {
if let Some(v) = m.downcast_ref::<Meta>()
&& pred(v)
{
ret = Some(v);
return false;
}
true
});
ret
}
fn metadata_get_all<Meta: 'static>(&self) -> Vec<&Meta> {
let mut ret: Vec<&Meta> = Vec::new();
self.metadata(&mut |m| {
if let Some(v) = m.downcast_ref::<Meta>() {
ret.push(v);
}
true
});
ret
}
fn metadata_find_all<Meta: 'static>(&self, pred: impl Fn(&Meta) -> bool) -> Vec<&Meta> {
let mut ret: Vec<&Meta> = Vec::new();
self.metadata(&mut |m| {
if let Some(v) = m.downcast_ref::<Meta>()
&& pred(v)
{
ret.push(v);
}
true
});
ret
}
fn metadata_contains<Meta: 'static>(&self, pred: impl Fn(&Meta) -> bool) -> bool {
let mut ret = false;
self.metadata(&mut |m| {
if let Some(v) = m.downcast_ref::<Meta>()
&& pred(v)
{
ret = true;
return false;
}
true
});
ret
}
}
pub trait TypedBuilder<T: Send + Sync + ?Sized>: Builder {
fn get(&self, cat: &Catalog) -> Result<Arc<T>, InjectionError> {
self.get_with_context(cat, &InjectionContext::new_root())
}
fn get_with_context(
&self,
cat: &Catalog,
ctx: &InjectionContext,
) -> Result<Arc<T>, InjectionError>;
fn bind_interfaces(&self, cat: &mut CatalogBuilder);
}
pub trait TypedBuilderExt<T: Send + Sync + ?Sized>: TypedBuilder<T> {
fn without_default_interfaces(self) -> impl TypedBuilder<T>;
}
pub trait TypedBuilderCast<I: Send + Sync + ?Sized> {
fn cast(self) -> impl TypedBuilder<I>;
}
pub trait Component {
type Impl: Send + Sync;
type Builder: TypedBuilder<Self::Impl>;
fn builder() -> Self::Builder;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TypeInfo {
pub id: TypeId,
pub name: &'static str,
}
impl TypeInfo {
pub fn of<T: ?Sized + 'static>() -> Self {
Self {
id: std::any::TypeId::of::<T>(),
name: std::any::type_name::<T>(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DependencyInfo {
pub iface: TypeInfo,
pub spec: TypeInfo,
pub is_bound: bool,
}
impl DependencyInfo {
pub fn of<T: ?Sized + 'static, Spec: DependencySpec + 'static>() -> Self {
Self {
iface: TypeInfo::of::<T>(),
spec: TypeInfo::of::<Spec>(),
is_bound: false,
}
}
pub fn bound(self, is_bound: bool) -> Self {
Self { is_bound, ..self }
}
}
impl<Bld, Impl> TypedBuilderExt<Impl> for Bld
where
Impl: Send + Sync,
Bld: TypedBuilder<Impl>,
{
fn without_default_interfaces(self) -> impl TypedBuilder<Impl> {
TypedBuilderWithoutDefaultInterfaces(self)
}
}
pub struct TypedBuilderWithoutDefaultInterfaces<Bld>(Bld);
impl<Bld> Builder for TypedBuilderWithoutDefaultInterfaces<Bld>
where
Bld: Builder,
{
fn instance_type(&self) -> TypeInfo {
self.0.instance_type()
}
fn scope_type(&self) -> TypeInfo {
self.0.scope_type()
}
fn interfaces(&self, clb: &mut dyn FnMut(&TypeInfo) -> bool) {
self.0.interfaces(clb);
}
fn dependencies(&self, clb: &mut dyn FnMut(&DependencyInfo) -> bool) {
self.0.dependencies(clb);
}
fn metadata<'a>(&'a self, clb: &mut dyn FnMut(&'a dyn std::any::Any) -> bool) {
self.0.metadata(clb);
}
fn get_any(
&self,
cat: &Catalog,
ctx: &InjectionContext,
) -> Result<Arc<dyn Any + Send + Sync>, InjectionError> {
self.0.get_any(cat, ctx)
}
}
impl<Bld, Impl> TypedBuilder<Impl> for TypedBuilderWithoutDefaultInterfaces<Bld>
where
Impl: Send + Sync,
Bld: TypedBuilder<Impl>,
{
fn get_with_context(
&self,
cat: &Catalog,
ctx: &InjectionContext,
) -> Result<Arc<Impl>, InjectionError> {
self.0.get_with_context(cat, ctx)
}
fn bind_interfaces(&self, _cat: &mut CatalogBuilder) {}
}
impl<Impl> Builder for Arc<Impl>
where
Impl: Send + Sync + 'static,
{
fn instance_type(&self) -> TypeInfo {
TypeInfo::of::<Impl>()
}
fn scope_type(&self) -> TypeInfo {
TypeInfo::of::<crate::scopes::Singleton>()
}
fn interfaces(&self, _clb: &mut dyn FnMut(&TypeInfo) -> bool) {}
fn dependencies(&self, _clb: &mut dyn FnMut(&DependencyInfo) -> bool) {}
fn metadata<'a>(&'a self, _clb: &mut dyn FnMut(&'a dyn Any) -> bool) {}
fn get_any(
&self,
_cat: &Catalog,
_ctx: &InjectionContext,
) -> Result<Arc<dyn Any + Send + Sync>, InjectionError> {
Ok(self.clone())
}
}
impl<Impl> TypedBuilder<Impl> for Arc<Impl>
where
Impl: Send + Sync + 'static,
{
fn get_with_context(
&self,
_cat: &Catalog,
_ctx: &InjectionContext,
) -> Result<Arc<Impl>, InjectionError> {
Ok(self.clone())
}
fn bind_interfaces(&self, _cat: &mut CatalogBuilder) {}
}
impl<Fct, Impl> Builder for Fct
where
Fct: Fn() -> Arc<Impl> + Send + Sync,
Impl: Send + Sync + 'static,
{
fn instance_type(&self) -> TypeInfo {
TypeInfo::of::<Impl>()
}
fn scope_type(&self) -> TypeInfo {
TypeInfo::of::<crate::scopes::Transient>()
}
fn interfaces(&self, _clb: &mut dyn FnMut(&TypeInfo) -> bool) {}
fn dependencies(&self, _clb: &mut dyn FnMut(&DependencyInfo) -> bool) {}
fn metadata<'a>(&'a self, _clb: &mut dyn FnMut(&'a dyn Any) -> bool) {}
fn get_any(
&self,
_cat: &Catalog,
_ctx: &InjectionContext,
) -> Result<Arc<dyn Any + Send + Sync>, InjectionError> {
Ok(self())
}
}
impl<Fct, Impl> TypedBuilder<Impl> for Fct
where
Fct: Fn() -> Arc<Impl> + Send + Sync,
Impl: Send + Sync + 'static,
{
fn get_with_context(
&self,
_cat: &Catalog,
_ctx: &InjectionContext,
) -> Result<Arc<Impl>, InjectionError> {
Ok(self())
}
fn bind_interfaces(&self, _cat: &mut CatalogBuilder) {}
}
pub(crate) struct LazyBuilder<Fct, Impl>
where
Fct: FnOnce() -> Impl,
Impl: 'static + Send + Sync,
{
state: Mutex<LazyBuilderState<Fct, Impl>>,
}
struct LazyBuilderState<Fct, Impl> {
factory: Option<Fct>,
instance: Option<Arc<Impl>>,
}
impl<Fct, Impl> LazyBuilder<Fct, Impl>
where
Fct: FnOnce() -> Impl,
Impl: 'static + Send + Sync,
{
pub fn new(factory: Fct) -> Self {
Self {
state: Mutex::new(LazyBuilderState {
factory: Some(factory),
instance: None,
}),
}
}
}
impl<Fct, Impl> Builder for LazyBuilder<Fct, Impl>
where
Fct: FnOnce() -> Impl + Send + Sync,
Impl: 'static + Send + Sync,
{
fn instance_type(&self) -> TypeInfo {
TypeInfo::of::<Impl>()
}
fn scope_type(&self) -> TypeInfo {
TypeInfo::of::<crate::scopes::Transient>()
}
fn interfaces(&self, _clb: &mut dyn FnMut(&TypeInfo) -> bool) {}
fn dependencies(&self, _clb: &mut dyn FnMut(&DependencyInfo) -> bool) {}
fn metadata<'a>(&'a self, _clb: &mut dyn FnMut(&'a dyn Any) -> bool) {}
fn get_any(
&self,
cat: &Catalog,
ctx: &InjectionContext,
) -> Result<Arc<dyn Any + Send + Sync>, InjectionError> {
Ok(TypedBuilder::get_with_context(self, cat, ctx)?)
}
}
impl<Fct, Impl> TypedBuilder<Impl> for LazyBuilder<Fct, Impl>
where
Fct: FnOnce() -> Impl + Send + Sync,
Impl: 'static + Send + Sync,
{
fn get_with_context(
&self,
_cat: &Catalog,
_ctx: &InjectionContext,
) -> Result<Arc<Impl>, InjectionError> {
let mut s = self.state.lock().unwrap();
if let Some(inst) = s.instance.as_ref() {
Ok(inst.clone())
} else {
let factory = s.factory.take().unwrap();
let inst = Arc::new(factory());
s.instance = Some(inst.clone());
Ok(inst)
}
}
fn bind_interfaces(&self, _cat: &mut CatalogBuilder) {}
}