use core::fmt;
#[cfg(feature = "tracing")]
use tracing::error;
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub enum ErrorKind {
ServiceNotProvided,
TypeMismatch,
ProviderAlreadyRegistered,
CircularDependency,
AsyncFactoryRequiresAsyncResolve,
ResourceLimitExceeded,
ModuleLifecycleFailed,
GraphValidationFailed,
}
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Error {
pub kind: ErrorKind,
pub message: String,
}
impl Error {
pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
let error = Self {
kind: kind.clone(),
message: message.into(),
};
#[cfg(feature = "tracing")]
error!("{}", error);
error
}
pub fn service_not_provided(type_name: &str) -> Self {
Self::new(
ErrorKind::ServiceNotProvided,
format!(
"No provider registered for type: {}. Register it in Module::configure(...) with injector.provide::<T>(Provider::...).",
type_name
),
)
}
pub fn service_not_provided_named(type_name: &str, name: &str) -> Self {
Self::new(
ErrorKind::ServiceNotProvided,
format!(
"No provider registered for type: {} with name: {}. Register it with provide_named/try_provide_named.",
type_name, name
),
)
}
pub fn service_not_provided_for_override(type_name: &str) -> Self {
Self::new(
ErrorKind::ServiceNotProvided,
format!(
"Cannot override provider for type: {} because no provider is registered. Register one first with provide/try_provide.",
type_name
),
)
}
pub fn type_mismatch(type_name: &str) -> Self {
Self::new(
ErrorKind::TypeMismatch,
format!("Type mismatch when resolving: {}", type_name),
)
}
pub fn provider_already_registered(type_name: &str, scope: &str) -> Self {
Self::new(
ErrorKind::ProviderAlreadyRegistered,
format!(
"Provider ({} scope) already registered for type: {}. Use override_provider/try_override_provider to replace it.",
scope, type_name
),
)
}
pub fn provider_already_registered_named(type_name: &str, name: &str, scope: &str) -> Self {
Self::new(
ErrorKind::ProviderAlreadyRegistered,
format!(
"Provider ({} scope) already registered for type: {} with name: {}.",
scope, type_name, name
),
)
}
pub fn circular_dependency(dependency_chain: &[&str]) -> Self {
Self::new(
ErrorKind::CircularDependency,
format!(
"Circular dependency detected: {}. Break the cycle by introducing a trait boundary, lazy lookup, or refactoring the dependency direction.",
dependency_chain.join(" -> ")
),
)
}
pub fn async_factory_requires_async_resolve(type_name: &str) -> Self {
Self::new(
ErrorKind::AsyncFactoryRequiresAsyncResolve,
format!(
"Type {} is registered with an async provider; use try_resolve_async/resolve_async",
type_name
),
)
}
pub fn resource_limit_exceeded(type_name: &str, details: &str) -> Self {
Self::new(
ErrorKind::ResourceLimitExceeded,
format!(
"Resource limit exceeded while creating type {}: {}",
type_name, details
),
)
}
pub fn module_lifecycle_failed(module_name: &str, phase: &str, details: &str) -> Self {
Self::new(
ErrorKind::ModuleLifecycleFailed,
format!(
"Module lifecycle failed: module={}, phase={}, details={}",
module_name, phase, details
),
)
}
pub fn graph_validation_failed(details: &str) -> Self {
Self::new(
ErrorKind::GraphValidationFailed,
format!(
"Dependency graph validation failed: {}. Check dependency_graph()/validate_graph() output for details.",
details
),
)
}
pub fn bootstrap_aggregate(errors: Vec<Error>) -> Self {
if errors.len() == 1 {
return errors.into_iter().next().unwrap();
}
let message = format!(
"Bootstrap failed: {} module(s) reported errors:\n{}",
errors.len(),
errors
.iter()
.enumerate()
.map(|(i, e)| format!(" {}) {}", i + 1, e.message))
.collect::<Vec<_>>()
.join("\n")
);
Self::new(ErrorKind::ModuleLifecycleFailed, message)
}
pub fn shutdown_aggregate(errors: Vec<Error>) -> Self {
if errors.len() == 1 {
return errors.into_iter().next().unwrap();
}
let message = format!(
"Shutdown failed: {} module(s) reported errors:\n{}",
errors.len(),
errors
.iter()
.enumerate()
.map(|(i, e)| format!(" {}) {}", i + 1, e.message))
.collect::<Vec<_>>()
.join("\n")
);
Self::new(ErrorKind::ModuleLifecycleFailed, message)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "debug")]
{
write!(f, "({:?}) - {}", self.kind, self.message)
}
#[cfg(not(feature = "debug"))]
{
write!(f, "{}", self.message)
}
}
}
#[cfg(feature = "debug")]
impl std::error::Error for Error {}
#[cfg(test)]
mod tests;