use std::any::{Any, TypeId};
use std::marker::PhantomData;
use crate::injector::{DependenciesAccess, DependenciesVisitor, Injector, IntrospectVisitor};
use crate::{AssembleError, TypeInfo};
pub fn check_factory<T: Any, F: Injector>() -> Vec<AssembleError> {
let mut checker = IntegrityChecker::<F>::new();
let mut visitor = CheckImplementations::new(&mut checker);
F::introspect::<T>(&mut visitor);
visitor.finish::<T>();
checker.into_errors()
}
struct IntegrityChecker<F: Injector> {
_introspect: PhantomData<F>,
stack: Vec<TypeId>,
errors: Vec<AssembleError>,
}
impl<F: Injector> IntegrityChecker<F> {
fn new() -> Self {
Self {
_introspect: PhantomData,
stack: vec![],
errors: vec![],
}
}
fn add_error(&mut self, err: AssembleError) {
self.errors.push(err);
}
fn into_errors(self) -> Vec<AssembleError> {
self.errors
}
}
struct CheckDeps<'a, F: Injector> {
checker: &'a mut IntegrityChecker<F>,
}
impl<'a, F: Injector> DependenciesVisitor for CheckDeps<'a, F> {
fn dependency<T: Any>(&mut self) {
let type_id = TypeId::of::<T>();
if self.checker.stack.contains(&type_id) {
self.checker.add_error(AssembleError::CyclicDependency {
ty: TypeInfo::of::<T>(),
});
return;
}
self.checker.stack.push(type_id);
let mut visitor = CheckImplementations::new(self.checker);
F::introspect::<T>(&mut visitor);
visitor.finish::<T>();
self.checker.stack.pop();
}
}
enum Implementations {
Single,
Multiple,
}
struct CheckImplementations<'a, F: Injector> {
implementations: Option<Implementations>,
checker: &'a mut IntegrityChecker<F>,
}
impl<'a, F: Injector> CheckImplementations<'a, F> {
fn new(checker: &'a mut IntegrityChecker<F>) -> Self {
Self {
implementations: None,
checker,
}
}
fn finish<T: Any>(self) {
match self.implementations {
None => self
.checker
.add_error(AssembleError::MissingImplementation {
ty: TypeInfo::of::<T>(),
}),
Some(Implementations::Multiple) => {
self.checker
.add_error(AssembleError::DuplicateImplementation {
ty: TypeInfo::of::<T>(),
})
}
Some(Implementations::Single) => (),
}
}
}
impl<'a, F: Injector> IntrospectVisitor for CheckImplementations<'a, F> {
fn item<M: Any, T: Any, A: DependenciesAccess>(&mut self, deps: A) {
match &mut self.implementations {
None => self.implementations = Some(Implementations::Single),
Some(Implementations::Single) => self.implementations = Some(Implementations::Multiple),
Some(Implementations::Multiple) => {
self.implementations = Some(Implementations::Multiple)
}
}
deps.next(&mut CheckDeps {
checker: self.checker,
});
}
}