1use crate::auth::{auth_middleware, AuthContext, AuthRequirements};
7use crate::core::{Handler, Route};
8use crate::error::Result;
9use crate::types::{HttpMethod, Request, Response};
10use std::collections::HashMap;
11use std::sync::Arc;
12
13pub type BoxFuture<T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send + 'static>>;
15
16pub struct AuthenticatedRoute {
18 pub route: Route,
19 pub auth_requirements: AuthRequirements,
20 pub auth_context: Arc<AuthContext>,
21}
22
23impl AuthenticatedRoute {
24 pub fn new(
26 route: Route,
27 auth_requirements: AuthRequirements,
28 auth_context: Arc<AuthContext>,
29 ) -> Self {
30 Self {
31 route,
32 auth_requirements,
33 auth_context,
34 }
35 }
36
37 pub async fn execute(&self, request: Request) -> Result<Response> {
39 let _user_session =
41 auth_middleware(&self.auth_context, &self.auth_requirements, &request).await?;
42
43 Ok(Response::ok().body("Authenticated route executed"))
46 }
47}
48
49pub struct MountableInterface {
55 name: String,
56 description: String,
57 routes: Vec<RouteDefinition>,
58 middleware: Vec<String>, mount_options: MountOptions,
60}
61
62pub struct RouteDefinition {
64 pub path: String,
66 pub method: HttpMethod,
68 pub handler_fn: Arc<dyn Fn() -> Route + Send + Sync>,
70 pub auth_requirements: AuthRequirements,
72 pub description: Option<String>,
74 pub tags: Vec<String>,
76}
77
78#[derive(Clone, Debug, Default)]
80pub struct MountOptions {
81 pub strip_prefix: bool,
83 pub add_trailing_slash: bool,
85 pub middleware: Vec<String>,
87}
88
89pub struct InterfaceBuilder {
91 name: String,
92 description: String,
93 routes: Vec<RouteDefinition>,
94 middleware: Vec<String>,
95 mount_options: MountOptions,
96}
97
98impl InterfaceBuilder {
99 pub fn new(name: impl Into<String>) -> Self {
101 Self {
102 name: name.into(),
103 description: String::new(),
104 routes: Vec::new(),
105 middleware: Vec::new(),
106 mount_options: MountOptions::default(),
107 }
108 }
109
110 pub fn description(mut self, description: impl Into<String>) -> Self {
112 self.description = description.into();
113 self
114 }
115
116 pub fn route<H, T>(mut self, path: impl Into<String>, method: HttpMethod, handler: H) -> Self
118 where
119 H: Handler<T> + Clone + Send + Sync + 'static,
120 T: Send + Sync + 'static,
121 {
122 let handler_fn = Arc::new(move || Route::new("", HttpMethod::GET, handler.clone()));
123
124 let route = RouteDefinition {
125 path: path.into(),
126 method,
127 handler_fn,
128 auth_requirements: AuthRequirements::default(),
129 description: None,
130 tags: Vec::new(),
131 };
132 self.routes.push(route);
133 self
134 }
135
136 pub fn route_with_auth<H, T>(
138 mut self,
139 path: impl Into<String>,
140 method: HttpMethod,
141 handler: H,
142 auth_requirements: AuthRequirements,
143 ) -> Self
144 where
145 H: Handler<T> + Clone + Send + Sync + 'static,
146 T: Send + Sync + 'static,
147 {
148 let handler_fn = Arc::new(move || Route::new("", HttpMethod::GET, handler.clone()));
149
150 let route = RouteDefinition {
151 path: path.into(),
152 method,
153 handler_fn,
154 auth_requirements,
155 description: None,
156 tags: Vec::new(),
157 };
158 self.routes.push(route);
159 self
160 }
161
162 pub fn route_with_meta<H, T>(
164 mut self,
165 path: impl Into<String>,
166 method: HttpMethod,
167 handler: H,
168 description: impl Into<String>,
169 tags: Vec<String>,
170 ) -> Self
171 where
172 H: Handler<T> + Clone + Send + Sync + 'static,
173 T: Send + Sync + 'static,
174 {
175 let handler_fn = Arc::new(move || Route::new("", HttpMethod::GET, handler.clone()));
176
177 let route = RouteDefinition {
178 path: path.into(),
179 method,
180 handler_fn,
181 auth_requirements: AuthRequirements::default(),
182 description: Some(description.into()),
183 tags,
184 };
185 self.routes.push(route);
186 self
187 }
188
189 pub fn route_with_full_config<H, T>(
191 mut self,
192 path: impl Into<String>,
193 method: HttpMethod,
194 handler: H,
195 auth_requirements: AuthRequirements,
196 description: impl Into<String>,
197 tags: Vec<String>,
198 ) -> Self
199 where
200 H: Handler<T> + Clone + Send + Sync + 'static,
201 T: Send + Sync + 'static,
202 {
203 let handler_fn = Arc::new(move || Route::new("", HttpMethod::GET, handler.clone()));
204
205 let route = RouteDefinition {
206 path: path.into(),
207 method,
208 handler_fn,
209 auth_requirements,
210 description: Some(description.into()),
211 tags,
212 };
213 self.routes.push(route);
214 self
215 }
216
217 pub fn middleware(mut self, middleware: impl Into<String>) -> Self {
219 self.middleware.push(middleware.into());
220 self
221 }
222
223 pub fn mount_options(mut self, options: MountOptions) -> Self {
225 self.mount_options = options;
226 self
227 }
228
229 pub fn default_auth_requirements(mut self, auth_requirements: AuthRequirements) -> Self {
231 for route in &mut self.routes {
233 if !route.auth_requirements.required && route.auth_requirements.permissions.is_empty() {
234 route.auth_requirements = auth_requirements.clone();
235 }
236 }
237 self
238 }
239
240 pub fn require_auth(mut self) -> Self {
242 let auth_requirements = AuthRequirements::required();
243 for route in &mut self.routes {
244 if !route.auth_requirements.required {
245 route.auth_requirements = auth_requirements.clone();
246 }
247 }
248 self
249 }
250
251 pub fn require_permissions(mut self, permissions: Vec<String>) -> Self {
253 let auth_requirements = AuthRequirements::required().with_permissions(permissions);
254 for route in &mut self.routes {
255 if route.auth_requirements.permissions.is_empty() {
256 route.auth_requirements = auth_requirements.clone();
257 }
258 }
259 self
260 }
261
262 pub fn build(self) -> MountableInterface {
264 MountableInterface {
265 name: self.name,
266 description: self.description,
267 routes: self.routes,
268 middleware: self.middleware,
269 mount_options: self.mount_options,
270 }
271 }
272}
273
274impl MountableInterface {
275 pub fn builder(name: impl Into<String>) -> InterfaceBuilder {
277 InterfaceBuilder::new(name)
278 }
279
280 pub fn name(&self) -> &str {
282 &self.name
283 }
284
285 pub fn description(&self) -> &str {
287 &self.description
288 }
289
290 pub fn routes(&self) -> &[RouteDefinition] {
292 &self.routes
293 }
294
295 pub fn middleware(&self) -> &[String] {
297 &self.middleware
298 }
299
300 pub fn mount_options(&self) -> &MountOptions {
302 &self.mount_options
303 }
304
305 pub fn mount_at(&self, prefix: impl AsRef<str>) -> Result<Vec<Route>> {
308 let prefix = prefix.as_ref();
309 let prefix = if prefix.starts_with('/') {
310 prefix.to_string()
311 } else {
312 format!("/{}", prefix)
313 };
314
315 let mut mounted_routes = Vec::new();
316
317 for route_def in &self.routes {
318 let _full_path = if prefix == "/" {
319 route_def.path.clone()
320 } else {
321 format!("{}{}", prefix, route_def.path)
322 };
323
324 let route = (route_def.handler_fn)();
326 mounted_routes.push(route);
327 }
328
329 Ok(mounted_routes)
330 }
331
332 pub fn mount_with_auth_at(
334 &self,
335 prefix: impl AsRef<str>,
336 auth_context: Arc<AuthContext>,
337 ) -> Result<Vec<AuthenticatedRoute>> {
338 let prefix = prefix.as_ref();
339 let prefix = if prefix.starts_with('/') {
340 prefix.to_string()
341 } else {
342 format!("/{}", prefix)
343 };
344
345 let mut mounted_routes = Vec::new();
346
347 for route_def in &self.routes {
348 let _full_path = if prefix == "/" {
349 route_def.path.clone()
350 } else {
351 format!("{}{}", prefix, route_def.path)
352 };
353
354 let route = (route_def.handler_fn)();
356
357 let authenticated_route = AuthenticatedRoute::new(
359 route,
360 route_def.auth_requirements.clone(),
361 auth_context.clone(),
362 );
363 mounted_routes.push(authenticated_route);
364 }
365
366 Ok(mounted_routes)
367 }
368
369 pub fn openapi_spec(&self) -> OpenApiSpec {
371 OpenApiSpec {
372 interface_name: self.name.clone(),
373 description: self.description.clone(),
374 routes: self
375 .routes
376 .iter()
377 .map(|r| RouteDoc {
378 path: r.path.clone(),
379 method: r.method,
380 description: r.description.clone(),
381 tags: r.tags.clone(),
382 })
383 .collect(),
384 }
385 }
386}
387
388pub struct InterfaceRegistry {
390 interfaces: HashMap<String, MountableInterface>,
391 auth_context: Option<Arc<AuthContext>>,
392}
393
394impl InterfaceRegistry {
395 pub fn new() -> Self {
397 Self {
398 interfaces: HashMap::new(),
399 auth_context: None,
400 }
401 }
402
403 pub fn with_auth(auth_context: Arc<AuthContext>) -> Self {
405 Self {
406 interfaces: HashMap::new(),
407 auth_context: Some(auth_context),
408 }
409 }
410
411 pub fn set_auth_context(&mut self, auth_context: Arc<AuthContext>) {
413 self.auth_context = Some(auth_context);
414 }
415
416 pub fn auth_context(&self) -> Option<&Arc<AuthContext>> {
418 self.auth_context.as_ref()
419 }
420
421 pub fn register(&mut self, interface: MountableInterface) -> Result<()> {
423 let name = interface.name().to_string();
424 if self.interfaces.contains_key(&name) {
425 return Err(crate::error::WebServerError::ConfigError(format!(
426 "Interface '{}' is already registered",
427 name
428 )));
429 }
430 self.interfaces.insert(name, interface);
431 Ok(())
432 }
433
434 pub fn get(&self, name: &str) -> Option<&MountableInterface> {
436 self.interfaces.get(name)
437 }
438
439 pub fn list(&self) -> Vec<&str> {
441 self.interfaces.keys().map(|s| s.as_str()).collect()
442 }
443
444 pub fn mount(
446 &self,
447 interface_name: &str,
448 prefix: impl AsRef<str>,
449 ) -> Result<Vec<AuthenticatedRoute>> {
450 let interface = self.get(interface_name).ok_or_else(|| {
451 crate::error::WebServerError::ConfigError(format!(
452 "Interface '{}' not found",
453 interface_name
454 ))
455 })?;
456
457 let auth_context = self.auth_context.as_ref().ok_or_else(|| {
458 crate::error::WebServerError::ConfigError(
459 "No authentication context available. Use with_auth() or set_auth_context()"
460 .to_string(),
461 )
462 })?;
463
464 interface.mount_with_auth_at(prefix, auth_context.clone())
465 }
466
467 pub fn mount_all_with_auth(&self) -> Result<Vec<AuthenticatedRoute>> {
469 let auth_context = self.auth_context.as_ref().ok_or_else(|| {
470 crate::error::WebServerError::ConfigError(
471 "No authentication context available".to_string(),
472 )
473 })?;
474
475 let mut all_routes = Vec::new();
476
477 for (name, interface) in &self.interfaces {
478 let routes =
479 interface.mount_with_auth_at(format!("/{}", name), auth_context.clone())?;
480 all_routes.extend(routes);
481 }
482
483 Ok(all_routes)
484 }
485
486 pub fn mount_all(&self) -> Result<Vec<AuthenticatedRoute>> {
488 let auth_context = self.auth_context.as_ref().ok_or_else(|| {
489 crate::error::WebServerError::ConfigError(
490 "No authentication context available".to_string(),
491 )
492 })?;
493
494 let mut all_routes = Vec::new();
495
496 for (name, interface) in &self.interfaces {
497 let routes =
498 interface.mount_with_auth_at(format!("/{}", name), auth_context.clone())?;
499 all_routes.extend(routes);
500 }
501
502 Ok(all_routes)
503 }
504}
505
506impl Default for InterfaceRegistry {
507 fn default() -> Self {
508 Self::new()
509 }
510}
511
512#[derive(Debug, Clone)]
514pub struct OpenApiSpec {
515 pub interface_name: String,
516 pub description: String,
517 pub routes: Vec<RouteDoc>,
518}
519
520#[derive(Debug, Clone)]
522pub struct RouteDoc {
523 pub path: String,
524 pub method: HttpMethod,
525 pub description: Option<String>,
526 pub tags: Vec<String>,
527}
528
529#[cfg(test)]
530mod tests {
531 use super::*;
532 use crate::types::Response;
533
534 async fn hello_handler(_req: crate::types::Request) -> Result<Response> {
535 Ok(Response::ok().body("Hello, World!"))
536 }
537
538 async fn status_handler(_req: crate::types::Request) -> Result<Response> {
539 Response::json(&serde_json::json!({"status": "ok"}))
540 }
541
542 #[test]
543 fn test_interface_builder() {
544 let interface = MountableInterface::builder("test-api")
545 .description("Test API interface")
546 .route("/hello", HttpMethod::GET, hello_handler)
547 .route("/status", HttpMethod::GET, status_handler)
548 .middleware("cors")
549 .build();
550
551 assert_eq!(interface.name(), "test-api");
552 assert_eq!(interface.description(), "Test API interface");
553 assert_eq!(interface.routes().len(), 2);
554 assert_eq!(interface.middleware().len(), 1);
555 }
556
557 #[test]
558 fn test_interface_registry() {
559 let mut registry = InterfaceRegistry::new();
560
561 let interface1 = MountableInterface::builder("api-v1")
562 .route("/users", HttpMethod::GET, hello_handler)
563 .build();
564
565 let interface2 = MountableInterface::builder("admin")
566 .route("/status", HttpMethod::GET, status_handler)
567 .build();
568
569 registry.register(interface1).unwrap();
570 registry.register(interface2).unwrap();
571
572 assert_eq!(registry.list().len(), 2);
573 assert!(registry.get("api-v1").is_some());
574 assert!(registry.get("admin").is_some());
575 assert!(registry.get("nonexistent").is_none());
576 }
577
578 #[tokio::test]
579 async fn test_mounting() {
580 let interface = MountableInterface::builder("test")
581 .route("/hello", HttpMethod::GET, hello_handler)
582 .build();
583
584 let routes = interface.mount_at("/api/v1").unwrap();
585 assert_eq!(routes.len(), 1);
586
587 }
590}