omni_director/routing/
router.rs1use super::{Route, resolver::RouteResolver};
6use crate::providers::{ProviderRegistry, ProviderError, ProviderResult};
7use std::collections::HashMap;
8use std::sync::Arc;
9use serde_json::Value;
10
11pub struct Router {
13 registry: Arc<ProviderRegistry>,
15 resolver: RouteResolver,
17}
18
19impl Router {
20 pub fn new(registry: Arc<ProviderRegistry>) -> Self {
22 Self {
23 resolver: RouteResolver::new(Arc::clone(®istry)),
24 registry,
25 }
26 }
27
28 pub async fn route_request(
30 &self,
31 route: Route,
32 args: HashMap<String, Value>,
33 ) -> ProviderResult<Value> {
34 self.resolver.resolve_route(&route).await?;
36
37 self.registry
39 .execute_operation(&route.provider, &route.feature, &route.operation, args)
40 .await
41 }
42
43 pub async fn route_path(
45 &self,
46 path: &str,
47 args: HashMap<String, Value>,
48 ) -> ProviderResult<Value> {
49 let route = Route::from_path(path)
50 .map_err(|e| ProviderError::InvalidRoute(e))?;
51
52 self.route_request(route, args).await
53 }
54
55 pub async fn route_url_path(
57 &self,
58 url_path: &str,
59 args: HashMap<String, Value>,
60 ) -> ProviderResult<Value> {
61 let path = if url_path.starts_with("/providers/") {
63 &url_path[11..] } else {
65 url_path
66 };
67
68 let normalized_path = path
70 .replace("/features/", "/")
71 .replace("/operations/", "/");
72
73 self.route_path(&normalized_path, args).await
74 }
75
76 pub async fn get_available_routes(&self) -> ProviderResult<Vec<Route>> {
78 self.resolver.list_all_routes().await
79 }
80
81 pub async fn get_provider_routes(&self, provider: &str) -> ProviderResult<Vec<Route>> {
83 self.resolver.list_provider_routes(provider).await
84 }
85
86 pub async fn get_feature_routes(&self, provider: &str, feature: &str) -> ProviderResult<Vec<Route>> {
88 self.resolver.list_feature_routes(provider, feature).await
89 }
90
91 pub async fn is_route_valid(&self, route: &Route) -> bool {
93 self.resolver.resolve_route(route).await.is_ok()
94 }
95
96 pub async fn get_route_metadata(&self, route: &Route) -> ProviderResult<RouteMetadata> {
98 self.resolver.resolve_route(route).await?;
100
101 let provider_metadata = self.registry
103 .get_metadata(&route.provider)
104 .await
105 .ok_or_else(|| ProviderError::NotFound(route.provider.clone()))?;
106
107 let feature_metadata = provider_metadata
109 .features
110 .iter()
111 .find(|f| f.name == route.feature)
112 .ok_or_else(|| ProviderError::FeatureNotSupported {
113 provider: route.provider.clone(),
114 feature: route.feature.clone(),
115 })?;
116
117 let operation_metadata = feature_metadata
119 .operations
120 .iter()
121 .find(|op| op.name == route.operation)
122 .ok_or_else(|| ProviderError::OperationNotSupported {
123 provider: route.provider.clone(),
124 feature: route.feature.clone(),
125 operation: route.operation.clone(),
126 })?;
127
128 Ok(RouteMetadata {
129 route: route.clone(),
130 provider_name: provider_metadata.name.clone(),
131 provider_version: provider_metadata.version.clone(),
132 feature_name: feature_metadata.name.clone(),
133 feature_description: feature_metadata.description.clone(),
134 operation_name: operation_metadata.name.clone(),
135 operation_description: operation_metadata.description.clone(),
136 operation_arguments: operation_metadata.arguments.clone(),
137 operation_return_type: operation_metadata.return_type.clone(),
138 is_mutating: operation_metadata.is_mutating,
139 estimated_duration_ms: operation_metadata.estimated_duration_ms,
140 })
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct RouteMetadata {
147 pub route: Route,
148 pub provider_name: String,
149 pub provider_version: String,
150 pub feature_name: String,
151 pub feature_description: String,
152 pub operation_name: String,
153 pub operation_description: String,
154 pub operation_arguments: Vec<crate::providers::ArgumentMetadata>,
155 pub operation_return_type: String,
156 pub is_mutating: bool,
157 pub estimated_duration_ms: Option<u64>,
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::providers::{ProviderContext, DefaultProviderContext};
164
165 #[tokio::test]
166 async fn test_route_parsing() {
167 let context = Arc::new(DefaultProviderContext::new());
168 let registry = Arc::new(ProviderRegistry::new(context));
169 let router = Router::new(registry);
170
171 let result = router.route_url_path(
173 "/providers/virtualbox/features/vm-management/operations/start-vm",
174 HashMap::new()
175 ).await;
176
177 assert!(matches!(result, Err(ProviderError::NotFound(_))));
179 }
180
181 #[test]
182 fn test_url_path_normalization() {
183 let path = "/providers/virtualbox/features/vm-management/operations/start-vm";
184 let normalized = path
185 .strip_prefix("/providers/").unwrap_or(path)
186 .replace("/features/", "/")
187 .replace("/operations/", "/");
188
189 assert_eq!(normalized, "virtualbox/vm-management/start-vm");
190 }
191}