elif_core/providers/
provider.rs

1use crate::container::{Container, ContainerBuilder};
2use crate::errors::CoreError;
3
4/// Provider error type
5#[derive(Debug, thiserror::Error)]
6pub enum ProviderError {
7    #[error("Circular dependency detected in provider: {provider}")]
8    CircularDependency { provider: String },
9    
10    #[error("Missing dependency '{dependency}' for provider '{provider}'")]
11    MissingDependency { provider: String, dependency: String },
12    
13    #[error("Provider registration failed: {message}")]
14    RegistrationFailed { message: String },
15    
16    #[error("Provider boot failed: {message}")]
17    BootFailed { message: String },
18    
19    #[error("Container error: {0}")]
20    Container(#[from] CoreError),
21}
22
23/// Service provider trait for registering services and managing lifecycle
24pub trait ServiceProvider: Send + Sync {
25    /// Provider name for identification and dependency resolution
26    fn name(&self) -> &'static str;
27    
28    /// Register services in the container builder
29    /// This is called during the registration phase
30    fn register(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ProviderError>;
31    
32    /// Boot the provider after all services are registered
33    /// This is called during the boot phase with access to the built container
34    fn boot(&self, container: &Container) -> Result<(), ProviderError> {
35        // Default implementation does nothing
36        let _ = container; // Suppress unused parameter warning
37        Ok(())
38    }
39    
40    /// Provider dependencies (other providers that must be registered first)
41    fn dependencies(&self) -> Vec<&'static str> {
42        vec![]
43    }
44    
45    /// Defer boot phase (useful for providers that need other providers to be booted first)
46    fn defer_boot(&self) -> bool {
47        false
48    }
49    
50    /// Provider version for compatibility checking
51    fn version(&self) -> Option<&'static str> {
52        None
53    }
54    
55    /// Provider description
56    fn description(&self) -> Option<&'static str> {
57        None
58    }
59    
60    /// Check if this provider is optional
61    fn is_optional(&self) -> bool {
62        true
63    }
64}
65
66/// Provider metadata for introspection
67#[derive(Debug, Clone)]
68pub struct ProviderMetadata {
69    pub name: String,
70    pub version: Option<String>,
71    pub description: Option<String>,
72    pub dependencies: Vec<String>,
73    pub defer_boot: bool,
74    pub is_optional: bool,
75}
76
77impl ProviderMetadata {
78    /// Create metadata from a provider
79    pub fn from_provider<P: ServiceProvider + ?Sized>(provider: &P) -> Self {
80        Self {
81            name: provider.name().to_string(),
82            version: provider.version().map(|v| v.to_string()),
83            description: provider.description().map(|d| d.to_string()),
84            dependencies: provider.dependencies().iter().map(|d| d.to_string()).collect(),
85            defer_boot: provider.defer_boot(),
86            is_optional: provider.is_optional(),
87        }
88    }
89}
90
91/// Base provider implementation for common functionality
92#[derive(Debug)]
93pub struct BaseProvider {
94    name: &'static str,
95    version: Option<&'static str>,
96    description: Option<&'static str>,
97    dependencies: Vec<&'static str>,
98    defer_boot: bool,
99    is_optional: bool,
100}
101
102impl BaseProvider {
103    /// Create a new base provider
104    pub fn new(name: &'static str) -> Self {
105        Self {
106            name,
107            version: None,
108            description: None,
109            dependencies: Vec::new(),
110            defer_boot: false,
111            is_optional: true,
112        }
113    }
114    
115    /// Set provider version
116    pub fn with_version(mut self, version: &'static str) -> Self {
117        self.version = Some(version);
118        self
119    }
120    
121    /// Set provider description
122    pub fn with_description(mut self, description: &'static str) -> Self {
123        self.description = Some(description);
124        self
125    }
126    
127    /// Set provider dependencies
128    pub fn with_dependencies(mut self, dependencies: Vec<&'static str>) -> Self {
129        self.dependencies = dependencies;
130        self
131    }
132    
133    /// Set defer boot flag
134    pub fn with_defer_boot(mut self, defer_boot: bool) -> Self {
135        self.defer_boot = defer_boot;
136        self
137    }
138    
139    /// Set if provider is optional
140    pub fn with_optional(mut self, is_optional: bool) -> Self {
141        self.is_optional = is_optional;
142        self
143    }
144}
145
146impl ServiceProvider for BaseProvider {
147    fn name(&self) -> &'static str {
148        self.name
149    }
150    
151    fn register(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ProviderError> {
152        // Base provider doesn't register anything by default
153        Ok(builder)
154    }
155    
156    fn dependencies(&self) -> Vec<&'static str> {
157        self.dependencies.clone()
158    }
159    
160    fn defer_boot(&self) -> bool {
161        self.defer_boot
162    }
163    
164    fn version(&self) -> Option<&'static str> {
165        self.version
166    }
167    
168    fn description(&self) -> Option<&'static str> {
169        self.description
170    }
171    
172    fn is_optional(&self) -> bool {
173        self.is_optional
174    }
175}
176
177/// Macro to simplify provider creation
178#[macro_export]
179macro_rules! provider {
180    (
181        name: $name:expr,
182        $(version: $version:expr,)?
183        $(description: $description:expr,)?
184        $(dependencies: [$($dep:expr),* $(,)?],)?
185        $(defer_boot: $defer:expr,)?
186        $(optional: $optional:expr,)?
187        register: |$builder:ident| $register:block
188        $(, boot: |$container:ident| $boot:block)?
189    ) => {
190        {
191            struct CustomProvider;
192            
193            impl $crate::providers::ServiceProvider for CustomProvider {
194                fn name(&self) -> &'static str {
195                    $name
196                }
197                
198                $(fn version(&self) -> Option<&'static str> {
199                    Some($version)
200                })?
201                
202                $(fn description(&self) -> Option<&'static str> {
203                    Some($description)
204                })?
205                
206                $(fn dependencies(&self) -> Vec<&'static str> {
207                    vec![$($dep),*]
208                })?
209                
210                $(fn defer_boot(&self) -> bool {
211                    $defer
212                })?
213                
214                $(fn is_optional(&self) -> bool {
215                    $optional
216                })?
217                
218                fn register(&self, $builder: $crate::container::ContainerBuilder) 
219                    -> Result<$crate::container::ContainerBuilder, $crate::providers::ProviderError> 
220                {
221                    $register
222                }
223                
224                $(fn boot(&self, $container: &$crate::container::Container) 
225                    -> Result<(), $crate::providers::ProviderError> 
226                {
227                    $boot
228                })?
229            }
230            
231            CustomProvider
232        }
233    };
234}
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239    
240    #[test]
241    fn test_provider_metadata() {
242        let base_provider = BaseProvider::new("test_provider")
243            .with_version("1.0.0")
244            .with_description("A test provider")
245            .with_dependencies(vec!["dependency1", "dependency2"])
246            .with_defer_boot(true)
247            .with_optional(false);
248        
249        let metadata = ProviderMetadata::from_provider(&base_provider);
250        
251        assert_eq!(metadata.name, "test_provider");
252        assert_eq!(metadata.version, Some("1.0.0".to_string()));
253        assert_eq!(metadata.description, Some("A test provider".to_string()));
254        assert_eq!(metadata.dependencies, vec!["dependency1", "dependency2"]);
255        assert!(metadata.defer_boot);
256        assert!(!metadata.is_optional);
257    }
258}