Skip to main content

allframe_core/router/
mod.rs

1//! # Protocol-Agnostic Router
2//!
3//! Write handlers once, expose them via REST, GraphQL, and gRPC.
4//!
5//! This is AllFrame's core differentiator - the same handler can serve
6//! multiple protocols without code changes.
7//!
8//! ## Quick Start
9//!
10//! ```rust
11//! use allframe_core::router::{Router, RestAdapter, GraphQLAdapter, GrpcAdapter};
12//!
13//! // Create router and register handlers
14//! let mut router = Router::new();
15//! router.register("get_user", || async {
16//!     r#"{"id": 42, "name": "Alice"}"#.to_string()
17//! });
18//!
19//! // Expose via REST
20//! let mut rest = RestAdapter::new();
21//! rest.route("GET", "/users/:id", "get_user");
22//!
23//! // Expose via GraphQL
24//! let mut graphql = GraphQLAdapter::new();
25//! graphql.query("user", "get_user");
26//!
27//! // Expose via gRPC
28//! let mut grpc = GrpcAdapter::new();
29//! grpc.unary("UserService", "GetUser", "get_user");
30//! ```
31//!
32//! ## Key Types
33//!
34//! - `Router` - Central handler registry
35//! - `RestAdapter` - REST protocol adapter
36//! - `GraphQLAdapter` - GraphQL protocol adapter
37//! - `GrpcAdapter` - gRPC protocol adapter
38//! - `ProtocolAdapter` - Trait for custom protocol adapters
39//!
40//! ## API Documentation
41//!
42//! Generate beautiful API documentation automatically:
43//!
44//! - `scalar_html` - Scalar UI for REST APIs (<50KB)
45//! - `graphiql_html` - GraphiQL playground for GraphQL
46//! - `grpc_explorer_html` - gRPC Explorer for gRPC services
47//! - `OpenApiGenerator` - OpenAPI 3.1 spec generation
48//!
49//! ## Configuration-Driven Protocol Selection
50//!
51//! Use TOML configuration to select protocols without code changes:
52//!
53//! ```toml
54//! [server]
55//! protocols = ["rest", "graphql", "grpc"]
56//!
57//! [server.rest]
58//! port = 8080
59//!
60//! [server.graphql]
61//! port = 8081
62//! ```
63
64use serde::de::DeserializeOwned;
65use std::{any::Any, collections::HashMap, future::Future, sync::Arc};
66
67pub mod adapter;
68pub mod builder;
69#[cfg(feature = "router")]
70pub mod config;
71pub mod contract;
72pub mod docs;
73pub mod graphiql;
74pub mod graphql;
75pub mod grpc;
76pub mod grpc_explorer;
77pub mod handler;
78pub mod metadata;
79pub mod method;
80pub mod openapi;
81pub mod rest;
82pub mod scalar;
83pub mod schema;
84
85// Production adapters (optional features)
86#[cfg(feature = "router-graphql")]
87pub mod graphql_prod;
88#[cfg(feature = "router-grpc")]
89pub mod grpc_prod;
90
91pub use adapter::ProtocolAdapter;
92pub use builder::RouteBuilder;
93#[cfg(feature = "router")]
94pub use config::{GraphQLConfig, GrpcConfig, RestConfig, RouterConfig, ServerConfig};
95pub use contract::{
96    ContractTestConfig, ContractTestResult, ContractTestResults, ContractTestable, ContractTester,
97};
98pub use docs::DocsConfig;
99pub use graphiql::{graphiql_html, GraphiQLConfig, GraphiQLTheme};
100pub use graphql::{GraphQLAdapter, GraphQLOperation, OperationType};
101// Re-export production adapters when features are enabled
102#[cfg(feature = "router-graphql")]
103pub use graphql_prod::GraphQLProductionAdapter;
104pub use grpc::{GrpcAdapter, GrpcMethod, GrpcMethodType, GrpcRequest, GrpcStatus};
105pub use grpc_explorer::{grpc_explorer_html, GrpcExplorerConfig, GrpcExplorerTheme};
106#[cfg(feature = "router-grpc")]
107pub use grpc_prod::{protobuf, status, streaming, GrpcProductionAdapter, GrpcService};
108pub use handler::{Handler, HandlerFn, HandlerWithArgs, HandlerWithState, HandlerWithStateOnly, State};
109pub use metadata::RouteMetadata;
110pub use method::Method;
111pub use openapi::{OpenApiGenerator, OpenApiServer};
112pub use rest::{RestAdapter, RestRequest, RestResponse, RestRoute};
113pub use scalar::{scalar_html, ScalarConfig, ScalarLayout, ScalarTheme};
114pub use schema::ToJsonSchema;
115
116/// Router manages handler registration and protocol adapters
117///
118/// The router allows you to register handlers once and expose them via
119/// multiple protocols based on configuration.
120pub struct Router {
121    handlers: HashMap<String, Box<dyn Handler>>,
122    adapters: HashMap<String, Box<dyn ProtocolAdapter>>,
123    routes: Vec<RouteMetadata>,
124    state: Option<Arc<dyn Any + Send + Sync>>,
125    #[cfg(feature = "router")]
126    #[allow(dead_code)]
127    config: Option<RouterConfig>,
128}
129
130impl Router {
131    /// Create a new router
132    pub fn new() -> Self {
133        Self {
134            handlers: HashMap::new(),
135            adapters: HashMap::new(),
136            routes: Vec::new(),
137            state: None,
138            #[cfg(feature = "router")]
139            config: None,
140        }
141    }
142
143    /// Create a new router with configuration
144    #[cfg(feature = "router")]
145    pub fn with_config(config: RouterConfig) -> Self {
146        let mut router = Self {
147            handlers: HashMap::new(),
148            adapters: HashMap::new(),
149            routes: Vec::new(),
150            state: None,
151            config: Some(config.clone()),
152        };
153
154        // Auto-register adapters based on config
155        if config.has_protocol("rest") {
156            router.add_adapter(Box::new(RestAdapter::new()));
157        }
158        if config.has_protocol("graphql") {
159            router.add_adapter(Box::new(GraphQLAdapter::new()));
160        }
161        if config.has_protocol("grpc") {
162            router.add_adapter(Box::new(GrpcAdapter::new()));
163        }
164
165        router
166    }
167
168    /// Set shared state for dependency injection (builder pattern)
169    pub fn with_state<S: Send + Sync + 'static>(mut self, state: S) -> Self {
170        self.state = Some(Arc::new(state));
171        self
172    }
173
174    /// Register a handler with a name (zero-arg, backward compatible)
175    pub fn register<F, Fut>(&mut self, name: &str, handler: F)
176    where
177        F: Fn() -> Fut + Send + Sync + 'static,
178        Fut: Future<Output = String> + Send + 'static,
179    {
180        self.handlers
181            .insert(name.to_string(), Box::new(HandlerFn::new(handler)));
182    }
183
184    /// Register a handler that receives typed, deserialized args
185    pub fn register_with_args<T, F, Fut>(&mut self, name: &str, handler: F)
186    where
187        T: DeserializeOwned + Send + 'static,
188        F: Fn(T) -> Fut + Send + Sync + 'static,
189        Fut: Future<Output = String> + Send + 'static,
190    {
191        self.handlers
192            .insert(name.to_string(), Box::new(HandlerWithArgs::new(handler)));
193    }
194
195    /// Register a handler that receives injected state and typed args
196    ///
197    /// # Panics
198    ///
199    /// Panics at call-time if `with_state` was not called, or if the state
200    /// type does not match `S`.
201    pub fn register_with_state<S, T, F, Fut>(&mut self, name: &str, handler: F)
202    where
203        S: Send + Sync + 'static,
204        T: DeserializeOwned + Send + 'static,
205        F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
206        Fut: Future<Output = String> + Send + 'static,
207    {
208        let state = self
209            .state
210            .clone()
211            .expect("register_with_state requires with_state to be called first");
212        self.handlers
213            .insert(name.to_string(), Box::new(HandlerWithState::new(handler, state)));
214    }
215
216    /// Register a handler that receives only injected state (no args)
217    ///
218    /// # Panics
219    ///
220    /// Panics at call-time if `with_state` was not called, or if the state
221    /// type does not match `S`.
222    pub fn register_with_state_only<S, F, Fut>(&mut self, name: &str, handler: F)
223    where
224        S: Send + Sync + 'static,
225        F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
226        Fut: Future<Output = String> + Send + 'static,
227    {
228        let state = self
229            .state
230            .clone()
231            .expect("register_with_state_only requires with_state to be called first");
232        self.handlers
233            .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(handler, state)));
234    }
235
236    /// Get the number of registered handlers
237    pub fn handlers_count(&self) -> usize {
238        self.handlers.len()
239    }
240
241    /// Add a protocol adapter
242    pub fn add_adapter(&mut self, adapter: Box<dyn ProtocolAdapter>) {
243        self.adapters.insert(adapter.name().to_string(), adapter);
244    }
245
246    /// Check if an adapter is registered
247    pub fn has_adapter(&self, name: &str) -> bool {
248        self.adapters.contains_key(name)
249    }
250
251    /// Get an adapter by name
252    pub fn get_adapter(&self, name: &str) -> Option<&dyn ProtocolAdapter> {
253        self.adapters.get(name).map(|b| &**b)
254    }
255
256    /// Route a request through the appropriate protocol adapter
257    pub async fn route_request(&self, protocol: &str, request: &str) -> Result<String, String> {
258        let adapter = self
259            .get_adapter(protocol)
260            .ok_or_else(|| format!("Adapter not found: {}", protocol))?;
261
262        adapter.handle(request).await
263    }
264
265    /// Execute a handler by name (zero-arg shorthand)
266    pub async fn execute(&self, name: &str) -> Result<String, String> {
267        self.execute_with_args(name, "{}").await
268    }
269
270    /// Execute a handler by name with JSON args
271    pub async fn execute_with_args(&self, name: &str, args: &str) -> Result<String, String> {
272        match self.handlers.get(name) {
273            Some(handler) => handler.call(args).await,
274            None => Err(format!("Handler '{}' not found", name)),
275        }
276    }
277
278    /// List all registered handler names
279    ///
280    /// Returns a vector of all handler names that have been registered
281    /// with this router. Used by MCP server for tool discovery.
282    pub fn list_handlers(&self) -> Vec<String> {
283        self.handlers.keys().cloned().collect()
284    }
285
286    /// Call a handler by name with request data (JSON args)
287    ///
288    /// Forwards the request string as args to the handler.
289    /// Used by MCP server and Tauri plugin.
290    pub async fn call_handler(&self, name: &str, request: &str) -> Result<String, String> {
291        self.execute_with_args(name, request).await
292    }
293
294    /// Check if handler can be called via REST
295    pub fn can_handle_rest(&self, _name: &str) -> bool {
296        self.has_adapter("rest")
297    }
298
299    /// Check if handler can be called via GraphQL
300    pub fn can_handle_graphql(&self, _name: &str) -> bool {
301        self.has_adapter("graphql")
302    }
303
304    /// Check if handler can be called via gRPC
305    pub fn can_handle_grpc(&self, _name: &str) -> bool {
306        self.has_adapter("grpc")
307    }
308
309    /// Get list of enabled protocols
310    pub fn enabled_protocols(&self) -> Vec<String> {
311        self.adapters.keys().cloned().collect()
312    }
313
314    /// Add a route with metadata
315    ///
316    /// This stores route metadata that can be used to generate
317    /// documentation (OpenAPI, GraphQL schemas, gRPC reflection).
318    pub fn add_route(&mut self, metadata: RouteMetadata) {
319        self.routes.push(metadata);
320    }
321
322    /// Get all registered routes
323    ///
324    /// Returns an immutable reference to all route metadata.
325    /// This is used for documentation generation.
326    pub fn routes(&self) -> &[RouteMetadata] {
327        &self.routes
328    }
329
330    /// Register a GET route
331    ///
332    /// This is a convenience method that registers both a handler and route
333    /// metadata for a GET request. The handler name is automatically
334    /// generated as "GET:{path}".
335    pub fn get<F, Fut>(&mut self, path: &str, handler: F)
336    where
337        F: Fn() -> Fut + Send + Sync + 'static,
338        Fut: Future<Output = String> + Send + 'static,
339    {
340        let handler_name = format!("GET:{}", path);
341        self.register(&handler_name, handler);
342        self.add_route(RouteMetadata::new(path, Method::GET, "rest"));
343    }
344
345    /// Register a POST route
346    ///
347    /// This is a convenience method that registers both a handler and route
348    /// metadata for a POST request. The handler name is automatically
349    /// generated as "POST:{path}".
350    pub fn post<F, Fut>(&mut self, path: &str, handler: F)
351    where
352        F: Fn() -> Fut + Send + Sync + 'static,
353        Fut: Future<Output = String> + Send + 'static,
354    {
355        let handler_name = format!("POST:{}", path);
356        self.register(&handler_name, handler);
357        self.add_route(RouteMetadata::new(path, Method::POST, "rest"));
358    }
359
360    /// Register a PUT route
361    ///
362    /// This is a convenience method that registers both a handler and route
363    /// metadata for a PUT request. The handler name is automatically
364    /// generated as "PUT:{path}".
365    pub fn put<F, Fut>(&mut self, path: &str, handler: F)
366    where
367        F: Fn() -> Fut + Send + Sync + 'static,
368        Fut: Future<Output = String> + Send + 'static,
369    {
370        let handler_name = format!("PUT:{}", path);
371        self.register(&handler_name, handler);
372        self.add_route(RouteMetadata::new(path, Method::PUT, "rest"));
373    }
374
375    /// Register a DELETE route
376    ///
377    /// This is a convenience method that registers both a handler and route
378    /// metadata for a DELETE request. The handler name is automatically
379    /// generated as "DELETE:{path}".
380    pub fn delete<F, Fut>(&mut self, path: &str, handler: F)
381    where
382        F: Fn() -> Fut + Send + Sync + 'static,
383        Fut: Future<Output = String> + Send + 'static,
384    {
385        let handler_name = format!("DELETE:{}", path);
386        self.register(&handler_name, handler);
387        self.add_route(RouteMetadata::new(path, Method::DELETE, "rest"));
388    }
389
390    /// Register a PATCH route
391    ///
392    /// This is a convenience method that registers both a handler and route
393    /// metadata for a PATCH request. The handler name is automatically
394    /// generated as "PATCH:{path}".
395    pub fn patch<F, Fut>(&mut self, path: &str, handler: F)
396    where
397        F: Fn() -> Fut + Send + Sync + 'static,
398        Fut: Future<Output = String> + Send + 'static,
399    {
400        let handler_name = format!("PATCH:{}", path);
401        self.register(&handler_name, handler);
402        self.add_route(RouteMetadata::new(path, Method::PATCH, "rest"));
403    }
404
405    /// Register a HEAD route
406    ///
407    /// This is a convenience method that registers both a handler and route
408    /// metadata for a HEAD request. The handler name is automatically
409    /// generated as "HEAD:{path}".
410    pub fn head<F, Fut>(&mut self, path: &str, handler: F)
411    where
412        F: Fn() -> Fut + Send + Sync + 'static,
413        Fut: Future<Output = String> + Send + 'static,
414    {
415        let handler_name = format!("HEAD:{}", path);
416        self.register(&handler_name, handler);
417        self.add_route(RouteMetadata::new(path, Method::HEAD, "rest"));
418    }
419
420    /// Register an OPTIONS route
421    ///
422    /// This is a convenience method that registers both a handler and route
423    /// metadata for an OPTIONS request. The handler name is automatically
424    /// generated as "OPTIONS:{path}".
425    pub fn options<F, Fut>(&mut self, path: &str, handler: F)
426    where
427        F: Fn() -> Fut + Send + Sync + 'static,
428        Fut: Future<Output = String> + Send + 'static,
429    {
430        let handler_name = format!("OPTIONS:{}", path);
431        self.register(&handler_name, handler);
432        self.add_route(RouteMetadata::new(path, Method::OPTIONS, "rest"));
433    }
434
435    /// Call handler via REST
436    pub async fn call_rest(&self, method: &str, path: &str) -> Result<String, String> {
437        let adapter = self
438            .adapters
439            .get("rest")
440            .ok_or_else(|| "REST adapter not enabled".to_string())?;
441
442        let request = format!("{} {}", method, path);
443        adapter.handle(&request).await
444    }
445
446    /// Call handler via GraphQL
447    pub async fn call_graphql(&self, query: &str) -> Result<String, String> {
448        let adapter = self
449            .adapters
450            .get("graphql")
451            .ok_or_else(|| "GraphQL adapter not enabled".to_string())?;
452
453        adapter.handle(query).await
454    }
455
456    /// Call handler via gRPC
457    pub async fn call_grpc(&self, method: &str, request: &str) -> Result<String, String> {
458        let adapter = self
459            .adapters
460            .get("grpc")
461            .ok_or_else(|| "gRPC adapter not enabled".to_string())?;
462
463        let grpc_request = format!("{}:{}", method, request);
464        adapter.handle(&grpc_request).await
465    }
466
467    /// Generate Scalar documentation HTML with default configuration
468    ///
469    /// This is a convenience method that generates a complete HTML page
470    /// with Scalar UI for interactive API documentation.
471    ///
472    /// # Arguments
473    ///
474    /// * `title` - API title
475    /// * `version` - API version
476    ///
477    /// # Example
478    ///
479    /// ```rust
480    /// use allframe_core::router::Router;
481    ///
482    /// let mut router = Router::new();
483    /// router.get("/users", || async { "Users".to_string() });
484    ///
485    /// let html = router.scalar("My API", "1.0.0");
486    /// // Serve this HTML at /docs endpoint
487    /// ```
488    pub fn scalar(&self, title: &str, version: &str) -> String {
489        let config = scalar::ScalarConfig::default();
490        self.scalar_docs(config, title, version)
491    }
492
493    /// Generate Scalar documentation HTML with custom configuration
494    ///
495    /// This method allows full customization of the Scalar UI appearance
496    /// and behavior.
497    ///
498    /// # Arguments
499    ///
500    /// * `config` - Scalar configuration
501    /// * `title` - API title
502    /// * `version` - API version
503    ///
504    /// # Example
505    ///
506    /// ```rust
507    /// use allframe_core::router::{Router, ScalarConfig, ScalarTheme};
508    ///
509    /// let mut router = Router::new();
510    /// router.get("/users", || async { "Users".to_string() });
511    ///
512    /// let config = ScalarConfig::new()
513    ///     .theme(ScalarTheme::Light)
514    ///     .show_sidebar(false);
515    ///
516    /// let html = router.scalar_docs(config, "My API", "1.0.0");
517    /// ```
518    pub fn scalar_docs(&self, config: scalar::ScalarConfig, title: &str, version: &str) -> String {
519        // Generate OpenAPI spec
520        let spec = OpenApiGenerator::new(title, version).generate(self);
521        let spec_json = serde_json::to_string(&spec).unwrap_or_else(|_| "{}".to_string());
522
523        // Generate Scalar HTML
524        scalar::scalar_html(&config, title, &spec_json)
525    }
526}
527
528impl Default for Router {
529    fn default() -> Self {
530        Self::new()
531    }
532}
533
534#[cfg(test)]
535mod tests {
536    use super::*;
537
538    #[tokio::test]
539    async fn test_router_creation() {
540        let router = Router::new();
541        assert_eq!(router.handlers_count(), 0);
542    }
543
544    #[tokio::test]
545    async fn test_handler_registration() {
546        let mut router = Router::new();
547        router.register("test", || async { "Hello".to_string() });
548        assert_eq!(router.handlers_count(), 1);
549    }
550
551    #[tokio::test]
552    async fn test_handler_execution() {
553        let mut router = Router::new();
554        router.register("test", || async { "Hello".to_string() });
555        let result = router.execute("test").await;
556        assert_eq!(result, Ok("Hello".to_string()));
557    }
558
559    // New tests for route metadata tracking
560    #[tokio::test]
561    async fn test_router_starts_with_no_routes() {
562        let router = Router::new();
563        let routes = router.routes();
564        assert_eq!(routes.len(), 0);
565    }
566
567    #[tokio::test]
568    async fn test_add_route_metadata() {
569        let mut router = Router::new();
570        let metadata = RouteMetadata::new("/users", "GET", "rest");
571
572        router.add_route(metadata.clone());
573
574        let routes = router.routes();
575        assert_eq!(routes.len(), 1);
576        assert_eq!(routes[0].path, "/users");
577        assert_eq!(routes[0].method, "GET");
578        assert_eq!(routes[0].protocol, "rest");
579    }
580
581    #[tokio::test]
582    async fn test_add_multiple_routes() {
583        let mut router = Router::new();
584
585        router.add_route(RouteMetadata::new("/users", "GET", "rest"));
586        router.add_route(RouteMetadata::new("/users", "POST", "rest"));
587        router.add_route(RouteMetadata::new("/posts", "GET", "rest"));
588
589        let routes = router.routes();
590        assert_eq!(routes.len(), 3);
591    }
592
593    #[tokio::test]
594    async fn test_routes_with_different_protocols() {
595        let mut router = Router::new();
596
597        router.add_route(RouteMetadata::new("/users", "GET", "rest"));
598        router.add_route(RouteMetadata::new("users", "query", "graphql"));
599        router.add_route(RouteMetadata::new("UserService.GetUser", "unary", "grpc"));
600
601        let routes = router.routes();
602        assert_eq!(routes.len(), 3);
603
604        assert_eq!(routes[0].protocol, "rest");
605        assert_eq!(routes[1].protocol, "graphql");
606        assert_eq!(routes[2].protocol, "grpc");
607    }
608
609    #[tokio::test]
610    async fn test_routes_returns_immutable_reference() {
611        let mut router = Router::new();
612        router.add_route(RouteMetadata::new("/test", "GET", "rest"));
613
614        let routes1 = router.routes();
615        let routes2 = router.routes();
616
617        // Both should have the same data
618        assert_eq!(routes1.len(), routes2.len());
619        assert_eq!(routes1[0].path, routes2[0].path);
620    }
621
622    // Tests for type-safe route registration
623    #[tokio::test]
624    async fn test_route_get_method() {
625        let mut router = Router::new();
626        router.get("/users", || async { "User list".to_string() });
627
628        let routes = router.routes();
629        assert_eq!(routes.len(), 1);
630        assert_eq!(routes[0].path, "/users");
631        assert_eq!(routes[0].method, "GET");
632        assert_eq!(routes[0].protocol, "rest");
633    }
634
635    #[tokio::test]
636    async fn test_route_post_method() {
637        let mut router = Router::new();
638        router.post("/users", || async { "User created".to_string() });
639
640        let routes = router.routes();
641        assert_eq!(routes.len(), 1);
642        assert_eq!(routes[0].path, "/users");
643        assert_eq!(routes[0].method, "POST");
644        assert_eq!(routes[0].protocol, "rest");
645    }
646
647    #[tokio::test]
648    async fn test_route_put_method() {
649        let mut router = Router::new();
650        router.put("/users/1", || async { "User updated".to_string() });
651
652        let routes = router.routes();
653        assert_eq!(routes.len(), 1);
654        assert_eq!(routes[0].method, "PUT");
655    }
656
657    #[tokio::test]
658    async fn test_route_delete_method() {
659        let mut router = Router::new();
660        router.delete("/users/1", || async { "User deleted".to_string() });
661
662        let routes = router.routes();
663        assert_eq!(routes.len(), 1);
664        assert_eq!(routes[0].method, "DELETE");
665    }
666
667    #[tokio::test]
668    async fn test_route_patch_method() {
669        let mut router = Router::new();
670        router.patch("/users/1", || async { "User patched".to_string() });
671
672        let routes = router.routes();
673        assert_eq!(routes.len(), 1);
674        assert_eq!(routes[0].method, "PATCH");
675    }
676
677    #[tokio::test]
678    async fn test_multiple_routes_different_methods() {
679        let mut router = Router::new();
680        router.get("/users", || async { "List".to_string() });
681        router.post("/users", || async { "Create".to_string() });
682        router.put("/users/1", || async { "Update".to_string() });
683        router.delete("/users/1", || async { "Delete".to_string() });
684
685        let routes = router.routes();
686        assert_eq!(routes.len(), 4);
687
688        assert_eq!(routes[0].method, "GET");
689        assert_eq!(routes[1].method, "POST");
690        assert_eq!(routes[2].method, "PUT");
691        assert_eq!(routes[3].method, "DELETE");
692    }
693
694    #[tokio::test]
695    async fn test_route_method_with_path_params() {
696        let mut router = Router::new();
697        router.get("/users/{id}", || async { "User details".to_string() });
698        router.get("/users/{id}/posts/{post_id}", || async {
699            "Post details".to_string()
700        });
701
702        let routes = router.routes();
703        assert_eq!(routes.len(), 2);
704        assert_eq!(routes[0].path, "/users/{id}");
705        assert_eq!(routes[1].path, "/users/{id}/posts/{post_id}");
706    }
707
708    #[tokio::test]
709    async fn test_route_registration_and_execution() {
710        let mut router = Router::new();
711        router.get("/test", || async { "GET response".to_string() });
712        router.post("/test", || async { "POST response".to_string() });
713
714        // Both route metadata and handler should be registered
715        assert_eq!(router.routes().len(), 2);
716        assert_eq!(router.handlers_count(), 2);
717
718        // Handlers should be executable
719        let result1 = router.execute("GET:/test").await;
720        let result2 = router.execute("POST:/test").await;
721
722        assert_eq!(result1, Ok("GET response".to_string()));
723        assert_eq!(result2, Ok("POST response".to_string()));
724    }
725
726    // Tests for Scalar integration
727    #[tokio::test]
728    async fn test_scalar_generates_html() {
729        let mut router = Router::new();
730        router.get("/users", || async { "Users".to_string() });
731
732        let html = router.scalar("Test API", "1.0.0");
733
734        assert!(html.contains("<!DOCTYPE html>"));
735        assert!(html.contains("<title>Test API - API Documentation</title>"));
736        assert!(html.contains("@scalar/api-reference"));
737    }
738
739    #[tokio::test]
740    async fn test_scalar_contains_openapi_spec() {
741        let mut router = Router::new();
742        router.get("/users", || async { "Users".to_string() });
743        router.post("/users", || async { "User created".to_string() });
744
745        let html = router.scalar("Test API", "1.0.0");
746
747        // Should contain the OpenAPI spec
748        assert!(html.contains("openapi"));
749        assert!(html.contains("Test API"));
750        assert!(html.contains("1.0.0"));
751    }
752
753    #[tokio::test]
754    async fn test_scalar_docs_with_custom_config() {
755        let mut router = Router::new();
756        router.get("/users", || async { "Users".to_string() });
757
758        let config = scalar::ScalarConfig::new()
759            .theme(scalar::ScalarTheme::Light)
760            .show_sidebar(false);
761
762        let html = router.scalar_docs(config, "Custom API", "2.0.0");
763
764        assert!(html.contains("Custom API"));
765        assert!(html.contains(r#""theme":"light""#));
766        assert!(html.contains(r#""showSidebar":false"#));
767    }
768
769    #[tokio::test]
770    async fn test_scalar_docs_with_custom_css() {
771        let mut router = Router::new();
772        router.get("/test", || async { "Test".to_string() });
773
774        let config = scalar::ScalarConfig::new().custom_css("body { font-family: 'Inter'; }");
775
776        let html = router.scalar_docs(config, "API", "1.0");
777
778        assert!(html.contains("<style>body { font-family: 'Inter'; }</style>"));
779    }
780
781    #[tokio::test]
782    async fn test_scalar_with_multiple_routes() {
783        let mut router = Router::new();
784        router.get("/users", || async { "Users".to_string() });
785        router.post("/users", || async { "Create".to_string() });
786        router.get("/users/{id}", || async { "User details".to_string() });
787        router.delete("/users/{id}", || async { "Delete".to_string() });
788
789        let html = router.scalar("API", "1.0.0");
790
791        // Should contain all routes in the OpenAPI spec
792        assert!(html.contains("/users"));
793    }
794
795    // Tests for protocol adapter management
796    #[tokio::test]
797    async fn test_get_adapter_returns_adapter() {
798        let mut router = Router::new();
799        router.add_adapter(Box::new(RestAdapter::new()));
800
801        let adapter = router.get_adapter("rest");
802        assert!(adapter.is_some());
803        assert_eq!(adapter.unwrap().name(), "rest");
804    }
805
806    #[tokio::test]
807    async fn test_get_adapter_returns_none_for_missing() {
808        let router = Router::new();
809        let adapter = router.get_adapter("rest");
810        assert!(adapter.is_none());
811    }
812
813    #[tokio::test]
814    async fn test_route_request_success() {
815        let mut router = Router::new();
816        router.register("test_handler", || async { "Success!".to_string() });
817
818        // Register adapter with a route
819        let mut rest_adapter = RestAdapter::new();
820        rest_adapter.route("GET", "/test", "test_handler");
821        router.add_adapter(Box::new(rest_adapter));
822
823        let result = router.route_request("rest", "GET /test").await;
824        assert!(result.is_ok());
825        let response = result.unwrap();
826        assert!(response.contains("HTTP 200") || response.contains("test_handler"));
827    }
828
829    #[tokio::test]
830    async fn test_route_request_unknown_adapter() {
831        let router = Router::new();
832        let result = router.route_request("unknown", "test").await;
833        assert!(result.is_err());
834        assert!(result.unwrap_err().contains("Adapter not found"));
835    }
836
837    #[tokio::test]
838    async fn test_enabled_protocols_empty() {
839        let router = Router::new();
840        let protocols = router.enabled_protocols();
841        assert_eq!(protocols.len(), 0);
842    }
843
844    #[tokio::test]
845    async fn test_enabled_protocols_multiple() {
846        let mut router = Router::new();
847        router.add_adapter(Box::new(RestAdapter::new()));
848        router.add_adapter(Box::new(GraphQLAdapter::new()));
849        router.add_adapter(Box::new(GrpcAdapter::new()));
850
851        let protocols = router.enabled_protocols();
852        assert_eq!(protocols.len(), 3);
853        assert!(protocols.contains(&"rest".to_string()));
854        assert!(protocols.contains(&"graphql".to_string()));
855        assert!(protocols.contains(&"grpc".to_string()));
856    }
857
858    #[tokio::test]
859    async fn test_can_handle_rest() {
860        let mut router = Router::new();
861        assert!(!router.can_handle_rest("test"));
862
863        router.add_adapter(Box::new(RestAdapter::new()));
864        assert!(router.can_handle_rest("test"));
865    }
866
867    #[tokio::test]
868    async fn test_can_handle_graphql() {
869        let mut router = Router::new();
870        assert!(!router.can_handle_graphql("test"));
871
872        router.add_adapter(Box::new(GraphQLAdapter::new()));
873        assert!(router.can_handle_graphql("test"));
874    }
875
876    #[tokio::test]
877    async fn test_can_handle_grpc() {
878        let mut router = Router::new();
879        assert!(!router.can_handle_grpc("test"));
880
881        router.add_adapter(Box::new(GrpcAdapter::new()));
882        assert!(router.can_handle_grpc("test"));
883    }
884
885    // ===== Integration Tests: Multi-Protocol Routing =====
886
887    #[tokio::test]
888    async fn test_integration_single_handler_rest() {
889        // Test: Single handler exposed via REST
890        let mut router = Router::new();
891        router.register("get_user", || async { "User data".to_string() });
892
893        // Configure REST adapter
894        let mut rest = RestAdapter::new();
895        rest.route("GET", "/users/:id", "get_user");
896        router.add_adapter(Box::new(rest));
897
898        // Route REST request
899        let response = router.route_request("rest", "GET /users/42").await;
900        assert!(response.is_ok());
901        assert!(response.unwrap().contains("get_user"));
902    }
903
904    #[tokio::test]
905    async fn test_integration_single_handler_graphql() {
906        // Test: Single handler exposed via GraphQL
907        let mut router = Router::new();
908        router.register("get_user", || async { "User data".to_string() });
909
910        // Configure GraphQL adapter
911        let mut graphql = GraphQLAdapter::new();
912        graphql.query("user", "get_user");
913        router.add_adapter(Box::new(graphql));
914
915        // Route GraphQL request
916        let response = router.route_request("graphql", "query { user }").await;
917        assert!(response.is_ok());
918        assert!(response.unwrap().contains("get_user"));
919    }
920
921    #[tokio::test]
922    async fn test_integration_single_handler_grpc() {
923        // Test: Single handler exposed via gRPC
924        let mut router = Router::new();
925        router.register("get_user", || async { "User data".to_string() });
926
927        // Configure gRPC adapter
928        let mut grpc = GrpcAdapter::new();
929        grpc.unary("UserService", "GetUser", "get_user");
930        router.add_adapter(Box::new(grpc));
931
932        // Route gRPC request
933        let response = router
934            .route_request("grpc", "UserService.GetUser:{\"id\":42}")
935            .await;
936        assert!(response.is_ok());
937        assert!(response.unwrap().contains("get_user"));
938    }
939
940    #[tokio::test]
941    async fn test_integration_single_handler_all_protocols() {
942        // Test: Single handler exposed via ALL protocols
943        let mut router = Router::new();
944        router.register("get_user", || async { "User data".to_string() });
945
946        // Configure REST adapter
947        let mut rest = RestAdapter::new();
948        rest.route("GET", "/users/:id", "get_user");
949        router.add_adapter(Box::new(rest));
950
951        // Configure GraphQL adapter
952        let mut graphql = GraphQLAdapter::new();
953        graphql.query("user", "get_user");
954        router.add_adapter(Box::new(graphql));
955
956        // Configure gRPC adapter
957        let mut grpc = GrpcAdapter::new();
958        grpc.unary("UserService", "GetUser", "get_user");
959        router.add_adapter(Box::new(grpc));
960
961        // Test REST
962        let rest_response = router.route_request("rest", "GET /users/42").await;
963        assert!(rest_response.is_ok());
964        assert!(rest_response.unwrap().contains("get_user"));
965
966        // Test GraphQL
967        let graphql_response = router.route_request("graphql", "query { user }").await;
968        assert!(graphql_response.is_ok());
969        assert!(graphql_response.unwrap().contains("get_user"));
970
971        // Test gRPC
972        let grpc_response = router
973            .route_request("grpc", "UserService.GetUser:{\"id\":42}")
974            .await;
975        assert!(grpc_response.is_ok());
976        assert!(grpc_response.unwrap().contains("get_user"));
977    }
978
979    #[tokio::test]
980    async fn test_integration_multiple_handlers_all_protocols() {
981        // Test: Multiple handlers, each exposed via all protocols
982        let mut router = Router::new();
983        router.register("get_user", || async { "User data".to_string() });
984        router.register("list_users", || async { "Users list".to_string() });
985        router.register("create_user", || async { "Created user".to_string() });
986
987        // Configure REST adapter
988        let mut rest = RestAdapter::new();
989        rest.route("GET", "/users/:id", "get_user");
990        rest.route("GET", "/users", "list_users");
991        rest.route("POST", "/users", "create_user");
992        router.add_adapter(Box::new(rest));
993
994        // Configure GraphQL adapter
995        let mut graphql = GraphQLAdapter::new();
996        graphql.query("user", "get_user");
997        graphql.query("users", "list_users");
998        graphql.mutation("createUser", "create_user");
999        router.add_adapter(Box::new(graphql));
1000
1001        // Configure gRPC adapter
1002        let mut grpc = GrpcAdapter::new();
1003        grpc.unary("UserService", "GetUser", "get_user");
1004        grpc.unary("UserService", "ListUsers", "list_users");
1005        grpc.unary("UserService", "CreateUser", "create_user");
1006        router.add_adapter(Box::new(grpc));
1007
1008        // Test each handler via each protocol
1009        assert!(router
1010            .route_request("rest", "GET /users/42")
1011            .await
1012            .unwrap()
1013            .contains("get_user"));
1014        assert!(router
1015            .route_request("graphql", "query { user }")
1016            .await
1017            .unwrap()
1018            .contains("get_user"));
1019        assert!(router
1020            .route_request("grpc", "UserService.GetUser:{}")
1021            .await
1022            .unwrap()
1023            .contains("get_user"));
1024    }
1025
1026    #[tokio::test]
1027    async fn test_integration_error_handling_rest_404() {
1028        // Test: REST 404 error
1029        let mut router = Router::new();
1030
1031        let mut rest = RestAdapter::new();
1032        rest.route("GET", "/users/:id", "get_user");
1033        router.add_adapter(Box::new(rest));
1034
1035        let response = router.route_request("rest", "GET /posts/42").await;
1036        assert!(response.is_ok());
1037        assert!(response.unwrap().contains("HTTP 404"));
1038    }
1039
1040    #[tokio::test]
1041    async fn test_integration_error_handling_graphql_not_found() {
1042        // Test: GraphQL operation not found
1043        let mut router = Router::new();
1044
1045        let mut graphql = GraphQLAdapter::new();
1046        graphql.query("user", "get_user");
1047        router.add_adapter(Box::new(graphql));
1048
1049        let response = router.route_request("graphql", "query { post }").await;
1050        assert!(response.is_ok());
1051        assert!(response.unwrap().contains("errors"));
1052    }
1053
1054    #[tokio::test]
1055    async fn test_integration_error_handling_grpc_unimplemented() {
1056        // Test: gRPC method not implemented
1057        let mut router = Router::new();
1058
1059        let mut grpc = GrpcAdapter::new();
1060        grpc.unary("UserService", "GetUser", "get_user");
1061        router.add_adapter(Box::new(grpc));
1062
1063        let response = router.route_request("grpc", "UserService.GetPost:{}").await;
1064        assert!(response.is_ok());
1065        assert!(response.unwrap().contains("grpc-status: 12")); // UNIMPLEMENTED
1066    }
1067
1068    #[tokio::test]
1069    async fn test_integration_unknown_protocol() {
1070        // Test: Unknown protocol error
1071        let router = Router::new();
1072
1073        let response = router.route_request("unknown", "request").await;
1074        assert!(response.is_err());
1075        assert!(response.unwrap_err().contains("Adapter not found"));
1076    }
1077
1078    #[tokio::test]
1079    async fn test_integration_protocol_specific_features_rest_methods() {
1080        // Test: REST-specific HTTP methods
1081        let mut router = Router::new();
1082        router.register("get_users", || async { "Users".to_string() });
1083        router.register("create_user", || async { "Created".to_string() });
1084        router.register("update_user", || async { "Updated".to_string() });
1085        router.register("delete_user", || async { "Deleted".to_string() });
1086
1087        let mut rest = RestAdapter::new();
1088        rest.route("GET", "/users", "get_users");
1089        rest.route("POST", "/users", "create_user");
1090        rest.route("PUT", "/users/:id", "update_user");
1091        rest.route("DELETE", "/users/:id", "delete_user");
1092        router.add_adapter(Box::new(rest));
1093
1094        // Test different HTTP methods
1095        assert!(router
1096            .route_request("rest", "GET /users")
1097            .await
1098            .unwrap()
1099            .contains("get_users"));
1100        assert!(router
1101            .route_request("rest", "POST /users")
1102            .await
1103            .unwrap()
1104            .contains("create_user"));
1105        assert!(router
1106            .route_request("rest", "PUT /users/42")
1107            .await
1108            .unwrap()
1109            .contains("update_user"));
1110        assert!(router
1111            .route_request("rest", "DELETE /users/42")
1112            .await
1113            .unwrap()
1114            .contains("delete_user"));
1115    }
1116
1117    #[tokio::test]
1118    async fn test_integration_protocol_specific_features_graphql_types() {
1119        // Test: GraphQL-specific query vs mutation
1120        let mut router = Router::new();
1121        router.register("get_user", || async { "User".to_string() });
1122        router.register("create_user", || async { "Created".to_string() });
1123
1124        let mut graphql = GraphQLAdapter::new();
1125        graphql.query("user", "get_user");
1126        graphql.mutation("createUser", "create_user");
1127        router.add_adapter(Box::new(graphql));
1128
1129        // Test query
1130        assert!(router
1131            .route_request("graphql", "query { user }")
1132            .await
1133            .unwrap()
1134            .contains("get_user"));
1135
1136        // Test mutation
1137        assert!(router
1138            .route_request("graphql", "mutation { createUser }")
1139            .await
1140            .unwrap()
1141            .contains("create_user"));
1142    }
1143
1144    #[tokio::test]
1145    async fn test_integration_protocol_specific_features_grpc_streaming() {
1146        // Test: gRPC-specific streaming modes
1147        let mut router = Router::new();
1148        router.register("get_user", || async { "User".to_string() });
1149        router.register("list_users", || async { "Users".to_string() });
1150
1151        let mut grpc = GrpcAdapter::new();
1152        grpc.unary("UserService", "GetUser", "get_user");
1153        grpc.server_streaming("UserService", "ListUsers", "list_users");
1154        router.add_adapter(Box::new(grpc));
1155
1156        // Test unary
1157        let unary_response = router
1158            .route_request("grpc", "UserService.GetUser:{}")
1159            .await
1160            .unwrap();
1161        assert!(unary_response.contains("unary"));
1162
1163        // Test server streaming
1164        let streaming_response = router
1165            .route_request("grpc", "UserService.ListUsers:{}")
1166            .await
1167            .unwrap();
1168        assert!(streaming_response.contains("server_streaming"));
1169    }
1170}