elif_core/container/
resolver.rs1use crate::container::descriptor::{ServiceDescriptor, ServiceId};
2use crate::errors::CoreError;
3use std::collections::{HashMap, HashSet, VecDeque};
4
5#[derive(Debug, Clone)]
7pub struct ResolutionPath {
8 pub services: Vec<ServiceId>,
9}
10
11impl Default for ResolutionPath {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl ResolutionPath {
18 pub fn new() -> Self {
20 Self {
21 services: Vec::new(),
22 }
23 }
24
25 pub fn push(&mut self, service_id: ServiceId) {
27 self.services.push(service_id);
28 }
29
30 pub fn pop(&mut self) -> Option<ServiceId> {
32 self.services.pop()
33 }
34
35 pub fn contains(&self, service_id: &ServiceId) -> bool {
37 self.services.contains(service_id)
38 }
39
40 pub fn path_string(&self) -> String {
42 self.services
43 .iter()
44 .map(|id| {
45 format!(
46 "{}({})",
47 id.type_name(),
48 id.name.as_deref().unwrap_or("default")
49 )
50 })
51 .collect::<Vec<_>>()
52 .join(" -> ")
53 }
54}
55
56#[derive(Debug)]
58pub struct DependencyNode {
59 pub service_id: ServiceId,
60 pub dependencies: Vec<ServiceId>,
61 pub dependents: Vec<ServiceId>,
62}
63
64#[derive(Debug)]
66pub struct DependencyGraph {
67 nodes: HashMap<ServiceId, DependencyNode>,
68}
69
70impl DependencyGraph {
71 pub fn new() -> Self {
73 Self {
74 nodes: HashMap::new(),
75 }
76 }
77
78 pub fn build_from_descriptors(descriptors: &[ServiceDescriptor]) -> Self {
80 let mut graph = Self::new();
81
82 for descriptor in descriptors {
84 graph.add_service(&descriptor.service_id, &descriptor.dependencies);
85 }
86
87 graph.build_reverse_dependencies();
89
90 graph
91 }
92
93 pub fn add_service(&mut self, service_id: &ServiceId, dependencies: &[ServiceId]) {
95 let node = DependencyNode {
96 service_id: service_id.clone(),
97 dependencies: dependencies.to_vec(),
98 dependents: Vec::new(),
99 };
100 self.nodes.insert(service_id.clone(), node);
101 }
102
103 fn build_reverse_dependencies(&mut self) {
105 let dependencies: Vec<(ServiceId, Vec<ServiceId>)> = self
106 .nodes
107 .iter()
108 .map(|(id, node)| (id.clone(), node.dependencies.clone()))
109 .collect();
110
111 for (service_id, deps) in dependencies {
112 for dep_id in deps {
113 if let Some(dep_node) = self.nodes.get_mut(&dep_id) {
114 dep_node.dependents.push(service_id.clone());
115 }
116 }
117 }
118 }
119
120 pub fn detect_cycles(&self) -> Result<(), CoreError> {
122 let mut visited = HashSet::new();
123 let mut in_progress = HashSet::new();
124
125 for service_id in self.nodes.keys() {
126 if !visited.contains(service_id) {
127 let mut path = ResolutionPath::new();
128 self.detect_cycle_dfs(service_id, &mut visited, &mut in_progress, &mut path)?;
129 }
130 }
131
132 Ok(())
133 }
134
135 fn detect_cycle_dfs(
137 &self,
138 service_id: &ServiceId,
139 visited: &mut HashSet<ServiceId>,
140 in_progress: &mut HashSet<ServiceId>,
141 path: &mut ResolutionPath,
142 ) -> Result<(), CoreError> {
143 if in_progress.contains(service_id) {
144 path.push(service_id.clone());
145 return Err(CoreError::CircularDependency {
146 path: path.path_string(),
147 cycle_service: format!(
148 "{}({})",
149 service_id.type_name(),
150 service_id.name.as_deref().unwrap_or("default")
151 ),
152 });
153 }
154
155 if visited.contains(service_id) {
156 return Ok(());
157 }
158
159 in_progress.insert(service_id.clone());
160 path.push(service_id.clone());
161
162 if let Some(node) = self.nodes.get(service_id) {
163 for dep_id in &node.dependencies {
164 self.detect_cycle_dfs(dep_id, visited, in_progress, path)?;
165 }
166 }
167
168 path.pop();
169 in_progress.remove(service_id);
170 visited.insert(service_id.clone());
171
172 Ok(())
173 }
174
175 pub fn topological_sort(&self) -> Result<Vec<ServiceId>, CoreError> {
177 self.detect_cycles()?;
178
179 let mut in_degree: HashMap<ServiceId, usize> = HashMap::new();
180 let mut queue = VecDeque::new();
181 let mut result = Vec::new();
182
183 for (service_id, node) in &self.nodes {
185 in_degree.insert(service_id.clone(), node.dependencies.len());
186 if node.dependencies.is_empty() {
187 queue.push_back(service_id.clone());
188 }
189 }
190
191 while let Some(service_id) = queue.pop_front() {
193 result.push(service_id.clone());
194
195 if let Some(node) = self.nodes.get(&service_id) {
196 for dependent_id in &node.dependents {
197 if let Some(degree) = in_degree.get_mut(dependent_id) {
198 *degree -= 1;
199 if *degree == 0 {
200 queue.push_back(dependent_id.clone());
201 }
202 }
203 }
204 }
205 }
206
207 if result.len() != self.nodes.len() {
208 return Err(CoreError::CircularDependency {
209 path: "Complex circular dependency detected".to_string(),
210 cycle_service: "Multiple services".to_string(),
211 });
212 }
213
214 Ok(result)
215 }
216
217 pub fn get_dependencies(&self, service_id: &ServiceId) -> Option<&[ServiceId]> {
219 self.nodes
220 .get(service_id)
221 .map(|node| node.dependencies.as_slice())
222 }
223
224 pub fn get_dependents(&self, service_id: &ServiceId) -> Option<&[ServiceId]> {
226 self.nodes
227 .get(service_id)
228 .map(|node| node.dependents.as_slice())
229 }
230}
231
232impl Default for DependencyGraph {
233 fn default() -> Self {
234 Self::new()
235 }
236}
237
238#[derive(Debug)]
240pub struct DependencyResolver {
241 graph: DependencyGraph,
242 resolution_order: Vec<ServiceId>,
243}
244
245impl DependencyResolver {
246 pub fn new(descriptors: &[ServiceDescriptor]) -> Result<Self, CoreError> {
248 let graph = DependencyGraph::build_from_descriptors(descriptors);
249 let resolution_order = graph.topological_sort()?;
250
251 Ok(Self {
252 graph,
253 resolution_order,
254 })
255 }
256
257 pub fn resolution_order(&self) -> &[ServiceId] {
259 &self.resolution_order
260 }
261
262 pub fn get_dependencies(&self, service_id: &ServiceId) -> Option<&[ServiceId]> {
264 self.graph.get_dependencies(service_id)
265 }
266
267 pub fn validate_dependencies(
269 &self,
270 available_services: &HashSet<ServiceId>,
271 ) -> Result<(), CoreError> {
272 for service_id in &self.resolution_order {
273 if let Some(dependencies) = self.get_dependencies(service_id) {
274 for dep_id in dependencies {
275 if !available_services.contains(dep_id) {
276 return Err(CoreError::ServiceNotFound {
277 service_type: format!(
278 "{}({})",
279 dep_id.type_name(),
280 dep_id.name.as_deref().unwrap_or("default")
281 ),
282 });
283 }
284 }
285 }
286 }
287 Ok(())
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294 use std::any::TypeId;
295
296 #[test]
297 fn test_service_id_creation() {
298 let id1 = ServiceId::of::<String>();
299 let id2 = ServiceId::named::<String>("cache");
300
301 assert_eq!(id1.type_id, TypeId::of::<String>());
302 assert_eq!(id1.name, None);
303
304 assert_eq!(id2.type_id, TypeId::of::<String>());
305 assert_eq!(id2.name, Some("cache".to_string()));
306
307 assert_ne!(id1, id2);
308 }
309
310 #[test]
311 fn test_dependency_graph_cycle_detection() {
312 let mut graph = DependencyGraph::new();
313
314 let service_a = ServiceId::named::<String>("A");
315 let service_b = ServiceId::named::<String>("B");
316 let service_c = ServiceId::named::<String>("C");
317
318 graph.add_service(&service_a, &[service_b.clone()]);
320 graph.add_service(&service_b, &[service_c.clone()]);
321 graph.add_service(&service_c, &[service_a.clone()]);
322
323 graph.build_reverse_dependencies();
324
325 let result = graph.detect_cycles();
326 assert!(result.is_err());
327
328 if let Err(CoreError::CircularDependency { path, .. }) = result {
329 assert!(path.contains("A") && path.contains("B") && path.contains("C"));
330 }
331 }
332
333 #[test]
334 fn test_dependency_graph_topological_sort() {
335 let mut graph = DependencyGraph::new();
336
337 let service_a = ServiceId::named::<String>("A");
338 let service_b = ServiceId::named::<String>("B");
339 let service_c = ServiceId::named::<String>("C");
340
341 graph.add_service(&service_c, &[]);
343 graph.add_service(&service_b, &[service_c.clone()]);
344 graph.add_service(&service_a, &[service_b.clone()]);
345
346 graph.build_reverse_dependencies();
347
348 let sorted = graph.topological_sort().unwrap();
349
350 let pos_c = sorted.iter().position(|id| id == &service_c).unwrap();
352 let pos_b = sorted.iter().position(|id| id == &service_b).unwrap();
353 let pos_a = sorted.iter().position(|id| id == &service_a).unwrap();
354
355 assert!(pos_c < pos_b);
356 assert!(pos_b < pos_a);
357 }
358
359 #[test]
360 fn test_resolution_path() {
361 let mut path = ResolutionPath::new();
362 let service_a = ServiceId::named::<String>("A");
363 let service_b = ServiceId::named::<String>("B");
364
365 path.push(service_a.clone());
366 path.push(service_b.clone());
367
368 assert!(path.contains(&service_a));
369 assert!(path.contains(&service_b));
370
371 let popped = path.pop();
372 assert_eq!(popped, Some(service_b.clone()));
373 assert!(!path.contains(&service_b));
374 assert!(path.contains(&service_a));
375 }
376}