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