elif_core/providers/
provider.rs1use crate::container::{Container, ContainerBuilder};
2use crate::errors::CoreError;
3
4#[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
23pub trait ServiceProvider: Send + Sync {
25 fn name(&self) -> &'static str;
27
28 fn register(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ProviderError>;
31
32 fn boot(&self, container: &Container) -> Result<(), ProviderError> {
35 let _ = container; Ok(())
38 }
39
40 fn dependencies(&self) -> Vec<&'static str> {
42 vec![]
43 }
44
45 fn defer_boot(&self) -> bool {
47 false
48 }
49
50 fn version(&self) -> Option<&'static str> {
52 None
53 }
54
55 fn description(&self) -> Option<&'static str> {
57 None
58 }
59
60 fn is_optional(&self) -> bool {
62 true
63 }
64}
65
66#[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 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#[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 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 pub fn with_version(mut self, version: &'static str) -> Self {
117 self.version = Some(version);
118 self
119 }
120
121 pub fn with_description(mut self, description: &'static str) -> Self {
123 self.description = Some(description);
124 self
125 }
126
127 pub fn with_dependencies(mut self, dependencies: Vec<&'static str>) -> Self {
129 self.dependencies = dependencies;
130 self
131 }
132
133 pub fn with_defer_boot(mut self, defer_boot: bool) -> Self {
135 self.defer_boot = defer_boot;
136 self
137 }
138
139 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 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_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}