omni_director/routing/
resolver.rs

1//! # Route Resolver
2//!
3//! Validates and resolves routes to ensure providers, features, and operations exist.
4
5use super::Route;
6use crate::providers::{ProviderRegistry, ProviderError, ProviderResult};
7use std::sync::Arc;
8
9/// Route resolver validates and resolves routes
10pub struct RouteResolver {
11    /// Provider registry for validation
12    registry: Arc<ProviderRegistry>,
13}
14
15impl RouteResolver {
16    /// Create a new route resolver
17    pub fn new(registry: Arc<ProviderRegistry>) -> Self {
18        Self { registry }
19    }
20
21    /// Resolve and validate a route
22    pub async fn resolve_route(&self, route: &Route) -> ProviderResult<ResolvedRoute> {
23        // Check if provider exists
24        let provider = self.registry
25            .get_provider(&route.provider)
26            .await
27            .ok_or_else(|| ProviderError::NotFound(route.provider.clone()))?;
28
29        // Check if provider supports the feature
30        if !provider.supports_feature(&route.feature) {
31            return Err(ProviderError::FeatureNotSupported {
32                provider: route.provider.clone(),
33                feature: route.feature.clone(),
34            });
35        }
36
37        // Check if feature supports the operation
38        let operations = provider.feature_operations(&route.feature)?;
39        if !operations.contains(&route.operation) {
40            return Err(ProviderError::OperationNotSupported {
41                provider: route.provider.clone(),
42                feature: route.feature.clone(),
43                operation: route.operation.clone(),
44            });
45        }
46
47        Ok(ResolvedRoute {
48            route: route.clone(),
49            provider: provider.name().to_string(),
50            feature: route.feature.clone(),
51            operation: route.operation.clone(),
52            available_operations: operations,
53        })
54    }
55
56    /// List all available routes in the system
57    pub async fn list_all_routes(&self) -> ProviderResult<Vec<Route>> {
58        let mut routes = Vec::new();
59
60        // Get all providers
61        let provider_names = self.registry.list_providers().await;
62
63        for provider_name in provider_names {
64            if let Some(provider) = self.registry.get_provider(&provider_name).await {
65                // Get all features for this provider
66                let features = provider.features();
67
68                for feature in features {
69                    // Get all operations for this feature
70                    if let Ok(operations) = provider.feature_operations(&feature) {
71                        for operation in operations {
72                            routes.push(Route::new(
73                                provider_name.clone(),
74                                feature.clone(),
75                                operation,
76                            ));
77                        }
78                    }
79                }
80            }
81        }
82
83        Ok(routes)
84    }
85
86    /// List routes for a specific provider
87    pub async fn list_provider_routes(&self, provider_name: &str) -> ProviderResult<Vec<Route>> {
88        let provider = self.registry
89            .get_provider(provider_name)
90            .await
91            .ok_or_else(|| ProviderError::NotFound(provider_name.to_string()))?;
92
93        let mut routes = Vec::new();
94        let features = provider.features();
95
96        for feature in features {
97            if let Ok(operations) = provider.feature_operations(&feature) {
98                for operation in operations {
99                    routes.push(Route::new(
100                        provider_name.to_string(),
101                        feature.clone(),
102                        operation,
103                    ));
104                }
105            }
106        }
107
108        Ok(routes)
109    }
110
111    /// List routes for a specific feature
112    pub async fn list_feature_routes(
113        &self,
114        provider_name: &str,
115        feature_name: &str,
116    ) -> ProviderResult<Vec<Route>> {
117        let provider = self.registry
118            .get_provider(provider_name)
119            .await
120            .ok_or_else(|| ProviderError::NotFound(provider_name.to_string()))?;
121
122        if !provider.supports_feature(feature_name) {
123            return Err(ProviderError::FeatureNotSupported {
124                provider: provider_name.to_string(),
125                feature: feature_name.to_string(),
126            });
127        }
128
129        let operations = provider.feature_operations(feature_name)?;
130        let routes = operations
131            .into_iter()
132            .map(|operation| {
133                Route::new(
134                    provider_name.to_string(),
135                    feature_name.to_string(),
136                    operation,
137                )
138            })
139            .collect();
140
141        Ok(routes)
142    }
143
144    /// Find routes by pattern matching
145    pub async fn find_routes_by_pattern(&self, pattern: &RoutePattern) -> ProviderResult<Vec<Route>> {
146        let all_routes = self.list_all_routes().await?;
147
148        let filtered_routes = all_routes
149            .into_iter()
150            .filter(|route| pattern.matches(route))
151            .collect();
152
153        Ok(filtered_routes)
154    }
155
156    /// Suggest similar routes when a route is not found
157    pub async fn suggest_similar_routes(&self, invalid_route: &Route) -> ProviderResult<Vec<Route>> {
158        let all_routes = self.list_all_routes().await?;
159
160        // Find routes with similar provider names
161        let mut suggestions = Vec::new();
162
163        // Exact provider match, different feature/operation
164        for route in &all_routes {
165            if route.provider == invalid_route.provider {
166                suggestions.push(route.clone());
167            }
168        }
169
170        // Similar provider names (basic fuzzy matching)
171        if suggestions.is_empty() {
172            for route in &all_routes {
173                if route.provider.contains(&invalid_route.provider) 
174                   || invalid_route.provider.contains(&route.provider) {
175                    suggestions.push(route.clone());
176                }
177            }
178        }
179
180        // Limit to top 5 suggestions
181        suggestions.truncate(5);
182        Ok(suggestions)
183    }
184}
185
186/// A resolved route with validated components
187#[derive(Debug, Clone)]
188pub struct ResolvedRoute {
189    pub route: Route,
190    pub provider: String,
191    pub feature: String,
192    pub operation: String,
193    pub available_operations: Vec<String>,
194}
195
196/// Pattern for matching routes
197#[derive(Debug, Clone)]
198pub struct RoutePattern {
199    pub provider: Option<String>,
200    pub feature: Option<String>,
201    pub operation: Option<String>,
202}
203
204impl RoutePattern {
205    /// Create a new route pattern
206    pub fn new() -> Self {
207        Self {
208            provider: None,
209            feature: None,
210            operation: None,
211        }
212    }
213
214    /// Set provider pattern
215    pub fn with_provider<S: Into<String>>(mut self, provider: S) -> Self {
216        self.provider = Some(provider.into());
217        self
218    }
219
220    /// Set feature pattern
221    pub fn with_feature<S: Into<String>>(mut self, feature: S) -> Self {
222        self.feature = Some(feature.into());
223        self
224    }
225
226    /// Set operation pattern
227    pub fn with_operation<S: Into<String>>(mut self, operation: S) -> Self {
228        self.operation = Some(operation.into());
229        self
230    }
231
232    /// Check if a route matches this pattern
233    pub fn matches(&self, route: &Route) -> bool {
234        if let Some(ref provider_pattern) = self.provider {
235            if !self.pattern_matches(provider_pattern, &route.provider) {
236                return false;
237            }
238        }
239
240        if let Some(ref feature_pattern) = self.feature {
241            if !self.pattern_matches(feature_pattern, &route.feature) {
242                return false;
243            }
244        }
245
246        if let Some(ref operation_pattern) = self.operation {
247            if !self.pattern_matches(operation_pattern, &route.operation) {
248                return false;
249            }
250        }
251
252        true
253    }
254
255    /// Simple pattern matching (supports * wildcard)
256    fn pattern_matches(&self, pattern: &str, value: &str) -> bool {
257        if pattern == "*" {
258            return true;
259        }
260
261        if pattern.contains('*') {
262            // Simple wildcard matching
263            let parts: Vec<&str> = pattern.split('*').collect();
264            if parts.len() == 2 {
265                let prefix = parts[0];
266                let suffix = parts[1];
267                return value.starts_with(prefix) && value.ends_with(suffix);
268            }
269        }
270
271        pattern == value
272    }
273}
274
275impl Default for RoutePattern {
276    fn default() -> Self {
277        Self::new()
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    #[test]
286    fn test_route_pattern_matching() {
287        let route = Route::new(
288            "virtualbox".to_string(),
289            "vm-management".to_string(),
290            "start-vm".to_string(),
291        );
292
293        // Exact match
294        let pattern = RoutePattern::new()
295            .with_provider("virtualbox")
296            .with_feature("vm-management")
297            .with_operation("start-vm");
298        assert!(pattern.matches(&route));
299
300        // Wildcard provider
301        let pattern = RoutePattern::new()
302            .with_provider("*")
303            .with_feature("vm-management");
304        assert!(pattern.matches(&route));
305
306        // Partial wildcard
307        let pattern = RoutePattern::new()
308            .with_provider("virtual*");
309        assert!(pattern.matches(&route));
310
311        // No match
312        let pattern = RoutePattern::new()
313            .with_provider("aws");
314        assert!(!pattern.matches(&route));
315    }
316}