dependency_injector/
error.rs1use std::any::TypeId;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
8pub enum DiError {
9 #[error(
16 "Service not found: {type_name} (not registered in this scope chain; \
17 was it registered in a different scope, or removed by clear()? \
18 Container::debug_registrations() lists what is registered)"
19 )]
20 NotFound {
21 type_name: &'static str,
22 type_id: TypeId,
23 },
24
25 #[error("Circular dependency detected while resolving: {type_name}")]
27 CircularDependency { type_name: &'static str },
28
29 #[error("Failed to create service {type_name}: {reason}")]
31 CreationFailed {
32 type_name: &'static str,
33 reason: String,
34 },
35
36 #[error("Container is locked - cannot register new services")]
38 Locked,
39
40 #[error("Service already registered: {type_name}")]
42 AlreadyRegistered { type_name: &'static str },
43
44 #[error("Parent scope has been dropped")]
46 ParentDropped,
47
48 #[error("Internal DI error: {0}")]
50 Internal(String),
51}
52
53impl DiError {
54 #[inline]
56 pub fn not_found<T: 'static>() -> Self {
57 Self::NotFound {
58 type_name: std::any::type_name::<T>(),
59 type_id: TypeId::of::<T>(),
60 }
61 }
62
63 #[inline]
65 pub fn creation_failed<T: 'static>(reason: impl Into<String>) -> Self {
66 Self::CreationFailed {
67 type_name: std::any::type_name::<T>(),
68 reason: reason.into(),
69 }
70 }
71
72 #[inline]
74 pub fn already_registered<T: 'static>() -> Self {
75 Self::AlreadyRegistered {
76 type_name: std::any::type_name::<T>(),
77 }
78 }
79
80 #[inline]
82 pub fn circular<T: 'static>() -> Self {
83 Self::CircularDependency {
84 type_name: std::any::type_name::<T>(),
85 }
86 }
87}
88
89impl Clone for DiError {
90 fn clone(&self) -> Self {
91 match self {
92 Self::NotFound { type_name, type_id } => Self::NotFound {
93 type_name,
94 type_id: *type_id,
95 },
96 Self::CircularDependency { type_name } => Self::CircularDependency { type_name },
97 Self::CreationFailed { type_name, reason } => Self::CreationFailed {
98 type_name,
99 reason: reason.clone(),
100 },
101 Self::Locked => Self::Locked,
102 Self::AlreadyRegistered { type_name } => Self::AlreadyRegistered { type_name },
103 Self::ParentDropped => Self::ParentDropped,
104 Self::Internal(s) => Self::Internal(s.clone()),
105 }
106 }
107}
108
109pub type Result<T> = std::result::Result<T, DiError>;
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[allow(dead_code)]
117 struct MissingService;
118
119 #[test]
120 fn test_not_found_message_contains_type_name_and_hint() {
121 let err = DiError::not_found::<MissingService>();
122 let message = err.to_string();
123
124 assert!(message.contains("MissingService"));
126 assert!(message.contains("different scope"));
128 assert!(message.contains("clear()"));
129 assert!(message.contains("Container::debug_registrations()"));
131 }
132
133 #[test]
134 fn test_not_found_carries_type_id() {
135 let err = DiError::not_found::<MissingService>();
136 match err {
137 DiError::NotFound { type_id, .. } => {
138 assert_eq!(type_id, TypeId::of::<MissingService>());
139 }
140 other => panic!("expected NotFound, got: {other}"),
141 }
142 }
143}