fluxdi 1.2.2

FluxDI - Semi-Automatic Dependency Injector
Documentation
use std::{any::TypeId, cell::RefCell};

use crate::error::{Error, ErrorKind};
#[cfg(feature = "tracing")]
use crate::observability::EVENT_CIRCULAR_DEPENDENCY;

#[cfg(feature = "tracing")]
use tracing::debug;

thread_local! {
    static RESOLVE_STACK: RefCell<Vec<TypeId>> = const { RefCell::new(Vec::new()) };
}

pub struct ResolveGuard {
    type_id: TypeId,
}

impl ResolveGuard {
    pub fn push(type_id: TypeId) -> Result<Self, Error> {
        RESOLVE_STACK.with(|stack| {
            let mut stack = stack.borrow_mut();

            if stack.contains(&type_id) {
                #[cfg(feature = "tracing")]
                debug!(
                    event = EVENT_CIRCULAR_DEPENDENCY,
                    type_id = ?type_id,
                    depth = stack.len(),
                    "Circular dependency detected during resolve"
                );

                return Err(Error::new(
                    ErrorKind::CircularDependency,
                    format!(
                        "Circular dependency detected while resolving type_id: {:?}",
                        type_id
                    ),
                ));
            }

            stack.push(type_id);
            Ok(Self { type_id })
        })
    }
}

impl Drop for ResolveGuard {
    fn drop(&mut self) {
        RESOLVE_STACK.with(|stack| {
            let mut stack = stack.borrow_mut();
            if let Some(last) = stack.pop() {
                if last != self.type_id {
                    panic!(
                        "ResolveGuard stack corrupted: expected to pop {:?} but popped {:?}",
                        self.type_id, last
                    );
                }
            } else {
                panic!("ResolveGuard stack corrupted: attempted to pop from an empty stack");
            }
        });
    }
}