1use serde::de::DeserializeOwned;
65use serde::Serialize;
66use std::{any::Any, collections::HashMap, future::Future, sync::Arc};
67
68pub mod adapter;
69pub mod builder;
70#[cfg(feature = "router")]
71pub mod config;
72pub mod contract;
73pub mod docs;
74pub mod graphiql;
75pub mod graphql;
76pub mod grpc;
77pub mod grpc_explorer;
78pub mod handler;
79pub mod metadata;
80pub mod method;
81pub mod openapi;
82pub mod rest;
83pub mod scalar;
84pub mod schema;
85pub mod ts_codegen;
86
87#[cfg(feature = "router-graphql")]
89pub mod graphql_prod;
90#[cfg(feature = "router-grpc")]
91pub mod grpc_prod;
92
93pub use adapter::ProtocolAdapter;
94pub use builder::RouteBuilder;
95#[cfg(feature = "router")]
96pub use config::{GraphQLConfig, GrpcConfig, RestConfig, RouterConfig, ServerConfig};
97pub use contract::{
98 ContractTestConfig, ContractTestResult, ContractTestResults, ContractTestable, ContractTester,
99};
100pub use docs::DocsConfig;
101pub use graphiql::{graphiql_html, GraphiQLConfig, GraphiQLTheme};
102pub use graphql::{GraphQLAdapter, GraphQLOperation, OperationType};
103#[cfg(feature = "router-graphql")]
105pub use graphql_prod::GraphQLProductionAdapter;
106pub use grpc::{GrpcAdapter, GrpcMethod, GrpcMethodType, GrpcRequest, GrpcStatus};
107pub use grpc_explorer::{grpc_explorer_html, GrpcExplorerConfig, GrpcExplorerTheme};
108#[cfg(feature = "router-grpc")]
109pub use grpc_prod::{protobuf, status, streaming, GrpcProductionAdapter, GrpcService};
110pub use handler::{
111 Handler, HandlerFn, HandlerWithArgs, HandlerWithState, HandlerWithStateOnly,
112 IntoHandlerResult, Json, State,
113};
114pub use metadata::RouteMetadata;
115pub use method::Method;
116pub use openapi::{OpenApiGenerator, OpenApiServer};
117pub use rest::{RestAdapter, RestRequest, RestResponse, RestRoute};
118pub use scalar::{scalar_html, ScalarConfig, ScalarLayout, ScalarTheme};
119pub use schema::ToJsonSchema;
120pub use ts_codegen::{generate_ts_client, HandlerMeta, TsField, TsType};
121
122pub struct Router {
127 handlers: HashMap<String, Box<dyn Handler>>,
128 adapters: HashMap<String, Box<dyn ProtocolAdapter>>,
129 routes: Vec<RouteMetadata>,
130 state: Option<Arc<dyn Any + Send + Sync>>,
131 handler_metas: HashMap<String, HandlerMeta>,
132 #[cfg(feature = "router")]
133 #[allow(dead_code)]
134 config: Option<RouterConfig>,
135}
136
137impl Router {
138 pub fn new() -> Self {
140 Self {
141 handlers: HashMap::new(),
142 adapters: HashMap::new(),
143 routes: Vec::new(),
144 state: None,
145 handler_metas: HashMap::new(),
146 #[cfg(feature = "router")]
147 config: None,
148 }
149 }
150
151 #[cfg(feature = "router")]
153 pub fn with_config(config: RouterConfig) -> Self {
154 let mut router = Self {
155 handlers: HashMap::new(),
156 adapters: HashMap::new(),
157 routes: Vec::new(),
158 state: None,
159 handler_metas: HashMap::new(),
160 config: Some(config.clone()),
161 };
162
163 if config.has_protocol("rest") {
165 router.add_adapter(Box::new(RestAdapter::new()));
166 }
167 if config.has_protocol("graphql") {
168 router.add_adapter(Box::new(GraphQLAdapter::new()));
169 }
170 if config.has_protocol("grpc") {
171 router.add_adapter(Box::new(GrpcAdapter::new()));
172 }
173
174 router
175 }
176
177 pub fn with_state<S: Send + Sync + 'static>(mut self, state: S) -> Self {
179 self.state = Some(Arc::new(state));
180 self
181 }
182
183 pub fn register<F, Fut>(&mut self, name: &str, handler: F)
185 where
186 F: Fn() -> Fut + Send + Sync + 'static,
187 Fut: Future<Output = String> + Send + 'static,
188 {
189 self.handlers
190 .insert(name.to_string(), Box::new(HandlerFn::new(handler)));
191 }
192
193 pub fn register_with_args<T, F, Fut>(&mut self, name: &str, handler: F)
195 where
196 T: DeserializeOwned + Send + 'static,
197 F: Fn(T) -> Fut + Send + Sync + 'static,
198 Fut: Future<Output = String> + Send + 'static,
199 {
200 self.handlers
201 .insert(name.to_string(), Box::new(HandlerWithArgs::new(handler)));
202 }
203
204 pub fn register_with_state<S, T, F, Fut>(&mut self, name: &str, handler: F)
211 where
212 S: Send + Sync + 'static,
213 T: DeserializeOwned + Send + 'static,
214 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
215 Fut: Future<Output = String> + Send + 'static,
216 {
217 let state = self
218 .state
219 .clone()
220 .expect("register_with_state requires with_state to be called first");
221 self.handlers
222 .insert(name.to_string(), Box::new(HandlerWithState::new(handler, state)));
223 }
224
225 pub fn register_with_state_only<S, F, Fut>(&mut self, name: &str, handler: F)
232 where
233 S: Send + Sync + 'static,
234 F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
235 Fut: Future<Output = String> + Send + 'static,
236 {
237 let state = self
238 .state
239 .clone()
240 .expect("register_with_state_only requires with_state to be called first");
241 self.handlers
242 .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(handler, state)));
243 }
244
245 pub fn register_typed<R, F, Fut>(&mut self, name: &str, handler: F)
249 where
250 R: Serialize + Send + 'static,
251 F: Fn() -> Fut + Send + Sync + 'static,
252 Fut: Future<Output = R> + Send + 'static,
253 {
254 let wrapped = move || {
255 let fut = handler();
256 async move { Json(fut.await) }
257 };
258 self.handlers
259 .insert(name.to_string(), Box::new(HandlerFn::new(wrapped)));
260 }
261
262 pub fn register_typed_with_args<T, R, F, Fut>(&mut self, name: &str, handler: F)
264 where
265 T: DeserializeOwned + Send + 'static,
266 R: Serialize + Send + 'static,
267 F: Fn(T) -> Fut + Send + Sync + 'static,
268 Fut: Future<Output = R> + Send + 'static,
269 {
270 let wrapped = move |args: T| {
271 let fut = handler(args);
272 async move { Json(fut.await) }
273 };
274 self.handlers
275 .insert(name.to_string(), Box::new(HandlerWithArgs::new(wrapped)));
276 }
277
278 pub fn register_typed_with_state<S, T, R, F, Fut>(&mut self, name: &str, handler: F)
280 where
281 S: Send + Sync + 'static,
282 T: DeserializeOwned + Send + 'static,
283 R: Serialize + Send + 'static,
284 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
285 Fut: Future<Output = R> + Send + 'static,
286 {
287 let state = self
288 .state
289 .clone()
290 .expect("register_typed_with_state requires with_state to be called first");
291 let wrapped = move |s: State<Arc<S>>, args: T| {
292 let fut = handler(s, args);
293 async move { Json(fut.await) }
294 };
295 self.handlers
296 .insert(name.to_string(), Box::new(HandlerWithState::new(wrapped, state)));
297 }
298
299 pub fn register_typed_with_state_only<S, R, F, Fut>(&mut self, name: &str, handler: F)
301 where
302 S: Send + Sync + 'static,
303 R: Serialize + Send + 'static,
304 F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
305 Fut: Future<Output = R> + Send + 'static,
306 {
307 let state = self
308 .state
309 .clone()
310 .expect("register_typed_with_state_only requires with_state to be called first");
311 let wrapped = move |s: State<Arc<S>>| {
312 let fut = handler(s);
313 async move { Json(fut.await) }
314 };
315 self.handlers
316 .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(wrapped, state)));
317 }
318
319 pub fn register_result<R, E, F, Fut>(&mut self, name: &str, handler: F)
326 where
327 R: Serialize + Send + 'static,
328 E: std::fmt::Display + Send + 'static,
329 F: Fn() -> Fut + Send + Sync + 'static,
330 Fut: Future<Output = Result<R, E>> + Send + 'static,
331 {
332 self.handlers
333 .insert(name.to_string(), Box::new(HandlerFn::new(handler)));
334 }
335
336 pub fn register_result_with_args<T, R, E, F, Fut>(&mut self, name: &str, handler: F)
338 where
339 T: DeserializeOwned + Send + 'static,
340 R: Serialize + Send + 'static,
341 E: std::fmt::Display + Send + 'static,
342 F: Fn(T) -> Fut + Send + Sync + 'static,
343 Fut: Future<Output = Result<R, E>> + Send + 'static,
344 {
345 self.handlers
346 .insert(name.to_string(), Box::new(HandlerWithArgs::new(handler)));
347 }
348
349 pub fn register_result_with_state<S, T, R, E, F, Fut>(&mut self, name: &str, handler: F)
351 where
352 S: Send + Sync + 'static,
353 T: DeserializeOwned + Send + 'static,
354 R: Serialize + Send + 'static,
355 E: std::fmt::Display + Send + 'static,
356 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
357 Fut: Future<Output = Result<R, E>> + Send + 'static,
358 {
359 let state = self
360 .state
361 .clone()
362 .expect("register_result_with_state requires with_state to be called first");
363 self.handlers
364 .insert(name.to_string(), Box::new(HandlerWithState::new(handler, state)));
365 }
366
367 pub fn register_result_with_state_only<S, R, E, F, Fut>(&mut self, name: &str, handler: F)
369 where
370 S: Send + Sync + 'static,
371 R: Serialize + Send + 'static,
372 E: std::fmt::Display + Send + 'static,
373 F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
374 Fut: Future<Output = Result<R, E>> + Send + 'static,
375 {
376 let state = self
377 .state
378 .clone()
379 .expect("register_result_with_state_only requires with_state to be called first");
380 self.handlers
381 .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(handler, state)));
382 }
383
384 pub fn handlers_count(&self) -> usize {
386 self.handlers.len()
387 }
388
389 pub fn describe_handler(
409 &mut self,
410 name: &str,
411 args: Vec<TsField>,
412 returns: TsType,
413 ) {
414 debug_assert!(
415 self.handlers.contains_key(name),
416 "describe_handler: handler '{}' not registered",
417 name
418 );
419 self.handler_metas
420 .insert(name.to_string(), HandlerMeta { args, returns });
421 }
422
423 pub fn generate_ts_client(&self) -> String {
446 generate_ts_client(&self.handler_metas)
447 }
448
449 pub fn handler_meta(&self, name: &str) -> Option<&HandlerMeta> {
451 self.handler_metas.get(name)
452 }
453
454 pub fn add_adapter(&mut self, adapter: Box<dyn ProtocolAdapter>) {
456 self.adapters.insert(adapter.name().to_string(), adapter);
457 }
458
459 pub fn has_adapter(&self, name: &str) -> bool {
461 self.adapters.contains_key(name)
462 }
463
464 pub fn get_adapter(&self, name: &str) -> Option<&dyn ProtocolAdapter> {
466 self.adapters.get(name).map(|b| &**b)
467 }
468
469 pub async fn route_request(&self, protocol: &str, request: &str) -> Result<String, String> {
471 let adapter = self
472 .get_adapter(protocol)
473 .ok_or_else(|| format!("Adapter not found: {}", protocol))?;
474
475 adapter.handle(request).await
476 }
477
478 pub async fn execute(&self, name: &str) -> Result<String, String> {
480 self.execute_with_args(name, "{}").await
481 }
482
483 pub async fn execute_with_args(&self, name: &str, args: &str) -> Result<String, String> {
485 match self.handlers.get(name) {
486 Some(handler) => handler.call(args).await,
487 None => Err(format!("Handler '{}' not found", name)),
488 }
489 }
490
491 pub fn list_handlers(&self) -> Vec<String> {
496 self.handlers.keys().cloned().collect()
497 }
498
499 pub async fn call_handler(&self, name: &str, request: &str) -> Result<String, String> {
504 self.execute_with_args(name, request).await
505 }
506
507 pub fn can_handle_rest(&self, _name: &str) -> bool {
509 self.has_adapter("rest")
510 }
511
512 pub fn can_handle_graphql(&self, _name: &str) -> bool {
514 self.has_adapter("graphql")
515 }
516
517 pub fn can_handle_grpc(&self, _name: &str) -> bool {
519 self.has_adapter("grpc")
520 }
521
522 pub fn enabled_protocols(&self) -> Vec<String> {
524 self.adapters.keys().cloned().collect()
525 }
526
527 pub fn add_route(&mut self, metadata: RouteMetadata) {
532 self.routes.push(metadata);
533 }
534
535 pub fn routes(&self) -> &[RouteMetadata] {
540 &self.routes
541 }
542
543 pub fn get<F, Fut>(&mut self, path: &str, handler: F)
549 where
550 F: Fn() -> Fut + Send + Sync + 'static,
551 Fut: Future<Output = String> + Send + 'static,
552 {
553 let handler_name = format!("GET:{}", path);
554 self.register(&handler_name, handler);
555 self.add_route(RouteMetadata::new(path, Method::GET, "rest"));
556 }
557
558 pub fn post<F, Fut>(&mut self, path: &str, handler: F)
564 where
565 F: Fn() -> Fut + Send + Sync + 'static,
566 Fut: Future<Output = String> + Send + 'static,
567 {
568 let handler_name = format!("POST:{}", path);
569 self.register(&handler_name, handler);
570 self.add_route(RouteMetadata::new(path, Method::POST, "rest"));
571 }
572
573 pub fn put<F, Fut>(&mut self, path: &str, handler: F)
579 where
580 F: Fn() -> Fut + Send + Sync + 'static,
581 Fut: Future<Output = String> + Send + 'static,
582 {
583 let handler_name = format!("PUT:{}", path);
584 self.register(&handler_name, handler);
585 self.add_route(RouteMetadata::new(path, Method::PUT, "rest"));
586 }
587
588 pub fn delete<F, Fut>(&mut self, path: &str, handler: F)
594 where
595 F: Fn() -> Fut + Send + Sync + 'static,
596 Fut: Future<Output = String> + Send + 'static,
597 {
598 let handler_name = format!("DELETE:{}", path);
599 self.register(&handler_name, handler);
600 self.add_route(RouteMetadata::new(path, Method::DELETE, "rest"));
601 }
602
603 pub fn patch<F, Fut>(&mut self, path: &str, handler: F)
609 where
610 F: Fn() -> Fut + Send + Sync + 'static,
611 Fut: Future<Output = String> + Send + 'static,
612 {
613 let handler_name = format!("PATCH:{}", path);
614 self.register(&handler_name, handler);
615 self.add_route(RouteMetadata::new(path, Method::PATCH, "rest"));
616 }
617
618 pub fn head<F, Fut>(&mut self, path: &str, handler: F)
624 where
625 F: Fn() -> Fut + Send + Sync + 'static,
626 Fut: Future<Output = String> + Send + 'static,
627 {
628 let handler_name = format!("HEAD:{}", path);
629 self.register(&handler_name, handler);
630 self.add_route(RouteMetadata::new(path, Method::HEAD, "rest"));
631 }
632
633 pub fn options<F, Fut>(&mut self, path: &str, handler: F)
639 where
640 F: Fn() -> Fut + Send + Sync + 'static,
641 Fut: Future<Output = String> + Send + 'static,
642 {
643 let handler_name = format!("OPTIONS:{}", path);
644 self.register(&handler_name, handler);
645 self.add_route(RouteMetadata::new(path, Method::OPTIONS, "rest"));
646 }
647
648 pub async fn call_rest(&self, method: &str, path: &str) -> Result<String, String> {
650 let adapter = self
651 .adapters
652 .get("rest")
653 .ok_or_else(|| "REST adapter not enabled".to_string())?;
654
655 let request = format!("{} {}", method, path);
656 adapter.handle(&request).await
657 }
658
659 pub async fn call_graphql(&self, query: &str) -> Result<String, String> {
661 let adapter = self
662 .adapters
663 .get("graphql")
664 .ok_or_else(|| "GraphQL adapter not enabled".to_string())?;
665
666 adapter.handle(query).await
667 }
668
669 pub async fn call_grpc(&self, method: &str, request: &str) -> Result<String, String> {
671 let adapter = self
672 .adapters
673 .get("grpc")
674 .ok_or_else(|| "gRPC adapter not enabled".to_string())?;
675
676 let grpc_request = format!("{}:{}", method, request);
677 adapter.handle(&grpc_request).await
678 }
679
680 pub fn scalar(&self, title: &str, version: &str) -> String {
702 let config = scalar::ScalarConfig::default();
703 self.scalar_docs(config, title, version)
704 }
705
706 pub fn scalar_docs(&self, config: scalar::ScalarConfig, title: &str, version: &str) -> String {
732 let spec = OpenApiGenerator::new(title, version).generate(self);
734 let spec_json = serde_json::to_string(&spec).unwrap_or_else(|_| "{}".to_string());
735
736 scalar::scalar_html(&config, title, &spec_json)
738 }
739}
740
741impl Default for Router {
742 fn default() -> Self {
743 Self::new()
744 }
745}
746
747#[cfg(test)]
748mod tests {
749 use super::*;
750
751 #[tokio::test]
752 async fn test_router_creation() {
753 let router = Router::new();
754 assert_eq!(router.handlers_count(), 0);
755 }
756
757 #[tokio::test]
758 async fn test_handler_registration() {
759 let mut router = Router::new();
760 router.register("test", || async { "Hello".to_string() });
761 assert_eq!(router.handlers_count(), 1);
762 }
763
764 #[tokio::test]
765 async fn test_handler_execution() {
766 let mut router = Router::new();
767 router.register("test", || async { "Hello".to_string() });
768 let result = router.execute("test").await;
769 assert_eq!(result, Ok("Hello".to_string()));
770 }
771
772 #[tokio::test]
774 async fn test_router_starts_with_no_routes() {
775 let router = Router::new();
776 let routes = router.routes();
777 assert_eq!(routes.len(), 0);
778 }
779
780 #[tokio::test]
781 async fn test_add_route_metadata() {
782 let mut router = Router::new();
783 let metadata = RouteMetadata::new("/users", "GET", "rest");
784
785 router.add_route(metadata.clone());
786
787 let routes = router.routes();
788 assert_eq!(routes.len(), 1);
789 assert_eq!(routes[0].path, "/users");
790 assert_eq!(routes[0].method, "GET");
791 assert_eq!(routes[0].protocol, "rest");
792 }
793
794 #[tokio::test]
795 async fn test_add_multiple_routes() {
796 let mut router = Router::new();
797
798 router.add_route(RouteMetadata::new("/users", "GET", "rest"));
799 router.add_route(RouteMetadata::new("/users", "POST", "rest"));
800 router.add_route(RouteMetadata::new("/posts", "GET", "rest"));
801
802 let routes = router.routes();
803 assert_eq!(routes.len(), 3);
804 }
805
806 #[tokio::test]
807 async fn test_routes_with_different_protocols() {
808 let mut router = Router::new();
809
810 router.add_route(RouteMetadata::new("/users", "GET", "rest"));
811 router.add_route(RouteMetadata::new("users", "query", "graphql"));
812 router.add_route(RouteMetadata::new("UserService.GetUser", "unary", "grpc"));
813
814 let routes = router.routes();
815 assert_eq!(routes.len(), 3);
816
817 assert_eq!(routes[0].protocol, "rest");
818 assert_eq!(routes[1].protocol, "graphql");
819 assert_eq!(routes[2].protocol, "grpc");
820 }
821
822 #[tokio::test]
823 async fn test_routes_returns_immutable_reference() {
824 let mut router = Router::new();
825 router.add_route(RouteMetadata::new("/test", "GET", "rest"));
826
827 let routes1 = router.routes();
828 let routes2 = router.routes();
829
830 assert_eq!(routes1.len(), routes2.len());
832 assert_eq!(routes1[0].path, routes2[0].path);
833 }
834
835 #[tokio::test]
837 async fn test_route_get_method() {
838 let mut router = Router::new();
839 router.get("/users", || async { "User list".to_string() });
840
841 let routes = router.routes();
842 assert_eq!(routes.len(), 1);
843 assert_eq!(routes[0].path, "/users");
844 assert_eq!(routes[0].method, "GET");
845 assert_eq!(routes[0].protocol, "rest");
846 }
847
848 #[tokio::test]
849 async fn test_route_post_method() {
850 let mut router = Router::new();
851 router.post("/users", || async { "User created".to_string() });
852
853 let routes = router.routes();
854 assert_eq!(routes.len(), 1);
855 assert_eq!(routes[0].path, "/users");
856 assert_eq!(routes[0].method, "POST");
857 assert_eq!(routes[0].protocol, "rest");
858 }
859
860 #[tokio::test]
861 async fn test_route_put_method() {
862 let mut router = Router::new();
863 router.put("/users/1", || async { "User updated".to_string() });
864
865 let routes = router.routes();
866 assert_eq!(routes.len(), 1);
867 assert_eq!(routes[0].method, "PUT");
868 }
869
870 #[tokio::test]
871 async fn test_route_delete_method() {
872 let mut router = Router::new();
873 router.delete("/users/1", || async { "User deleted".to_string() });
874
875 let routes = router.routes();
876 assert_eq!(routes.len(), 1);
877 assert_eq!(routes[0].method, "DELETE");
878 }
879
880 #[tokio::test]
881 async fn test_route_patch_method() {
882 let mut router = Router::new();
883 router.patch("/users/1", || async { "User patched".to_string() });
884
885 let routes = router.routes();
886 assert_eq!(routes.len(), 1);
887 assert_eq!(routes[0].method, "PATCH");
888 }
889
890 #[tokio::test]
891 async fn test_multiple_routes_different_methods() {
892 let mut router = Router::new();
893 router.get("/users", || async { "List".to_string() });
894 router.post("/users", || async { "Create".to_string() });
895 router.put("/users/1", || async { "Update".to_string() });
896 router.delete("/users/1", || async { "Delete".to_string() });
897
898 let routes = router.routes();
899 assert_eq!(routes.len(), 4);
900
901 assert_eq!(routes[0].method, "GET");
902 assert_eq!(routes[1].method, "POST");
903 assert_eq!(routes[2].method, "PUT");
904 assert_eq!(routes[3].method, "DELETE");
905 }
906
907 #[tokio::test]
908 async fn test_route_method_with_path_params() {
909 let mut router = Router::new();
910 router.get("/users/{id}", || async { "User details".to_string() });
911 router.get("/users/{id}/posts/{post_id}", || async {
912 "Post details".to_string()
913 });
914
915 let routes = router.routes();
916 assert_eq!(routes.len(), 2);
917 assert_eq!(routes[0].path, "/users/{id}");
918 assert_eq!(routes[1].path, "/users/{id}/posts/{post_id}");
919 }
920
921 #[tokio::test]
922 async fn test_route_registration_and_execution() {
923 let mut router = Router::new();
924 router.get("/test", || async { "GET response".to_string() });
925 router.post("/test", || async { "POST response".to_string() });
926
927 assert_eq!(router.routes().len(), 2);
929 assert_eq!(router.handlers_count(), 2);
930
931 let result1 = router.execute("GET:/test").await;
933 let result2 = router.execute("POST:/test").await;
934
935 assert_eq!(result1, Ok("GET response".to_string()));
936 assert_eq!(result2, Ok("POST response".to_string()));
937 }
938
939 #[tokio::test]
941 async fn test_scalar_generates_html() {
942 let mut router = Router::new();
943 router.get("/users", || async { "Users".to_string() });
944
945 let html = router.scalar("Test API", "1.0.0");
946
947 assert!(html.contains("<!DOCTYPE html>"));
948 assert!(html.contains("<title>Test API - API Documentation</title>"));
949 assert!(html.contains("@scalar/api-reference"));
950 }
951
952 #[tokio::test]
953 async fn test_scalar_contains_openapi_spec() {
954 let mut router = Router::new();
955 router.get("/users", || async { "Users".to_string() });
956 router.post("/users", || async { "User created".to_string() });
957
958 let html = router.scalar("Test API", "1.0.0");
959
960 assert!(html.contains("openapi"));
962 assert!(html.contains("Test API"));
963 assert!(html.contains("1.0.0"));
964 }
965
966 #[tokio::test]
967 async fn test_scalar_docs_with_custom_config() {
968 let mut router = Router::new();
969 router.get("/users", || async { "Users".to_string() });
970
971 let config = scalar::ScalarConfig::new()
972 .theme(scalar::ScalarTheme::Light)
973 .show_sidebar(false);
974
975 let html = router.scalar_docs(config, "Custom API", "2.0.0");
976
977 assert!(html.contains("Custom API"));
978 assert!(html.contains(r#""theme":"light""#));
979 assert!(html.contains(r#""showSidebar":false"#));
980 }
981
982 #[tokio::test]
983 async fn test_scalar_docs_with_custom_css() {
984 let mut router = Router::new();
985 router.get("/test", || async { "Test".to_string() });
986
987 let config = scalar::ScalarConfig::new().custom_css("body { font-family: 'Inter'; }");
988
989 let html = router.scalar_docs(config, "API", "1.0");
990
991 assert!(html.contains("<style>body { font-family: 'Inter'; }</style>"));
992 }
993
994 #[tokio::test]
995 async fn test_scalar_with_multiple_routes() {
996 let mut router = Router::new();
997 router.get("/users", || async { "Users".to_string() });
998 router.post("/users", || async { "Create".to_string() });
999 router.get("/users/{id}", || async { "User details".to_string() });
1000 router.delete("/users/{id}", || async { "Delete".to_string() });
1001
1002 let html = router.scalar("API", "1.0.0");
1003
1004 assert!(html.contains("/users"));
1006 }
1007
1008 #[tokio::test]
1010 async fn test_get_adapter_returns_adapter() {
1011 let mut router = Router::new();
1012 router.add_adapter(Box::new(RestAdapter::new()));
1013
1014 let adapter = router.get_adapter("rest");
1015 assert!(adapter.is_some());
1016 assert_eq!(adapter.unwrap().name(), "rest");
1017 }
1018
1019 #[tokio::test]
1020 async fn test_get_adapter_returns_none_for_missing() {
1021 let router = Router::new();
1022 let adapter = router.get_adapter("rest");
1023 assert!(adapter.is_none());
1024 }
1025
1026 #[tokio::test]
1027 async fn test_route_request_success() {
1028 let mut router = Router::new();
1029 router.register("test_handler", || async { "Success!".to_string() });
1030
1031 let mut rest_adapter = RestAdapter::new();
1033 rest_adapter.route("GET", "/test", "test_handler");
1034 router.add_adapter(Box::new(rest_adapter));
1035
1036 let result = router.route_request("rest", "GET /test").await;
1037 assert!(result.is_ok());
1038 let response = result.unwrap();
1039 assert!(response.contains("HTTP 200") || response.contains("test_handler"));
1040 }
1041
1042 #[tokio::test]
1043 async fn test_route_request_unknown_adapter() {
1044 let router = Router::new();
1045 let result = router.route_request("unknown", "test").await;
1046 assert!(result.is_err());
1047 assert!(result.unwrap_err().contains("Adapter not found"));
1048 }
1049
1050 #[tokio::test]
1051 async fn test_enabled_protocols_empty() {
1052 let router = Router::new();
1053 let protocols = router.enabled_protocols();
1054 assert_eq!(protocols.len(), 0);
1055 }
1056
1057 #[tokio::test]
1058 async fn test_enabled_protocols_multiple() {
1059 let mut router = Router::new();
1060 router.add_adapter(Box::new(RestAdapter::new()));
1061 router.add_adapter(Box::new(GraphQLAdapter::new()));
1062 router.add_adapter(Box::new(GrpcAdapter::new()));
1063
1064 let protocols = router.enabled_protocols();
1065 assert_eq!(protocols.len(), 3);
1066 assert!(protocols.contains(&"rest".to_string()));
1067 assert!(protocols.contains(&"graphql".to_string()));
1068 assert!(protocols.contains(&"grpc".to_string()));
1069 }
1070
1071 #[tokio::test]
1072 async fn test_can_handle_rest() {
1073 let mut router = Router::new();
1074 assert!(!router.can_handle_rest("test"));
1075
1076 router.add_adapter(Box::new(RestAdapter::new()));
1077 assert!(router.can_handle_rest("test"));
1078 }
1079
1080 #[tokio::test]
1081 async fn test_can_handle_graphql() {
1082 let mut router = Router::new();
1083 assert!(!router.can_handle_graphql("test"));
1084
1085 router.add_adapter(Box::new(GraphQLAdapter::new()));
1086 assert!(router.can_handle_graphql("test"));
1087 }
1088
1089 #[tokio::test]
1090 async fn test_can_handle_grpc() {
1091 let mut router = Router::new();
1092 assert!(!router.can_handle_grpc("test"));
1093
1094 router.add_adapter(Box::new(GrpcAdapter::new()));
1095 assert!(router.can_handle_grpc("test"));
1096 }
1097
1098 #[tokio::test]
1101 async fn test_integration_single_handler_rest() {
1102 let mut router = Router::new();
1104 router.register("get_user", || async { "User data".to_string() });
1105
1106 let mut rest = RestAdapter::new();
1108 rest.route("GET", "/users/:id", "get_user");
1109 router.add_adapter(Box::new(rest));
1110
1111 let response = router.route_request("rest", "GET /users/42").await;
1113 assert!(response.is_ok());
1114 assert!(response.unwrap().contains("get_user"));
1115 }
1116
1117 #[tokio::test]
1118 async fn test_integration_single_handler_graphql() {
1119 let mut router = Router::new();
1121 router.register("get_user", || async { "User data".to_string() });
1122
1123 let mut graphql = GraphQLAdapter::new();
1125 graphql.query("user", "get_user");
1126 router.add_adapter(Box::new(graphql));
1127
1128 let response = router.route_request("graphql", "query { user }").await;
1130 assert!(response.is_ok());
1131 assert!(response.unwrap().contains("get_user"));
1132 }
1133
1134 #[tokio::test]
1135 async fn test_integration_single_handler_grpc() {
1136 let mut router = Router::new();
1138 router.register("get_user", || async { "User data".to_string() });
1139
1140 let mut grpc = GrpcAdapter::new();
1142 grpc.unary("UserService", "GetUser", "get_user");
1143 router.add_adapter(Box::new(grpc));
1144
1145 let response = router
1147 .route_request("grpc", "UserService.GetUser:{\"id\":42}")
1148 .await;
1149 assert!(response.is_ok());
1150 assert!(response.unwrap().contains("get_user"));
1151 }
1152
1153 #[tokio::test]
1154 async fn test_integration_single_handler_all_protocols() {
1155 let mut router = Router::new();
1157 router.register("get_user", || async { "User data".to_string() });
1158
1159 let mut rest = RestAdapter::new();
1161 rest.route("GET", "/users/:id", "get_user");
1162 router.add_adapter(Box::new(rest));
1163
1164 let mut graphql = GraphQLAdapter::new();
1166 graphql.query("user", "get_user");
1167 router.add_adapter(Box::new(graphql));
1168
1169 let mut grpc = GrpcAdapter::new();
1171 grpc.unary("UserService", "GetUser", "get_user");
1172 router.add_adapter(Box::new(grpc));
1173
1174 let rest_response = router.route_request("rest", "GET /users/42").await;
1176 assert!(rest_response.is_ok());
1177 assert!(rest_response.unwrap().contains("get_user"));
1178
1179 let graphql_response = router.route_request("graphql", "query { user }").await;
1181 assert!(graphql_response.is_ok());
1182 assert!(graphql_response.unwrap().contains("get_user"));
1183
1184 let grpc_response = router
1186 .route_request("grpc", "UserService.GetUser:{\"id\":42}")
1187 .await;
1188 assert!(grpc_response.is_ok());
1189 assert!(grpc_response.unwrap().contains("get_user"));
1190 }
1191
1192 #[tokio::test]
1193 async fn test_integration_multiple_handlers_all_protocols() {
1194 let mut router = Router::new();
1196 router.register("get_user", || async { "User data".to_string() });
1197 router.register("list_users", || async { "Users list".to_string() });
1198 router.register("create_user", || async { "Created user".to_string() });
1199
1200 let mut rest = RestAdapter::new();
1202 rest.route("GET", "/users/:id", "get_user");
1203 rest.route("GET", "/users", "list_users");
1204 rest.route("POST", "/users", "create_user");
1205 router.add_adapter(Box::new(rest));
1206
1207 let mut graphql = GraphQLAdapter::new();
1209 graphql.query("user", "get_user");
1210 graphql.query("users", "list_users");
1211 graphql.mutation("createUser", "create_user");
1212 router.add_adapter(Box::new(graphql));
1213
1214 let mut grpc = GrpcAdapter::new();
1216 grpc.unary("UserService", "GetUser", "get_user");
1217 grpc.unary("UserService", "ListUsers", "list_users");
1218 grpc.unary("UserService", "CreateUser", "create_user");
1219 router.add_adapter(Box::new(grpc));
1220
1221 assert!(router
1223 .route_request("rest", "GET /users/42")
1224 .await
1225 .unwrap()
1226 .contains("get_user"));
1227 assert!(router
1228 .route_request("graphql", "query { user }")
1229 .await
1230 .unwrap()
1231 .contains("get_user"));
1232 assert!(router
1233 .route_request("grpc", "UserService.GetUser:{}")
1234 .await
1235 .unwrap()
1236 .contains("get_user"));
1237 }
1238
1239 #[tokio::test]
1240 async fn test_integration_error_handling_rest_404() {
1241 let mut router = Router::new();
1243
1244 let mut rest = RestAdapter::new();
1245 rest.route("GET", "/users/:id", "get_user");
1246 router.add_adapter(Box::new(rest));
1247
1248 let response = router.route_request("rest", "GET /posts/42").await;
1249 assert!(response.is_ok());
1250 assert!(response.unwrap().contains("HTTP 404"));
1251 }
1252
1253 #[tokio::test]
1254 async fn test_integration_error_handling_graphql_not_found() {
1255 let mut router = Router::new();
1257
1258 let mut graphql = GraphQLAdapter::new();
1259 graphql.query("user", "get_user");
1260 router.add_adapter(Box::new(graphql));
1261
1262 let response = router.route_request("graphql", "query { post }").await;
1263 assert!(response.is_ok());
1264 assert!(response.unwrap().contains("errors"));
1265 }
1266
1267 #[tokio::test]
1268 async fn test_integration_error_handling_grpc_unimplemented() {
1269 let mut router = Router::new();
1271
1272 let mut grpc = GrpcAdapter::new();
1273 grpc.unary("UserService", "GetUser", "get_user");
1274 router.add_adapter(Box::new(grpc));
1275
1276 let response = router.route_request("grpc", "UserService.GetPost:{}").await;
1277 assert!(response.is_ok());
1278 assert!(response.unwrap().contains("grpc-status: 12")); }
1280
1281 #[tokio::test]
1282 async fn test_integration_unknown_protocol() {
1283 let router = Router::new();
1285
1286 let response = router.route_request("unknown", "request").await;
1287 assert!(response.is_err());
1288 assert!(response.unwrap_err().contains("Adapter not found"));
1289 }
1290
1291 #[tokio::test]
1292 async fn test_integration_protocol_specific_features_rest_methods() {
1293 let mut router = Router::new();
1295 router.register("get_users", || async { "Users".to_string() });
1296 router.register("create_user", || async { "Created".to_string() });
1297 router.register("update_user", || async { "Updated".to_string() });
1298 router.register("delete_user", || async { "Deleted".to_string() });
1299
1300 let mut rest = RestAdapter::new();
1301 rest.route("GET", "/users", "get_users");
1302 rest.route("POST", "/users", "create_user");
1303 rest.route("PUT", "/users/:id", "update_user");
1304 rest.route("DELETE", "/users/:id", "delete_user");
1305 router.add_adapter(Box::new(rest));
1306
1307 assert!(router
1309 .route_request("rest", "GET /users")
1310 .await
1311 .unwrap()
1312 .contains("get_users"));
1313 assert!(router
1314 .route_request("rest", "POST /users")
1315 .await
1316 .unwrap()
1317 .contains("create_user"));
1318 assert!(router
1319 .route_request("rest", "PUT /users/42")
1320 .await
1321 .unwrap()
1322 .contains("update_user"));
1323 assert!(router
1324 .route_request("rest", "DELETE /users/42")
1325 .await
1326 .unwrap()
1327 .contains("delete_user"));
1328 }
1329
1330 #[tokio::test]
1331 async fn test_integration_protocol_specific_features_graphql_types() {
1332 let mut router = Router::new();
1334 router.register("get_user", || async { "User".to_string() });
1335 router.register("create_user", || async { "Created".to_string() });
1336
1337 let mut graphql = GraphQLAdapter::new();
1338 graphql.query("user", "get_user");
1339 graphql.mutation("createUser", "create_user");
1340 router.add_adapter(Box::new(graphql));
1341
1342 assert!(router
1344 .route_request("graphql", "query { user }")
1345 .await
1346 .unwrap()
1347 .contains("get_user"));
1348
1349 assert!(router
1351 .route_request("graphql", "mutation { createUser }")
1352 .await
1353 .unwrap()
1354 .contains("create_user"));
1355 }
1356
1357 #[tokio::test]
1358 async fn test_integration_protocol_specific_features_grpc_streaming() {
1359 let mut router = Router::new();
1361 router.register("get_user", || async { "User".to_string() });
1362 router.register("list_users", || async { "Users".to_string() });
1363
1364 let mut grpc = GrpcAdapter::new();
1365 grpc.unary("UserService", "GetUser", "get_user");
1366 grpc.server_streaming("UserService", "ListUsers", "list_users");
1367 router.add_adapter(Box::new(grpc));
1368
1369 let unary_response = router
1371 .route_request("grpc", "UserService.GetUser:{}")
1372 .await
1373 .unwrap();
1374 assert!(unary_response.contains("unary"));
1375
1376 let streaming_response = router
1378 .route_request("grpc", "UserService.ListUsers:{}")
1379 .await
1380 .unwrap();
1381 assert!(streaming_response.contains("server_streaming"));
1382 }
1383}