1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
use std::any::{type_name, TypeId};
use std::collections::HashMap;
use std::marker::Unsize;
use std::sync::Arc;
use multimap::MultiMap;
use super::catalog::*;
use crate::*;
/////////////////////////////////////////////////////////////////////////////////////////
#[derive(Clone)]
pub struct CatalogBuilder {
builders: HashMap<ImplTypeId, Arc<dyn Builder>>,
bindings: MultiMap<IfaceTypeId, Binding>,
chained_catalog: Option<Catalog>,
}
/////////////////////////////////////////////////////////////////////////////////////////
impl CatalogBuilder {
pub fn new() -> Self {
Self {
builders: HashMap::new(),
bindings: MultiMap::new(),
chained_catalog: None,
}
}
pub fn new_chained(chained_catalog: &Catalog) -> Self {
Self {
builders: HashMap::new(),
bindings: MultiMap::new(),
chained_catalog: Some(chained_catalog.clone()),
}
}
/// Registers a component using its associated builder.
///
/// Note that unline [CatalogBuilder::add_builder()] this will also bind the
/// implementation to component's default interfaces.
pub fn add<C: Component>(&mut self) -> &mut Self {
C::register(self);
self
}
pub fn add_builder<Bld, Impl>(&mut self, builder: Bld) -> &mut Self
where
Impl: 'static + Send + Sync,
Bld: TypedBuilder<Impl> + 'static,
{
let key = ImplTypeId(TypeId::of::<Impl>());
if self.builders.contains_key(&key) {
panic!(
"Builder for type {} is already registered",
type_name::<Impl>()
);
}
let builder = Arc::new(builder);
self.builders.insert(key, builder.clone());
self.bindings.insert(
IfaceTypeId(TypeId::of::<Impl>()),
Binding::new(
Arc::new(TypeCaster::<Impl> {
// SAFETY: `TypeCaster<Iface>` is guaranteed to be invoked only on the `Impl`
// instances
cast_arc: |v| v.downcast().unwrap(),
}),
builder,
),
);
self
}
pub fn add_value<Impl>(&mut self, value: Impl) -> &mut Self
where
Impl: 'static + Send + Sync,
{
self.add_builder(Arc::new(value));
self
}
/// Uses the provided factory once and caches the instance in a [Singleton]
/// scope
pub fn add_value_lazy<Fct, Impl>(&mut self, factory: Fct) -> &mut Self
where
Fct: FnOnce() -> Impl + Send + Sync + 'static,
Impl: Send + Sync + 'static,
{
self.add_builder(Lazy::new(factory));
self
}
// TODO: WTF is Unsize
pub fn bind<Iface, Impl>(&mut self) -> &mut Self
where
Iface: 'static + ?Sized,
Impl: 'static + Send + Sync + Unsize<Iface>,
{
let iface_type = IfaceTypeId(TypeId::of::<Iface>());
let impl_type = ImplTypeId(TypeId::of::<Impl>());
let builder = self.builders.get(&impl_type);
if builder.is_none() {
panic!("Builder for type {} is not registered", type_name::<Impl>());
}
self.bindings.insert(
iface_type,
Binding::new(
Arc::new(TypeCaster::<Iface> {
cast_arc: |v| {
// SAFETY: `TypeCaster<Iface>` is guaranteed to be invoked only on the
// `Impl` instances
let s: Arc<Impl> = v.downcast().unwrap();
let t: Arc<Iface> = s;
t
},
}),
builder.unwrap().clone(),
),
);
self
}
pub fn build(&mut self) -> Catalog {
let mut builders = HashMap::new();
let mut bindings = MultiMap::new();
std::mem::swap(&mut self.builders, &mut builders);
std::mem::swap(&mut self.bindings, &mut bindings);
Catalog::new(builders, bindings, self.chained_catalog.take())
}
/// Validates the dependency graph returning a combined error.
///
/// In case some of your types are registered dynamically you can
/// [ValidationErrorExt::ignore()] method which is implemented on the
/// Result type (you need to import the trait).
///
/// Example:
/// ```
/// use dill::*;
/// trait MyDynamicType {}
///
/// let mut b = CatalogBuilder::new();
/// // Populate the builder
/// b.validate()
/// .ignore::<dyn MyDynamicType>()
/// .unwrap();
/// ```
pub fn validate(&mut self) -> Result<(), ValidationError> {
// TODO: Should return a validation report type that will track
// - Unresolved dependencies
// - Ambiguous dependencies
// - Missing dependenies with defaults
// - AllOf that don't resolve to anything
//
// Users will then be able to specify whether to treat them as errors / warnings
// or have them ignored.
let mut errors = Vec::new();
// TODO: Avoid allocations when constructing a temporary catalog
let cat = self.build();
for builder in cat.builders() {
if let Err(mut err) = builder.check(&cat) {
errors.append(&mut err.errors);
}
}
// Sort and deduplicate by type
errors.sort_by_key(|e| match e {
InjectionError::Unregistered(err) => err.type_id,
InjectionError::Ambiguous(err) => err.type_id,
});
errors.dedup_by_key(|e| match e {
InjectionError::Unregistered(err) => err.type_id,
InjectionError::Ambiguous(err) => err.type_id,
});
// Return builder to its original state
let mut cat = Arc::into_inner(cat.0).unwrap();
std::mem::swap(&mut self.builders, &mut cat.builders);
std::mem::swap(&mut self.bindings, &mut cat.bindings);
self.chained_catalog = cat.chained_catalog.take();
if errors.len() != 0 {
Err(ValidationError { errors })
} else {
Ok(())
}
}
}