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 {
12 provider: String,
13 dependency: String,
14 },
15
16 #[error("Provider registration failed: {message}")]
17 RegistrationFailed { message: String },
18
19 #[error("Provider boot failed: {message}")]
20 BootFailed { message: String },
21
22 #[error("Container error: {0}")]
23 Container(#[from] CoreError),
24}
25
26pub trait ServiceProvider: Send + Sync {
28 fn name(&self) -> &'static str;
30
31 fn register(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ProviderError>;
34
35 fn boot(&self, container: &Container) -> Result<(), ProviderError> {
38 let _ = container; Ok(())
41 }
42
43 fn dependencies(&self) -> Vec<&'static str> {
45 vec![]
46 }
47
48 fn defer_boot(&self) -> bool {
50 false
51 }
52
53 fn version(&self) -> Option<&'static str> {
55 None
56 }
57
58 fn description(&self) -> Option<&'static str> {
60 None
61 }
62
63 fn is_optional(&self) -> bool {
65 true
66 }
67}
68
69#[derive(Debug, Clone)]
71pub struct ProviderMetadata {
72 pub name: String,
73 pub version: Option<String>,
74 pub description: Option<String>,
75 pub dependencies: Vec<String>,
76 pub defer_boot: bool,
77 pub is_optional: bool,
78}
79
80impl ProviderMetadata {
81 pub fn from_provider<P: ServiceProvider + ?Sized>(provider: &P) -> Self {
83 Self {
84 name: provider.name().to_string(),
85 version: provider.version().map(|v| v.to_string()),
86 description: provider.description().map(|d| d.to_string()),
87 dependencies: provider
88 .dependencies()
89 .iter()
90 .map(|d| d.to_string())
91 .collect(),
92 defer_boot: provider.defer_boot(),
93 is_optional: provider.is_optional(),
94 }
95 }
96}
97
98#[derive(Debug)]
100pub struct BaseProvider {
101 name: &'static str,
102 version: Option<&'static str>,
103 description: Option<&'static str>,
104 dependencies: Vec<&'static str>,
105 defer_boot: bool,
106 is_optional: bool,
107}
108
109impl BaseProvider {
110 pub fn new(name: &'static str) -> Self {
112 Self {
113 name,
114 version: None,
115 description: None,
116 dependencies: Vec::new(),
117 defer_boot: false,
118 is_optional: true,
119 }
120 }
121
122 pub fn with_version(mut self, version: &'static str) -> Self {
124 self.version = Some(version);
125 self
126 }
127
128 pub fn with_description(mut self, description: &'static str) -> Self {
130 self.description = Some(description);
131 self
132 }
133
134 pub fn with_dependencies(mut self, dependencies: Vec<&'static str>) -> Self {
136 self.dependencies = dependencies;
137 self
138 }
139
140 pub fn with_defer_boot(mut self, defer_boot: bool) -> Self {
142 self.defer_boot = defer_boot;
143 self
144 }
145
146 pub fn with_optional(mut self, is_optional: bool) -> Self {
148 self.is_optional = is_optional;
149 self
150 }
151}
152
153impl ServiceProvider for BaseProvider {
154 fn name(&self) -> &'static str {
155 self.name
156 }
157
158 fn register(&self, builder: ContainerBuilder) -> Result<ContainerBuilder, ProviderError> {
159 Ok(builder)
161 }
162
163 fn dependencies(&self) -> Vec<&'static str> {
164 self.dependencies.clone()
165 }
166
167 fn defer_boot(&self) -> bool {
168 self.defer_boot
169 }
170
171 fn version(&self) -> Option<&'static str> {
172 self.version
173 }
174
175 fn description(&self) -> Option<&'static str> {
176 self.description
177 }
178
179 fn is_optional(&self) -> bool {
180 self.is_optional
181 }
182}
183
184#[macro_export]
186macro_rules! provider {
187 (
188 name: $name:expr,
189 $(version: $version:expr,)?
190 $(description: $description:expr,)?
191 $(dependencies: [$($dep:expr),* $(,)?],)?
192 $(defer_boot: $defer:expr,)?
193 $(optional: $optional:expr,)?
194 register: |$builder:ident| $register:block
195 $(, boot: |$container:ident| $boot:block)?
196 ) => {
197 {
198 struct CustomProvider;
199
200 impl $crate::providers::ServiceProvider for CustomProvider {
201 fn name(&self) -> &'static str {
202 $name
203 }
204
205 $(fn version(&self) -> Option<&'static str> {
206 Some($version)
207 })?
208
209 $(fn description(&self) -> Option<&'static str> {
210 Some($description)
211 })?
212
213 $(fn dependencies(&self) -> Vec<&'static str> {
214 vec![$($dep),*]
215 })?
216
217 $(fn defer_boot(&self) -> bool {
218 $defer
219 })?
220
221 $(fn is_optional(&self) -> bool {
222 $optional
223 })?
224
225 fn register(&self, $builder: $crate::container::ContainerBuilder)
226 -> Result<$crate::container::ContainerBuilder, $crate::providers::ProviderError>
227 {
228 $register
229 }
230
231 $(fn boot(&self, $container: &$crate::container::Container)
232 -> Result<(), $crate::providers::ProviderError>
233 {
234 $boot
235 })?
236 }
237
238 CustomProvider
239 }
240 };
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_provider_metadata() {
249 let base_provider = BaseProvider::new("test_provider")
250 .with_version("1.0.0")
251 .with_description("A test provider")
252 .with_dependencies(vec!["dependency1", "dependency2"])
253 .with_defer_boot(true)
254 .with_optional(false);
255
256 let metadata = ProviderMetadata::from_provider(&base_provider);
257
258 assert_eq!(metadata.name, "test_provider");
259 assert_eq!(metadata.version, Some("1.0.0".to_string()));
260 assert_eq!(metadata.description, Some("A test provider".to_string()));
261 assert_eq!(metadata.dependencies, vec!["dependency1", "dependency2"]);
262 assert!(metadata.defer_boot);
263 assert!(!metadata.is_optional);
264 }
265}