omni_director/routing/
resolver.rs1use super::Route;
6use crate::providers::{ProviderRegistry, ProviderError, ProviderResult};
7use std::sync::Arc;
8
9pub struct RouteResolver {
11 registry: Arc<ProviderRegistry>,
13}
14
15impl RouteResolver {
16 pub fn new(registry: Arc<ProviderRegistry>) -> Self {
18 Self { registry }
19 }
20
21 pub async fn resolve_route(&self, route: &Route) -> ProviderResult<ResolvedRoute> {
23 let provider = self.registry
25 .get_provider(&route.provider)
26 .await
27 .ok_or_else(|| ProviderError::NotFound(route.provider.clone()))?;
28
29 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 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 pub async fn list_all_routes(&self) -> ProviderResult<Vec<Route>> {
58 let mut routes = Vec::new();
59
60 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 let features = provider.features();
67
68 for feature in features {
69 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 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 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 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 pub async fn suggest_similar_routes(&self, invalid_route: &Route) -> ProviderResult<Vec<Route>> {
158 let all_routes = self.list_all_routes().await?;
159
160 let mut suggestions = Vec::new();
162
163 for route in &all_routes {
165 if route.provider == invalid_route.provider {
166 suggestions.push(route.clone());
167 }
168 }
169
170 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 suggestions.truncate(5);
182 Ok(suggestions)
183 }
184}
185
186#[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#[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 pub fn new() -> Self {
207 Self {
208 provider: None,
209 feature: None,
210 operation: None,
211 }
212 }
213
214 pub fn with_provider<S: Into<String>>(mut self, provider: S) -> Self {
216 self.provider = Some(provider.into());
217 self
218 }
219
220 pub fn with_feature<S: Into<String>>(mut self, feature: S) -> Self {
222 self.feature = Some(feature.into());
223 self
224 }
225
226 pub fn with_operation<S: Into<String>>(mut self, operation: S) -> Self {
228 self.operation = Some(operation.into());
229 self
230 }
231
232 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 fn pattern_matches(&self, pattern: &str, value: &str) -> bool {
257 if pattern == "*" {
258 return true;
259 }
260
261 if pattern.contains('*') {
262 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 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 let pattern = RoutePattern::new()
302 .with_provider("*")
303 .with_feature("vm-management");
304 assert!(pattern.matches(&route));
305
306 let pattern = RoutePattern::new()
308 .with_provider("virtual*");
309 assert!(pattern.matches(&route));
310
311 let pattern = RoutePattern::new()
313 .with_provider("aws");
314 assert!(!pattern.matches(&route));
315 }
316}