1use std::any::Any;
33use std::collections::HashMap;
34use std::fmt;
35use std::sync::Arc;
36
37#[derive(Debug)]
39pub enum DependencyError {
40 NotFound(String),
42 CircularDependency(Vec<String>),
44 InitializationFailed {
46 name: String,
48 source: Box<dyn std::error::Error + Send + Sync>,
50 },
51 ConfigError(String),
53 TypeMismatch {
55 expected: String,
57 actual: String,
59 },
60}
61
62impl fmt::Display for DependencyError {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match self {
65 DependencyError::NotFound(name) => {
66 write!(f, "Dependency not found: {}", name)
67 }
68 DependencyError::CircularDependency(chain) => {
69 write!(f, "Circular dependency detected: {}", chain.join(" -> "))
70 }
71 DependencyError::InitializationFailed { name, source } => {
72 write!(f, "Failed to initialize '{}': {}", name, source)
73 }
74 DependencyError::ConfigError(msg) => {
75 write!(f, "Configuration error: {}", msg)
76 }
77 DependencyError::TypeMismatch { expected, actual } => {
78 write!(f, "Type mismatch: expected {}, got {}", expected, actual)
79 }
80 }
81 }
82}
83
84impl std::error::Error for DependencyError {
85 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
86 match self {
87 DependencyError::InitializationFailed { source, .. } => Some(source.as_ref()),
88 _ => None,
89 }
90 }
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
95pub enum Scope {
96 #[default]
98 Singleton,
99 Transient,
101}
102
103#[crate::async_trait::async_trait]
109pub trait Provider<T>: Send + Sync {
110 async fn provide(&self) -> Result<T, DependencyError>;
112}
113
114pub trait FromEnv: Sized {
116 fn from_env() -> Result<Self, DependencyError>;
118}
119
120#[crate::async_trait::async_trait]
122pub trait AsyncInit: Sized {
123 async fn init() -> Result<Self, DependencyError>;
125}
126
127#[crate::async_trait::async_trait]
129pub trait AsyncInitWith<Deps>: Sized {
130 async fn init_with(deps: Deps) -> Result<Self, DependencyError>;
132}
133
134pub struct DependencyRegistry {
136 singletons: HashMap<std::any::TypeId, Arc<dyn Any + Send + Sync>>,
137}
138
139impl Default for DependencyRegistry {
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145impl DependencyRegistry {
146 pub fn new() -> Self {
148 Self {
149 singletons: HashMap::new(),
150 }
151 }
152
153 pub fn store_singleton<T: Send + Sync + 'static>(&mut self, value: T) {
155 let type_id = std::any::TypeId::of::<T>();
156 self.singletons.insert(type_id, Arc::new(value));
157 }
158
159 pub fn get_singleton<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
161 let type_id = std::any::TypeId::of::<T>();
162 self.singletons
163 .get(&type_id)
164 .and_then(|any| any.clone().downcast::<T>().ok())
165 }
166
167 pub fn has_singleton<T: 'static>(&self) -> bool {
169 let type_id = std::any::TypeId::of::<T>();
170 self.singletons.contains_key(&type_id)
171 }
172}
173
174pub struct ContainerBuilder {
176 initialization_order: Vec<String>,
177 initialized: std::collections::HashSet<String>,
178}
179
180impl Default for ContainerBuilder {
181 fn default() -> Self {
182 Self::new()
183 }
184}
185
186impl ContainerBuilder {
187 pub fn new() -> Self {
189 Self {
190 initialization_order: Vec::new(),
191 initialized: std::collections::HashSet::new(),
192 }
193 }
194
195 pub fn mark_initialized(&mut self, name: &str) {
197 self.initialized.insert(name.to_string());
198 self.initialization_order.push(name.to_string());
199 }
200
201 pub fn is_initialized(&self, name: &str) -> bool {
203 self.initialized.contains(name)
204 }
205
206 pub fn initialization_order(&self) -> &[String] {
208 &self.initialization_order
209 }
210
211 pub fn validate_dependencies(&self, deps: &[&str]) -> Result<(), DependencyError> {
213 for dep in deps {
214 if !self.is_initialized(dep) {
215 return Err(DependencyError::NotFound((*dep).to_string()));
216 }
217 }
218 Ok(())
219 }
220}
221
222pub fn env_var(name: &str) -> Result<String, DependencyError> {
224 std::env::var(name).map_err(|_| {
225 DependencyError::ConfigError(format!("Environment variable '{}' not set", name))
226 })
227}
228
229pub fn env_var_opt(name: &str) -> Option<String> {
231 std::env::var(name).ok()
232}
233
234pub fn env_var_or(name: &str, default: &str) -> String {
236 std::env::var(name).unwrap_or_else(|_| default.to_string())
237}
238
239pub fn env_var_parse<T: std::str::FromStr>(name: &str) -> Result<T, DependencyError>
241where
242 T::Err: std::fmt::Display,
243{
244 let value = env_var(name)?;
245 value.parse().map_err(|e: T::Err| {
246 DependencyError::ConfigError(format!(
247 "Failed to parse environment variable '{}': {}",
248 name, e
249 ))
250 })
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn test_dependency_error_display() {
259 let err = DependencyError::NotFound("database".to_string());
260 assert!(err.to_string().contains("database"));
261
262 let err = DependencyError::CircularDependency(vec![
263 "a".to_string(),
264 "b".to_string(),
265 "a".to_string(),
266 ]);
267 assert!(err.to_string().contains("a -> b -> a"));
268
269 let err = DependencyError::ConfigError("missing var".to_string());
270 assert!(err.to_string().contains("missing var"));
271 }
272
273 #[test]
274 fn test_scope_default() {
275 assert_eq!(Scope::default(), Scope::Singleton);
276 }
277
278 #[test]
279 fn test_dependency_registry() {
280 let mut registry = DependencyRegistry::new();
281
282 registry.store_singleton(42i32);
283 assert!(registry.has_singleton::<i32>());
284
285 let value = registry.get_singleton::<i32>();
286 assert_eq!(*value.unwrap(), 42);
287
288 assert!(!registry.has_singleton::<String>());
289 }
290
291 #[test]
292 fn test_container_builder() {
293 let mut builder = ContainerBuilder::new();
294
295 assert!(!builder.is_initialized("config"));
296 builder.mark_initialized("config");
297 assert!(builder.is_initialized("config"));
298
299 builder.mark_initialized("database");
300 assert_eq!(builder.initialization_order(), &["config", "database"]);
301 }
302
303 #[test]
304 fn test_container_builder_validate() {
305 let mut builder = ContainerBuilder::new();
306 builder.mark_initialized("config");
307
308 assert!(builder.validate_dependencies(&["config"]).is_ok());
309 assert!(builder.validate_dependencies(&["database"]).is_err());
310 }
311
312 #[test]
313 fn test_env_helpers() {
314 let value = env_var_or("NONEXISTENT_VAR_12345", "default");
316 assert_eq!(value, "default");
317
318 let value = env_var_opt("NONEXISTENT_VAR_12345");
320 assert!(value.is_none());
321 }
322}