1use serde::de::DeserializeOwned;
65use serde::Serialize;
66use serde_json::{Map, Value};
67use futures_core::Stream;
68use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc};
69
70pub mod adapter;
71pub mod builder;
72#[cfg(feature = "router")]
73pub mod config;
74pub mod contract;
75pub mod docs;
76pub mod graphiql;
77pub mod graphql;
78pub mod grpc;
79pub mod grpc_explorer;
80pub mod handler;
81pub mod metadata;
82pub mod method;
83pub mod openapi;
84pub mod rest;
85pub mod scalar;
86pub mod schema;
87pub mod ts_codegen;
88
89#[cfg(feature = "router-graphql")]
91pub mod graphql_prod;
92#[cfg(feature = "router-grpc")]
93pub mod grpc_prod;
94
95pub use adapter::ProtocolAdapter;
96pub use builder::RouteBuilder;
97#[cfg(feature = "router")]
98pub use config::{GraphQLConfig, GrpcConfig, RestConfig, RouterConfig, ServerConfig};
99pub use contract::{
100 ContractTestConfig, ContractTestResult, ContractTestResults, ContractTestable, ContractTester,
101};
102pub use docs::DocsConfig;
103pub use graphiql::{graphiql_html, GraphiQLConfig, GraphiQLTheme};
104pub use graphql::{GraphQLAdapter, GraphQLOperation, OperationType};
105#[cfg(feature = "router-graphql")]
107pub use graphql_prod::GraphQLProductionAdapter;
108pub use grpc::{GrpcAdapter, GrpcMethod, GrpcMethodType, GrpcRequest, GrpcStatus};
109pub use grpc_explorer::{grpc_explorer_html, GrpcExplorerConfig, GrpcExplorerTheme};
110#[cfg(feature = "router-grpc")]
111pub use grpc_prod::{protobuf, status, streaming, GrpcProductionAdapter, GrpcService};
112pub use handler::{
113 ErasedHandler, ErasedStreamHandler, Handler, HandlerCallFn, HandlerFn, HandlerWithArgs,
114 HandlerWithState, HandlerWithStateOnly, IntoHandlerResult, IntoStreamItem, Json,
115 SharedStateMap, State, StreamError, StreamHandler, StreamHandlerCallFn, StreamReceiver,
116 StreamSender, StreamingHandlerFn, StreamingHandlerWithArgs, StreamingHandlerWithState,
117 StreamingHandlerWithStateOnly, DEFAULT_STREAM_CAPACITY,
118};
119pub use handler::resolve_state;
120pub use handler::resolve_state_erased;
121pub use metadata::RouteMetadata;
122pub use method::Method;
123pub use openapi::{OpenApiGenerator, OpenApiServer};
124pub use rest::{RestAdapter, RestRequest, RestResponse, RestRoute};
125pub use scalar::{scalar_html, ScalarConfig, ScalarLayout, ScalarTheme};
126pub use schema::ToJsonSchema;
127pub use ts_codegen::{generate_ts_client, HandlerMeta, TsField, TsType};
128
129async fn drive_stream<T, St>(stream: St, tx: &StreamSender) -> String
134where
135 T: IntoStreamItem,
136 St: Stream<Item = T> + Send,
137{
138 tokio::pin!(stream);
139 loop {
140 let next = std::future::poll_fn(|cx| stream.as_mut().poll_next(cx)).await;
141 match next {
142 Some(item) => match tx.send(item).await {
143 Ok(()) => {}
144 Err(StreamError::Closed) => break,
145 Err(StreamError::Serialize(e)) => {
146 return serde_json::json!({"error": e}).to_string();
147 }
148 },
149 None => break,
150 }
151 }
152 "null".to_string()
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum KeyTransform {
172 CamelToSnake,
174}
175
176fn camel_to_snake(s: &str) -> String {
178 let mut result = String::with_capacity(s.len() + 4);
179 for (i, ch) in s.chars().enumerate() {
180 if ch.is_ascii_uppercase() {
181 if i > 0 {
182 result.push('_');
183 }
184 result.push(ch.to_ascii_lowercase());
185 } else {
186 result.push(ch);
187 }
188 }
189 result
190}
191
192fn transform_keys(value: Value, transform: KeyTransform) -> Value {
194 match value {
195 Value::Object(map) => {
196 let new_map: Map<String, Value> = map
197 .into_iter()
198 .map(|(k, v)| {
199 let new_key = match transform {
200 KeyTransform::CamelToSnake => camel_to_snake(&k),
201 };
202 (new_key, transform_keys(v, transform))
203 })
204 .collect();
205 Value::Object(new_map)
206 }
207 Value::Array(arr) => {
208 Value::Array(arr.into_iter().map(|v| transform_keys(v, transform)).collect())
209 }
210 other => other,
211 }
212}
213
214fn apply_key_transform(args: &str, transform: KeyTransform) -> String {
219 match serde_json::from_str::<Value>(args) {
220 Ok(value) => transform_keys(value, transform).to_string(),
221 Err(_) => args.to_string(),
222 }
223}
224
225pub struct Router {
230 handlers: HashMap<String, Box<dyn Handler>>,
231 streaming_handlers: HashMap<String, Box<dyn StreamHandler>>,
232 adapters: HashMap<String, Box<dyn ProtocolAdapter>>,
233 routes: Vec<RouteMetadata>,
234 states: SharedStateMap,
235 handler_metas: HashMap<String, HandlerMeta>,
236 key_transform: Option<KeyTransform>,
237 #[cfg(feature = "router")]
238 #[allow(dead_code)]
239 config: Option<RouterConfig>,
240}
241
242impl Router {
243 pub fn new() -> Self {
245 Self {
246 handlers: HashMap::new(),
247 streaming_handlers: HashMap::new(),
248 adapters: HashMap::new(),
249 routes: Vec::new(),
250 states: Arc::new(std::sync::RwLock::new(HashMap::new())),
251 handler_metas: HashMap::new(),
252 key_transform: None,
253 #[cfg(feature = "router")]
254 config: None,
255 }
256 }
257
258 #[cfg(feature = "router")]
260 pub fn with_config(config: RouterConfig) -> Self {
261 let mut router = Self {
262 handlers: HashMap::new(),
263 streaming_handlers: HashMap::new(),
264 adapters: HashMap::new(),
265 routes: Vec::new(),
266 states: Arc::new(std::sync::RwLock::new(HashMap::new())),
267 handler_metas: HashMap::new(),
268 key_transform: None,
269 config: Some(config.clone()),
270 };
271
272 if config.has_protocol("rest") {
274 router.add_adapter(Box::new(RestAdapter::new()));
275 }
276 if config.has_protocol("graphql") {
277 router.add_adapter(Box::new(GraphQLAdapter::new()));
278 }
279 if config.has_protocol("grpc") {
280 router.add_adapter(Box::new(GrpcAdapter::new()));
281 }
282
283 router
284 }
285
286 pub fn with_key_transform(mut self, transform: KeyTransform) -> Self {
303 self.key_transform = Some(transform);
304 self
305 }
306
307 pub fn with_state<S: Send + Sync + 'static>(mut self, state: S) -> Self {
319 self.insert_state::<S>(state);
320 self
321 }
322
323 pub fn inject_state<S: Send + Sync + 'static>(&mut self, state: S) {
330 self.insert_state::<S>(state);
331 }
332
333 fn insert_state<S: Send + Sync + 'static>(&mut self, state: S) {
334 let id = std::any::TypeId::of::<S>();
335 let mut map = self.states.write().expect("state lock poisoned");
336 if map.contains_key(&id) {
337 #[cfg(debug_assertions)]
338 eprintln!(
339 "allframe: with_state called twice for type `{}` — previous value replaced",
340 std::any::type_name::<S>()
341 );
342 }
343 map.insert(id, Arc::new(state));
344 }
345
346 pub fn shared_states(&self) -> SharedStateMap {
352 self.states.clone()
353 }
354
355 pub fn register<F, Fut>(&mut self, name: &str, handler: F)
357 where
358 F: Fn() -> Fut + Send + Sync + 'static,
359 Fut: Future<Output = String> + Send + 'static,
360 {
361 self.handlers
362 .insert(name.to_string(), Box::new(ErasedHandler::no_args(handler)));
363 }
364
365 pub fn register_with_args<T, F, Fut>(&mut self, name: &str, handler: F)
367 where
368 T: DeserializeOwned + Send + 'static,
369 F: Fn(T) -> Fut + Send + Sync + 'static,
370 Fut: Future<Output = String> + Send + 'static,
371 {
372 self.handlers
373 .insert(name.to_string(), Box::new(ErasedHandler::with_args(handler)));
374 }
375
376 pub fn register_with_state<S, T, F, Fut>(&mut self, name: &str, handler: F)
382 where
383 S: Send + Sync + 'static,
384 T: DeserializeOwned + Send + 'static,
385 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
386 Fut: Future<Output = String> + Send + 'static,
387 {
388 let state = self.states.clone();
389 self.handlers
390 .insert(name.to_string(), Box::new(ErasedHandler::with_state(handler, state)));
391 }
392
393 pub fn register_with_state_only<S, F, Fut>(&mut self, name: &str, handler: F)
399 where
400 S: Send + Sync + 'static,
401 F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
402 Fut: Future<Output = String> + Send + 'static,
403 {
404 let state = self.states.clone();
405 self.handlers
406 .insert(name.to_string(), Box::new(ErasedHandler::with_state_only(handler, state)));
407 }
408
409 pub fn register_typed<R, F, Fut>(&mut self, name: &str, handler: F)
413 where
414 R: Serialize + Send + 'static,
415 F: Fn() -> Fut + Send + Sync + 'static,
416 Fut: Future<Output = R> + Send + 'static,
417 {
418 let wrapped = move || {
419 let fut = handler();
420 async move { Json(fut.await) }
421 };
422 self.handlers
423 .insert(name.to_string(), Box::new(ErasedHandler::no_args(wrapped)));
424 }
425
426 pub fn register_typed_with_args<T, R, F, Fut>(&mut self, name: &str, handler: F)
428 where
429 T: DeserializeOwned + Send + 'static,
430 R: Serialize + Send + 'static,
431 F: Fn(T) -> Fut + Send + Sync + 'static,
432 Fut: Future<Output = R> + Send + 'static,
433 {
434 let wrapped = move |args: T| {
435 let fut = handler(args);
436 async move { Json(fut.await) }
437 };
438 self.handlers
439 .insert(name.to_string(), Box::new(ErasedHandler::with_args(wrapped)));
440 }
441
442 pub fn register_typed_with_state<S, T, R, F, Fut>(&mut self, name: &str, handler: F)
444 where
445 S: Send + Sync + 'static,
446 T: DeserializeOwned + Send + 'static,
447 R: Serialize + Send + 'static,
448 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
449 Fut: Future<Output = R> + Send + 'static,
450 {
451 let state = self.states.clone();
452 let wrapped = move |s: State<Arc<S>>, args: T| {
453 let fut = handler(s, args);
454 async move { Json(fut.await) }
455 };
456 self.handlers
457 .insert(name.to_string(), Box::new(ErasedHandler::with_state(wrapped, state)));
458 }
459
460 pub fn register_typed_with_state_only<S, R, F, Fut>(&mut self, name: &str, handler: F)
462 where
463 S: Send + Sync + 'static,
464 R: Serialize + Send + 'static,
465 F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
466 Fut: Future<Output = R> + Send + 'static,
467 {
468 let state = self.states.clone();
469 let wrapped = move |s: State<Arc<S>>| {
470 let fut = handler(s);
471 async move { Json(fut.await) }
472 };
473 self.handlers
474 .insert(name.to_string(), Box::new(ErasedHandler::with_state_only(wrapped, state)));
475 }
476
477 pub fn register_result<R, E, F, Fut>(&mut self, name: &str, handler: F)
484 where
485 R: Serialize + Send + 'static,
486 E: std::fmt::Display + Send + 'static,
487 F: Fn() -> Fut + Send + Sync + 'static,
488 Fut: Future<Output = Result<R, E>> + Send + 'static,
489 {
490 self.handlers
491 .insert(name.to_string(), Box::new(ErasedHandler::no_args(handler)));
492 }
493
494 pub fn register_result_with_args<T, R, E, F, Fut>(&mut self, name: &str, handler: F)
496 where
497 T: DeserializeOwned + Send + 'static,
498 R: Serialize + Send + 'static,
499 E: std::fmt::Display + Send + 'static,
500 F: Fn(T) -> Fut + Send + Sync + 'static,
501 Fut: Future<Output = Result<R, E>> + Send + 'static,
502 {
503 self.handlers
504 .insert(name.to_string(), Box::new(ErasedHandler::with_args(handler)));
505 }
506
507 pub fn register_result_with_state<S, T, R, E, F, Fut>(&mut self, name: &str, handler: F)
509 where
510 S: Send + Sync + 'static,
511 T: DeserializeOwned + Send + 'static,
512 R: Serialize + Send + 'static,
513 E: std::fmt::Display + Send + 'static,
514 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
515 Fut: Future<Output = Result<R, E>> + Send + 'static,
516 {
517 let state = self.states.clone();
518 self.handlers
519 .insert(name.to_string(), Box::new(ErasedHandler::with_state(handler, state)));
520 }
521
522 pub fn register_result_with_state_only<S, R, E, F, Fut>(&mut self, name: &str, handler: F)
524 where
525 S: Send + Sync + 'static,
526 R: Serialize + Send + 'static,
527 E: std::fmt::Display + Send + 'static,
528 F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
529 Fut: Future<Output = Result<R, E>> + Send + 'static,
530 {
531 let state = self.states.clone();
532 self.handlers
533 .insert(name.to_string(), Box::new(ErasedHandler::with_state_only(handler, state)));
534 }
535
536 pub fn handlers_count(&self) -> usize {
538 self.handlers.len()
539 }
540
541 pub fn register_erased(&mut self, name: &str, handler: ErasedHandler) {
569 self.handlers.insert(name.to_string(), Box::new(handler));
570 }
571
572 pub fn register_streaming_erased(&mut self, name: &str, handler: ErasedStreamHandler) {
576 self.streaming_handlers
577 .insert(name.to_string(), Box::new(handler));
578 }
579
580
581 pub fn register_streaming<F, Fut, R>(&mut self, name: &str, handler: F)
585 where
586 F: Fn(StreamSender) -> Fut + Send + Sync + 'static,
587 Fut: Future<Output = R> + Send + 'static,
588 R: IntoHandlerResult + 'static,
589 {
590 self.streaming_handlers
591 .insert(name.to_string(), Box::new(ErasedStreamHandler::no_args(handler)));
592 }
593
594 pub fn register_streaming_with_args<T, F, Fut, R>(&mut self, name: &str, handler: F)
596 where
597 T: DeserializeOwned + Send + 'static,
598 F: Fn(T, StreamSender) -> Fut + Send + Sync + 'static,
599 Fut: Future<Output = R> + Send + 'static,
600 R: IntoHandlerResult + 'static,
601 {
602 self.streaming_handlers
603 .insert(name.to_string(), Box::new(ErasedStreamHandler::with_args(handler)));
604 }
605
606 pub fn register_streaming_with_state<S, T, F, Fut, R>(&mut self, name: &str, handler: F)
608 where
609 S: Send + Sync + 'static,
610 T: DeserializeOwned + Send + 'static,
611 F: Fn(State<Arc<S>>, T, StreamSender) -> Fut + Send + Sync + 'static,
612 Fut: Future<Output = R> + Send + 'static,
613 R: IntoHandlerResult + 'static,
614 {
615 let state = self.states.clone();
616 self.streaming_handlers
617 .insert(name.to_string(), Box::new(ErasedStreamHandler::with_state(handler, state)));
618 }
619
620 pub fn register_streaming_with_state_only<S, F, Fut, R>(&mut self, name: &str, handler: F)
622 where
623 S: Send + Sync + 'static,
624 F: Fn(State<Arc<S>>, StreamSender) -> Fut + Send + Sync + 'static,
625 Fut: Future<Output = R> + Send + 'static,
626 R: IntoHandlerResult + 'static,
627 {
628 let state = self.states.clone();
629 self.streaming_handlers
630 .insert(name.to_string(), Box::new(ErasedStreamHandler::with_state_only(handler, state)));
631 }
632
633 pub fn register_stream<T, St, F, Fut>(&mut self, name: &str, handler: F)
642 where
643 T: IntoStreamItem + 'static,
644 St: Stream<Item = T> + Send + 'static,
645 F: Fn() -> Fut + Send + Sync + 'static,
646 Fut: Future<Output = St> + Send + 'static,
647 {
648 self.register_streaming(name, move |tx: StreamSender| {
649 let stream_fut = handler();
650 async move {
651 drive_stream(stream_fut.await, &tx).await
652 }
653 });
654 }
655
656 pub fn register_stream_with_args<T, Item, St, F, Fut>(&mut self, name: &str, handler: F)
658 where
659 T: DeserializeOwned + Send + 'static,
660 Item: IntoStreamItem + 'static,
661 St: Stream<Item = Item> + Send + 'static,
662 F: Fn(T) -> Fut + Send + Sync + 'static,
663 Fut: Future<Output = St> + Send + 'static,
664 {
665 self.register_streaming_with_args::<T, _, _, _>(name, move |args: T, tx: StreamSender| {
666 let stream_fut = handler(args);
667 async move {
668 drive_stream(stream_fut.await, &tx).await
669 }
670 });
671 }
672
673 pub fn register_stream_with_state<S, T, Item, St, F, Fut>(&mut self, name: &str, handler: F)
675 where
676 S: Send + Sync + 'static,
677 T: DeserializeOwned + Send + 'static,
678 Item: IntoStreamItem + 'static,
679 St: Stream<Item = Item> + Send + 'static,
680 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
681 Fut: Future<Output = St> + Send + 'static,
682 {
683 self.register_streaming_with_state::<S, T, _, _, _>(name, move |state: State<Arc<S>>, args: T, tx: StreamSender| {
684 let stream_fut = handler(state, args);
685 async move {
686 drive_stream(stream_fut.await, &tx).await
687 }
688 });
689 }
690
691 pub fn is_streaming(&self, name: &str) -> bool {
693 self.streaming_handlers.contains_key(name)
694 }
695
696 #[allow(clippy::type_complexity)]
706 pub fn call_streaming_handler(
707 &self,
708 name: &str,
709 args: &str,
710 ) -> Result<
711 (
712 StreamReceiver,
713 Pin<Box<dyn Future<Output = Result<String, String>> + Send + '_>>,
714 ),
715 String,
716 > {
717 let handler = self
718 .streaming_handlers
719 .get(name)
720 .ok_or_else(|| format!("Streaming handler '{}' not found", name))?;
721
722 let transformed = self.maybe_transform_args(args);
723 let args = transformed.as_deref().unwrap_or(args);
724
725 let (tx, rx) = StreamSender::channel();
726 let fut = handler.call_streaming(args, tx);
727 Ok((rx, fut))
728 }
729
730 #[allow(clippy::type_complexity)]
735 pub fn spawn_streaming_handler(
736 self: &Arc<Self>,
737 name: &str,
738 args: &str,
739 ) -> Result<
740 (
741 StreamReceiver,
742 tokio::task::JoinHandle<Result<String, String>>,
743 ),
744 String,
745 > {
746 if !self.streaming_handlers.contains_key(name) {
747 return Err(format!("Streaming handler '{}' not found", name));
748 }
749
750 let router = self.clone();
751 let name = name.to_string();
752 let args = match self.maybe_transform_args(args) {
753 Some(t) => t,
754 None => args.to_string(),
755 };
756
757 let (tx, rx) = StreamSender::channel();
758
759 let handle = tokio::spawn(async move {
760 let handler = router
761 .streaming_handlers
762 .get(&name)
763 .expect("handler verified to exist");
764 handler.call_streaming(&args, tx).await
765 });
766
767 Ok((rx, handle))
768 }
769
770 pub fn describe_handler(
790 &mut self,
791 name: &str,
792 args: Vec<TsField>,
793 returns: TsType,
794 ) {
795 assert!(
796 self.handlers.contains_key(name),
797 "describe_handler: handler '{}' not registered",
798 name
799 );
800 self.handler_metas
801 .insert(name.to_string(), HandlerMeta::new(args, returns));
802 }
803
804 pub fn describe_streaming_handler(
806 &mut self,
807 name: &str,
808 args: Vec<TsField>,
809 item_type: TsType,
810 final_type: TsType,
811 ) {
812 assert!(
813 self.streaming_handlers.contains_key(name),
814 "describe_streaming_handler: streaming handler '{}' not registered",
815 name
816 );
817 self.handler_metas
818 .insert(name.to_string(), HandlerMeta::streaming(args, item_type, final_type));
819 }
820
821 pub fn generate_ts_client(&self) -> String {
844 generate_ts_client(&self.handler_metas)
845 }
846
847 pub fn handler_meta(&self, name: &str) -> Option<&HandlerMeta> {
849 self.handler_metas.get(name)
850 }
851
852 pub fn add_adapter(&mut self, adapter: Box<dyn ProtocolAdapter>) {
854 self.adapters.insert(adapter.name().to_string(), adapter);
855 }
856
857 pub fn has_adapter(&self, name: &str) -> bool {
859 self.adapters.contains_key(name)
860 }
861
862 pub fn get_adapter(&self, name: &str) -> Option<&dyn ProtocolAdapter> {
864 self.adapters.get(name).map(|b| &**b)
865 }
866
867 pub async fn route_request(&self, protocol: &str, request: &str) -> Result<String, String> {
869 let adapter = self
870 .get_adapter(protocol)
871 .ok_or_else(|| format!("Adapter not found: {}", protocol))?;
872
873 adapter.handle(request).await
874 }
875
876 pub async fn execute(&self, name: &str) -> Result<String, String> {
878 self.execute_with_args(name, "{}").await
879 }
880
881 fn maybe_transform_args(&self, args: &str) -> Option<String> {
883 self.key_transform.map(|t| apply_key_transform(args, t))
884 }
885
886 pub async fn execute_with_args(&self, name: &str, args: &str) -> Result<String, String> {
888 let transformed;
889 let args = match self.maybe_transform_args(args) {
890 Some(t) => {
891 transformed = t;
892 &transformed
893 }
894 None => args,
895 };
896 match self.handlers.get(name) {
897 Some(handler) => handler.call(args).await,
898 None => Err(format!("Handler '{}' not found", name)),
899 }
900 }
901
902 pub fn list_handlers(&self) -> Vec<String> {
907 let mut names: Vec<String> = self.handlers.keys().cloned().collect();
908 names.extend(self.streaming_handlers.keys().cloned());
909 names
910 }
911
912 pub async fn call_handler(&self, name: &str, request: &str) -> Result<String, String> {
917 self.execute_with_args(name, request).await
918 }
919
920 pub fn can_handle_rest(&self, _name: &str) -> bool {
922 self.has_adapter("rest")
923 }
924
925 pub fn can_handle_graphql(&self, _name: &str) -> bool {
927 self.has_adapter("graphql")
928 }
929
930 pub fn can_handle_grpc(&self, _name: &str) -> bool {
932 self.has_adapter("grpc")
933 }
934
935 pub fn enabled_protocols(&self) -> Vec<String> {
937 self.adapters.keys().cloned().collect()
938 }
939
940 pub fn add_route(&mut self, metadata: RouteMetadata) {
945 self.routes.push(metadata);
946 }
947
948 pub fn routes(&self) -> &[RouteMetadata] {
953 &self.routes
954 }
955
956 pub fn get<F, Fut>(&mut self, path: &str, handler: F)
962 where
963 F: Fn() -> Fut + Send + Sync + 'static,
964 Fut: Future<Output = String> + Send + 'static,
965 {
966 let handler_name = format!("GET:{}", path);
967 self.register(&handler_name, handler);
968 self.add_route(RouteMetadata::new(path, Method::GET, "rest"));
969 }
970
971 pub fn post<F, Fut>(&mut self, path: &str, handler: F)
977 where
978 F: Fn() -> Fut + Send + Sync + 'static,
979 Fut: Future<Output = String> + Send + 'static,
980 {
981 let handler_name = format!("POST:{}", path);
982 self.register(&handler_name, handler);
983 self.add_route(RouteMetadata::new(path, Method::POST, "rest"));
984 }
985
986 pub fn put<F, Fut>(&mut self, path: &str, handler: F)
992 where
993 F: Fn() -> Fut + Send + Sync + 'static,
994 Fut: Future<Output = String> + Send + 'static,
995 {
996 let handler_name = format!("PUT:{}", path);
997 self.register(&handler_name, handler);
998 self.add_route(RouteMetadata::new(path, Method::PUT, "rest"));
999 }
1000
1001 pub fn delete<F, Fut>(&mut self, path: &str, handler: F)
1007 where
1008 F: Fn() -> Fut + Send + Sync + 'static,
1009 Fut: Future<Output = String> + Send + 'static,
1010 {
1011 let handler_name = format!("DELETE:{}", path);
1012 self.register(&handler_name, handler);
1013 self.add_route(RouteMetadata::new(path, Method::DELETE, "rest"));
1014 }
1015
1016 pub fn patch<F, Fut>(&mut self, path: &str, handler: F)
1022 where
1023 F: Fn() -> Fut + Send + Sync + 'static,
1024 Fut: Future<Output = String> + Send + 'static,
1025 {
1026 let handler_name = format!("PATCH:{}", path);
1027 self.register(&handler_name, handler);
1028 self.add_route(RouteMetadata::new(path, Method::PATCH, "rest"));
1029 }
1030
1031 pub fn head<F, Fut>(&mut self, path: &str, handler: F)
1037 where
1038 F: Fn() -> Fut + Send + Sync + 'static,
1039 Fut: Future<Output = String> + Send + 'static,
1040 {
1041 let handler_name = format!("HEAD:{}", path);
1042 self.register(&handler_name, handler);
1043 self.add_route(RouteMetadata::new(path, Method::HEAD, "rest"));
1044 }
1045
1046 pub fn options<F, Fut>(&mut self, path: &str, handler: F)
1052 where
1053 F: Fn() -> Fut + Send + Sync + 'static,
1054 Fut: Future<Output = String> + Send + 'static,
1055 {
1056 let handler_name = format!("OPTIONS:{}", path);
1057 self.register(&handler_name, handler);
1058 self.add_route(RouteMetadata::new(path, Method::OPTIONS, "rest"));
1059 }
1060
1061 pub async fn call_rest(&self, method: &str, path: &str) -> Result<String, String> {
1063 let adapter = self
1064 .adapters
1065 .get("rest")
1066 .ok_or_else(|| "REST adapter not enabled".to_string())?;
1067
1068 let request = format!("{} {}", method, path);
1069 adapter.handle(&request).await
1070 }
1071
1072 pub async fn call_graphql(&self, query: &str) -> Result<String, String> {
1074 let adapter = self
1075 .adapters
1076 .get("graphql")
1077 .ok_or_else(|| "GraphQL adapter not enabled".to_string())?;
1078
1079 adapter.handle(query).await
1080 }
1081
1082 pub async fn call_grpc(&self, method: &str, request: &str) -> Result<String, String> {
1084 let adapter = self
1085 .adapters
1086 .get("grpc")
1087 .ok_or_else(|| "gRPC adapter not enabled".to_string())?;
1088
1089 let grpc_request = format!("{}:{}", method, request);
1090 adapter.handle(&grpc_request).await
1091 }
1092
1093 pub fn scalar(&self, title: &str, version: &str) -> String {
1115 let config = scalar::ScalarConfig::default();
1116 self.scalar_docs(config, title, version)
1117 }
1118
1119 pub fn scalar_docs(&self, config: scalar::ScalarConfig, title: &str, version: &str) -> String {
1145 let spec = OpenApiGenerator::new(title, version).generate(self);
1147 let spec_json = serde_json::to_string(&spec).unwrap_or_else(|_| "{}".to_string());
1148
1149 scalar::scalar_html(&config, title, &spec_json)
1151 }
1152}
1153
1154impl Default for Router {
1155 fn default() -> Self {
1156 Self::new()
1157 }
1158}
1159
1160#[macro_export]
1219macro_rules! register_handlers {
1220 ($router:expr, [ $($entry:tt)* ]) => {
1221 $crate::register_handlers!(@entries $router, $($entry)*)
1222 };
1223
1224 (@entries $router:expr, ) => {};
1226
1227 (@entries $router:expr, $name:literal => $handler:path, $($rest:tt)*) => {
1229 $router.register($name, $handler);
1230 $crate::register_handlers!(@entries $router, $($rest)*)
1231 };
1232
1233 (@entries $router:expr, args $name:literal => $handler:path, $($rest:tt)*) => {
1235 $router.register_with_args($name, $handler);
1236 $crate::register_handlers!(@entries $router, $($rest)*)
1237 };
1238
1239 (@entries $router:expr, streaming $name:literal => $handler:path, $($rest:tt)*) => {
1241 $router.register_streaming($name, $handler);
1242 $crate::register_handlers!(@entries $router, $($rest)*)
1243 };
1244
1245 (@entries $router:expr, streaming args $name:literal => $handler:path, $($rest:tt)*) => {
1247 $router.register_streaming_with_args($name, $handler);
1248 $crate::register_handlers!(@entries $router, $($rest)*)
1249 };
1250
1251 (@entries $router:expr, state $name:literal => $handler:path, $($rest:tt)*) => {
1253 $router.register_with_state_only($name, $handler);
1254 $crate::register_handlers!(@entries $router, $($rest)*)
1255 };
1256
1257 (@entries $router:expr, state args $name:literal => $handler:path, $($rest:tt)*) => {
1259 $router.register_with_state($name, $handler);
1260 $crate::register_handlers!(@entries $router, $($rest)*)
1261 };
1262
1263 (@entries $router:expr, state streaming $name:literal => $handler:path, $($rest:tt)*) => {
1265 $router.register_streaming_with_state_only($name, $handler);
1266 $crate::register_handlers!(@entries $router, $($rest)*)
1267 };
1268
1269 (@entries $router:expr, state streaming args $name:literal => $handler:path, $($rest:tt)*) => {
1271 $router.register_streaming_with_state($name, $handler);
1272 $crate::register_handlers!(@entries $router, $($rest)*)
1273 };
1274}
1275
1276#[cfg(test)]
1277mod tests {
1278 use super::*;
1279
1280 #[tokio::test]
1281 async fn test_router_creation() {
1282 let router = Router::new();
1283 assert_eq!(router.handlers_count(), 0);
1284 }
1285
1286 #[tokio::test]
1287 async fn test_handler_registration() {
1288 let mut router = Router::new();
1289 router.register("test", || async { "Hello".to_string() });
1290 assert_eq!(router.handlers_count(), 1);
1291 }
1292
1293 #[tokio::test]
1294 async fn test_handler_execution() {
1295 let mut router = Router::new();
1296 router.register("test", || async { "Hello".to_string() });
1297 let result = router.execute("test").await;
1298 assert_eq!(result, Ok("Hello".to_string()));
1299 }
1300
1301 #[tokio::test]
1303 async fn test_router_starts_with_no_routes() {
1304 let router = Router::new();
1305 let routes = router.routes();
1306 assert_eq!(routes.len(), 0);
1307 }
1308
1309 #[tokio::test]
1310 async fn test_add_route_metadata() {
1311 let mut router = Router::new();
1312 let metadata = RouteMetadata::new("/users", "GET", "rest");
1313
1314 router.add_route(metadata.clone());
1315
1316 let routes = router.routes();
1317 assert_eq!(routes.len(), 1);
1318 assert_eq!(routes[0].path, "/users");
1319 assert_eq!(routes[0].method, "GET");
1320 assert_eq!(routes[0].protocol, "rest");
1321 }
1322
1323 #[tokio::test]
1324 async fn test_add_multiple_routes() {
1325 let mut router = Router::new();
1326
1327 router.add_route(RouteMetadata::new("/users", "GET", "rest"));
1328 router.add_route(RouteMetadata::new("/users", "POST", "rest"));
1329 router.add_route(RouteMetadata::new("/posts", "GET", "rest"));
1330
1331 let routes = router.routes();
1332 assert_eq!(routes.len(), 3);
1333 }
1334
1335 #[tokio::test]
1336 async fn test_routes_with_different_protocols() {
1337 let mut router = Router::new();
1338
1339 router.add_route(RouteMetadata::new("/users", "GET", "rest"));
1340 router.add_route(RouteMetadata::new("users", "query", "graphql"));
1341 router.add_route(RouteMetadata::new("UserService.GetUser", "unary", "grpc"));
1342
1343 let routes = router.routes();
1344 assert_eq!(routes.len(), 3);
1345
1346 assert_eq!(routes[0].protocol, "rest");
1347 assert_eq!(routes[1].protocol, "graphql");
1348 assert_eq!(routes[2].protocol, "grpc");
1349 }
1350
1351 #[tokio::test]
1352 async fn test_routes_returns_immutable_reference() {
1353 let mut router = Router::new();
1354 router.add_route(RouteMetadata::new("/test", "GET", "rest"));
1355
1356 let routes1 = router.routes();
1357 let routes2 = router.routes();
1358
1359 assert_eq!(routes1.len(), routes2.len());
1361 assert_eq!(routes1[0].path, routes2[0].path);
1362 }
1363
1364 #[tokio::test]
1366 async fn test_route_get_method() {
1367 let mut router = Router::new();
1368 router.get("/users", || async { "User list".to_string() });
1369
1370 let routes = router.routes();
1371 assert_eq!(routes.len(), 1);
1372 assert_eq!(routes[0].path, "/users");
1373 assert_eq!(routes[0].method, "GET");
1374 assert_eq!(routes[0].protocol, "rest");
1375 }
1376
1377 #[tokio::test]
1378 async fn test_route_post_method() {
1379 let mut router = Router::new();
1380 router.post("/users", || async { "User created".to_string() });
1381
1382 let routes = router.routes();
1383 assert_eq!(routes.len(), 1);
1384 assert_eq!(routes[0].path, "/users");
1385 assert_eq!(routes[0].method, "POST");
1386 assert_eq!(routes[0].protocol, "rest");
1387 }
1388
1389 #[tokio::test]
1390 async fn test_route_put_method() {
1391 let mut router = Router::new();
1392 router.put("/users/1", || async { "User updated".to_string() });
1393
1394 let routes = router.routes();
1395 assert_eq!(routes.len(), 1);
1396 assert_eq!(routes[0].method, "PUT");
1397 }
1398
1399 #[tokio::test]
1400 async fn test_route_delete_method() {
1401 let mut router = Router::new();
1402 router.delete("/users/1", || async { "User deleted".to_string() });
1403
1404 let routes = router.routes();
1405 assert_eq!(routes.len(), 1);
1406 assert_eq!(routes[0].method, "DELETE");
1407 }
1408
1409 #[tokio::test]
1410 async fn test_route_patch_method() {
1411 let mut router = Router::new();
1412 router.patch("/users/1", || async { "User patched".to_string() });
1413
1414 let routes = router.routes();
1415 assert_eq!(routes.len(), 1);
1416 assert_eq!(routes[0].method, "PATCH");
1417 }
1418
1419 #[tokio::test]
1420 async fn test_multiple_routes_different_methods() {
1421 let mut router = Router::new();
1422 router.get("/users", || async { "List".to_string() });
1423 router.post("/users", || async { "Create".to_string() });
1424 router.put("/users/1", || async { "Update".to_string() });
1425 router.delete("/users/1", || async { "Delete".to_string() });
1426
1427 let routes = router.routes();
1428 assert_eq!(routes.len(), 4);
1429
1430 assert_eq!(routes[0].method, "GET");
1431 assert_eq!(routes[1].method, "POST");
1432 assert_eq!(routes[2].method, "PUT");
1433 assert_eq!(routes[3].method, "DELETE");
1434 }
1435
1436 #[tokio::test]
1437 async fn test_route_method_with_path_params() {
1438 let mut router = Router::new();
1439 router.get("/users/{id}", || async { "User details".to_string() });
1440 router.get("/users/{id}/posts/{post_id}", || async {
1441 "Post details".to_string()
1442 });
1443
1444 let routes = router.routes();
1445 assert_eq!(routes.len(), 2);
1446 assert_eq!(routes[0].path, "/users/{id}");
1447 assert_eq!(routes[1].path, "/users/{id}/posts/{post_id}");
1448 }
1449
1450 #[tokio::test]
1451 async fn test_route_registration_and_execution() {
1452 let mut router = Router::new();
1453 router.get("/test", || async { "GET response".to_string() });
1454 router.post("/test", || async { "POST response".to_string() });
1455
1456 assert_eq!(router.routes().len(), 2);
1458 assert_eq!(router.handlers_count(), 2);
1459
1460 let result1 = router.execute("GET:/test").await;
1462 let result2 = router.execute("POST:/test").await;
1463
1464 assert_eq!(result1, Ok("GET response".to_string()));
1465 assert_eq!(result2, Ok("POST response".to_string()));
1466 }
1467
1468 #[tokio::test]
1470 async fn test_scalar_generates_html() {
1471 let mut router = Router::new();
1472 router.get("/users", || async { "Users".to_string() });
1473
1474 let html = router.scalar("Test API", "1.0.0");
1475
1476 assert!(html.contains("<!DOCTYPE html>"));
1477 assert!(html.contains("<title>Test API - API Documentation</title>"));
1478 assert!(html.contains("@scalar/api-reference"));
1479 }
1480
1481 #[tokio::test]
1482 async fn test_scalar_contains_openapi_spec() {
1483 let mut router = Router::new();
1484 router.get("/users", || async { "Users".to_string() });
1485 router.post("/users", || async { "User created".to_string() });
1486
1487 let html = router.scalar("Test API", "1.0.0");
1488
1489 assert!(html.contains("openapi"));
1491 assert!(html.contains("Test API"));
1492 assert!(html.contains("1.0.0"));
1493 }
1494
1495 #[tokio::test]
1496 async fn test_scalar_docs_with_custom_config() {
1497 let mut router = Router::new();
1498 router.get("/users", || async { "Users".to_string() });
1499
1500 let config = scalar::ScalarConfig::new()
1501 .theme(scalar::ScalarTheme::Light)
1502 .show_sidebar(false);
1503
1504 let html = router.scalar_docs(config, "Custom API", "2.0.0");
1505
1506 assert!(html.contains("Custom API"));
1507 assert!(html.contains(r#""theme":"light""#));
1508 assert!(html.contains(r#""showSidebar":false"#));
1509 }
1510
1511 #[tokio::test]
1512 async fn test_scalar_docs_with_custom_css() {
1513 let mut router = Router::new();
1514 router.get("/test", || async { "Test".to_string() });
1515
1516 let config = scalar::ScalarConfig::new().custom_css("body { font-family: 'Inter'; }");
1517
1518 let html = router.scalar_docs(config, "API", "1.0");
1519
1520 assert!(html.contains("<style>body { font-family: 'Inter'; }</style>"));
1521 }
1522
1523 #[tokio::test]
1524 async fn test_scalar_with_multiple_routes() {
1525 let mut router = Router::new();
1526 router.get("/users", || async { "Users".to_string() });
1527 router.post("/users", || async { "Create".to_string() });
1528 router.get("/users/{id}", || async { "User details".to_string() });
1529 router.delete("/users/{id}", || async { "Delete".to_string() });
1530
1531 let html = router.scalar("API", "1.0.0");
1532
1533 assert!(html.contains("/users"));
1535 }
1536
1537 #[tokio::test]
1539 async fn test_get_adapter_returns_adapter() {
1540 let mut router = Router::new();
1541 router.add_adapter(Box::new(RestAdapter::new()));
1542
1543 let adapter = router.get_adapter("rest");
1544 assert!(adapter.is_some());
1545 assert_eq!(adapter.unwrap().name(), "rest");
1546 }
1547
1548 #[tokio::test]
1549 async fn test_get_adapter_returns_none_for_missing() {
1550 let router = Router::new();
1551 let adapter = router.get_adapter("rest");
1552 assert!(adapter.is_none());
1553 }
1554
1555 #[tokio::test]
1556 async fn test_route_request_success() {
1557 let mut router = Router::new();
1558 router.register("test_handler", || async { "Success!".to_string() });
1559
1560 let mut rest_adapter = RestAdapter::new();
1562 rest_adapter.route("GET", "/test", "test_handler");
1563 router.add_adapter(Box::new(rest_adapter));
1564
1565 let result = router.route_request("rest", "GET /test").await;
1566 assert!(result.is_ok());
1567 let response = result.unwrap();
1568 assert!(response.contains("HTTP 200") || response.contains("test_handler"));
1569 }
1570
1571 #[tokio::test]
1572 async fn test_route_request_unknown_adapter() {
1573 let router = Router::new();
1574 let result = router.route_request("unknown", "test").await;
1575 assert!(result.is_err());
1576 assert!(result.unwrap_err().contains("Adapter not found"));
1577 }
1578
1579 #[tokio::test]
1580 async fn test_enabled_protocols_empty() {
1581 let router = Router::new();
1582 let protocols = router.enabled_protocols();
1583 assert_eq!(protocols.len(), 0);
1584 }
1585
1586 #[tokio::test]
1587 async fn test_enabled_protocols_multiple() {
1588 let mut router = Router::new();
1589 router.add_adapter(Box::new(RestAdapter::new()));
1590 router.add_adapter(Box::new(GraphQLAdapter::new()));
1591 router.add_adapter(Box::new(GrpcAdapter::new()));
1592
1593 let protocols = router.enabled_protocols();
1594 assert_eq!(protocols.len(), 3);
1595 assert!(protocols.contains(&"rest".to_string()));
1596 assert!(protocols.contains(&"graphql".to_string()));
1597 assert!(protocols.contains(&"grpc".to_string()));
1598 }
1599
1600 #[tokio::test]
1601 async fn test_can_handle_rest() {
1602 let mut router = Router::new();
1603 assert!(!router.can_handle_rest("test"));
1604
1605 router.add_adapter(Box::new(RestAdapter::new()));
1606 assert!(router.can_handle_rest("test"));
1607 }
1608
1609 #[tokio::test]
1610 async fn test_can_handle_graphql() {
1611 let mut router = Router::new();
1612 assert!(!router.can_handle_graphql("test"));
1613
1614 router.add_adapter(Box::new(GraphQLAdapter::new()));
1615 assert!(router.can_handle_graphql("test"));
1616 }
1617
1618 #[tokio::test]
1619 async fn test_can_handle_grpc() {
1620 let mut router = Router::new();
1621 assert!(!router.can_handle_grpc("test"));
1622
1623 router.add_adapter(Box::new(GrpcAdapter::new()));
1624 assert!(router.can_handle_grpc("test"));
1625 }
1626
1627 #[tokio::test]
1630 async fn test_integration_single_handler_rest() {
1631 let mut router = Router::new();
1633 router.register("get_user", || async { "User data".to_string() });
1634
1635 let mut rest = RestAdapter::new();
1637 rest.route("GET", "/users/:id", "get_user");
1638 router.add_adapter(Box::new(rest));
1639
1640 let response = router.route_request("rest", "GET /users/42").await;
1642 assert!(response.is_ok());
1643 assert!(response.unwrap().contains("get_user"));
1644 }
1645
1646 #[tokio::test]
1647 async fn test_integration_single_handler_graphql() {
1648 let mut router = Router::new();
1650 router.register("get_user", || async { "User data".to_string() });
1651
1652 let mut graphql = GraphQLAdapter::new();
1654 graphql.query("user", "get_user");
1655 router.add_adapter(Box::new(graphql));
1656
1657 let response = router.route_request("graphql", "query { user }").await;
1659 assert!(response.is_ok());
1660 assert!(response.unwrap().contains("get_user"));
1661 }
1662
1663 #[tokio::test]
1664 async fn test_integration_single_handler_grpc() {
1665 let mut router = Router::new();
1667 router.register("get_user", || async { "User data".to_string() });
1668
1669 let mut grpc = GrpcAdapter::new();
1671 grpc.unary("UserService", "GetUser", "get_user");
1672 router.add_adapter(Box::new(grpc));
1673
1674 let response = router
1676 .route_request("grpc", "UserService.GetUser:{\"id\":42}")
1677 .await;
1678 assert!(response.is_ok());
1679 assert!(response.unwrap().contains("get_user"));
1680 }
1681
1682 #[tokio::test]
1683 async fn test_integration_single_handler_all_protocols() {
1684 let mut router = Router::new();
1686 router.register("get_user", || async { "User data".to_string() });
1687
1688 let mut rest = RestAdapter::new();
1690 rest.route("GET", "/users/:id", "get_user");
1691 router.add_adapter(Box::new(rest));
1692
1693 let mut graphql = GraphQLAdapter::new();
1695 graphql.query("user", "get_user");
1696 router.add_adapter(Box::new(graphql));
1697
1698 let mut grpc = GrpcAdapter::new();
1700 grpc.unary("UserService", "GetUser", "get_user");
1701 router.add_adapter(Box::new(grpc));
1702
1703 let rest_response = router.route_request("rest", "GET /users/42").await;
1705 assert!(rest_response.is_ok());
1706 assert!(rest_response.unwrap().contains("get_user"));
1707
1708 let graphql_response = router.route_request("graphql", "query { user }").await;
1710 assert!(graphql_response.is_ok());
1711 assert!(graphql_response.unwrap().contains("get_user"));
1712
1713 let grpc_response = router
1715 .route_request("grpc", "UserService.GetUser:{\"id\":42}")
1716 .await;
1717 assert!(grpc_response.is_ok());
1718 assert!(grpc_response.unwrap().contains("get_user"));
1719 }
1720
1721 #[tokio::test]
1722 async fn test_integration_multiple_handlers_all_protocols() {
1723 let mut router = Router::new();
1725 router.register("get_user", || async { "User data".to_string() });
1726 router.register("list_users", || async { "Users list".to_string() });
1727 router.register("create_user", || async { "Created user".to_string() });
1728
1729 let mut rest = RestAdapter::new();
1731 rest.route("GET", "/users/:id", "get_user");
1732 rest.route("GET", "/users", "list_users");
1733 rest.route("POST", "/users", "create_user");
1734 router.add_adapter(Box::new(rest));
1735
1736 let mut graphql = GraphQLAdapter::new();
1738 graphql.query("user", "get_user");
1739 graphql.query("users", "list_users");
1740 graphql.mutation("createUser", "create_user");
1741 router.add_adapter(Box::new(graphql));
1742
1743 let mut grpc = GrpcAdapter::new();
1745 grpc.unary("UserService", "GetUser", "get_user");
1746 grpc.unary("UserService", "ListUsers", "list_users");
1747 grpc.unary("UserService", "CreateUser", "create_user");
1748 router.add_adapter(Box::new(grpc));
1749
1750 assert!(router
1752 .route_request("rest", "GET /users/42")
1753 .await
1754 .unwrap()
1755 .contains("get_user"));
1756 assert!(router
1757 .route_request("graphql", "query { user }")
1758 .await
1759 .unwrap()
1760 .contains("get_user"));
1761 assert!(router
1762 .route_request("grpc", "UserService.GetUser:{}")
1763 .await
1764 .unwrap()
1765 .contains("get_user"));
1766 }
1767
1768 #[tokio::test]
1769 async fn test_integration_error_handling_rest_404() {
1770 let mut router = Router::new();
1772
1773 let mut rest = RestAdapter::new();
1774 rest.route("GET", "/users/:id", "get_user");
1775 router.add_adapter(Box::new(rest));
1776
1777 let response = router.route_request("rest", "GET /posts/42").await;
1778 assert!(response.is_ok());
1779 assert!(response.unwrap().contains("HTTP 404"));
1780 }
1781
1782 #[tokio::test]
1783 async fn test_integration_error_handling_graphql_not_found() {
1784 let mut router = Router::new();
1786
1787 let mut graphql = GraphQLAdapter::new();
1788 graphql.query("user", "get_user");
1789 router.add_adapter(Box::new(graphql));
1790
1791 let response = router.route_request("graphql", "query { post }").await;
1792 assert!(response.is_ok());
1793 assert!(response.unwrap().contains("errors"));
1794 }
1795
1796 #[tokio::test]
1797 async fn test_integration_error_handling_grpc_unimplemented() {
1798 let mut router = Router::new();
1800
1801 let mut grpc = GrpcAdapter::new();
1802 grpc.unary("UserService", "GetUser", "get_user");
1803 router.add_adapter(Box::new(grpc));
1804
1805 let response = router.route_request("grpc", "UserService.GetPost:{}").await;
1806 assert!(response.is_ok());
1807 assert!(response.unwrap().contains("grpc-status: 12")); }
1809
1810 #[tokio::test]
1811 async fn test_integration_unknown_protocol() {
1812 let router = Router::new();
1814
1815 let response = router.route_request("unknown", "request").await;
1816 assert!(response.is_err());
1817 assert!(response.unwrap_err().contains("Adapter not found"));
1818 }
1819
1820 #[tokio::test]
1821 async fn test_integration_protocol_specific_features_rest_methods() {
1822 let mut router = Router::new();
1824 router.register("get_users", || async { "Users".to_string() });
1825 router.register("create_user", || async { "Created".to_string() });
1826 router.register("update_user", || async { "Updated".to_string() });
1827 router.register("delete_user", || async { "Deleted".to_string() });
1828
1829 let mut rest = RestAdapter::new();
1830 rest.route("GET", "/users", "get_users");
1831 rest.route("POST", "/users", "create_user");
1832 rest.route("PUT", "/users/:id", "update_user");
1833 rest.route("DELETE", "/users/:id", "delete_user");
1834 router.add_adapter(Box::new(rest));
1835
1836 assert!(router
1838 .route_request("rest", "GET /users")
1839 .await
1840 .unwrap()
1841 .contains("get_users"));
1842 assert!(router
1843 .route_request("rest", "POST /users")
1844 .await
1845 .unwrap()
1846 .contains("create_user"));
1847 assert!(router
1848 .route_request("rest", "PUT /users/42")
1849 .await
1850 .unwrap()
1851 .contains("update_user"));
1852 assert!(router
1853 .route_request("rest", "DELETE /users/42")
1854 .await
1855 .unwrap()
1856 .contains("delete_user"));
1857 }
1858
1859 #[tokio::test]
1860 async fn test_integration_protocol_specific_features_graphql_types() {
1861 let mut router = Router::new();
1863 router.register("get_user", || async { "User".to_string() });
1864 router.register("create_user", || async { "Created".to_string() });
1865
1866 let mut graphql = GraphQLAdapter::new();
1867 graphql.query("user", "get_user");
1868 graphql.mutation("createUser", "create_user");
1869 router.add_adapter(Box::new(graphql));
1870
1871 assert!(router
1873 .route_request("graphql", "query { user }")
1874 .await
1875 .unwrap()
1876 .contains("get_user"));
1877
1878 assert!(router
1880 .route_request("graphql", "mutation { createUser }")
1881 .await
1882 .unwrap()
1883 .contains("create_user"));
1884 }
1885
1886 #[tokio::test]
1887 async fn test_integration_protocol_specific_features_grpc_streaming() {
1888 let mut router = Router::new();
1890 router.register("get_user", || async { "User".to_string() });
1891 router.register("list_users", || async { "Users".to_string() });
1892
1893 let mut grpc = GrpcAdapter::new();
1894 grpc.unary("UserService", "GetUser", "get_user");
1895 grpc.server_streaming("UserService", "ListUsers", "list_users");
1896 router.add_adapter(Box::new(grpc));
1897
1898 let unary_response = router
1900 .route_request("grpc", "UserService.GetUser:{}")
1901 .await
1902 .unwrap();
1903 assert!(unary_response.contains("unary"));
1904
1905 let streaming_response = router
1907 .route_request("grpc", "UserService.ListUsers:{}")
1908 .await
1909 .unwrap();
1910 assert!(streaming_response.contains("server_streaming"));
1911 }
1912
1913 #[tokio::test]
1916 async fn test_register_streaming_handler() {
1917 let mut router = Router::new();
1918 router.register_streaming("stream_data", |tx: StreamSender| async move {
1919 tx.send("item".to_string()).await.ok();
1920 "done".to_string()
1921 });
1922 assert!(router.is_streaming("stream_data"));
1923 assert!(!router.is_streaming("nonexistent"));
1924 }
1925
1926 #[tokio::test]
1927 async fn test_register_streaming_with_args() {
1928 #[derive(serde::Deserialize)]
1929 struct Input {
1930 count: usize,
1931 }
1932
1933 let mut router = Router::new();
1934 router.register_streaming_with_args("stream_items", |args: Input, tx: StreamSender| async move {
1935 for i in 0..args.count {
1936 tx.send(format!("item-{i}")).await.ok();
1937 }
1938 "done".to_string()
1939 });
1940 assert!(router.is_streaming("stream_items"));
1941 }
1942
1943 #[tokio::test]
1944 async fn test_streaming_handler_not_in_regular_handlers() {
1945 let mut router = Router::new();
1946 router.register_streaming("stream", |_tx: StreamSender| async move {
1947 "done".to_string()
1948 });
1949 assert_eq!(router.handlers_count(), 0);
1951 }
1952
1953 #[tokio::test]
1954 async fn test_list_handlers_includes_streaming() {
1955 let mut router = Router::new();
1956 router.register("regular", || async { "ok".to_string() });
1957 router.register_streaming("stream", |_tx: StreamSender| async move {
1958 "ok".to_string()
1959 });
1960
1961 let handlers = router.list_handlers();
1962 assert_eq!(handlers.len(), 2);
1963 assert!(handlers.contains(&"regular".to_string()));
1964 assert!(handlers.contains(&"stream".to_string()));
1965 }
1966
1967 #[tokio::test]
1968 async fn test_call_streaming_handler() {
1969 let mut router = Router::new();
1970 router.register_streaming("stream", |tx: StreamSender| async move {
1971 tx.send("a".to_string()).await.ok();
1972 tx.send("b".to_string()).await.ok();
1973 "final".to_string()
1974 });
1975
1976 let (mut rx, fut) = router.call_streaming_handler("stream", "{}").unwrap();
1977 let result = fut.await;
1978
1979 assert_eq!(result, Ok("final".to_string()));
1980 assert_eq!(rx.recv().await, Some("a".to_string()));
1981 assert_eq!(rx.recv().await, Some("b".to_string()));
1982 }
1983
1984 #[tokio::test]
1985 async fn test_call_streaming_handler_with_args() {
1986 #[derive(serde::Deserialize)]
1987 struct Input {
1988 n: usize,
1989 }
1990
1991 let mut router = Router::new();
1992 router.register_streaming_with_args("count", |args: Input, tx: StreamSender| async move {
1993 for i in 0..args.n {
1994 tx.send(format!("{i}")).await.ok();
1995 }
1996 format!("counted to {}", args.n)
1997 });
1998
1999 let (mut rx, fut) = router.call_streaming_handler("count", r#"{"n":3}"#).unwrap();
2000 let result = fut.await;
2001
2002 assert_eq!(result, Ok("counted to 3".to_string()));
2003 assert_eq!(rx.recv().await, Some("0".to_string()));
2004 assert_eq!(rx.recv().await, Some("1".to_string()));
2005 assert_eq!(rx.recv().await, Some("2".to_string()));
2006 }
2007
2008 #[tokio::test]
2009 async fn test_call_streaming_handler_not_found() {
2010 let router = Router::new();
2011 let result = router.call_streaming_handler("missing", "{}");
2012 assert!(result.is_err());
2013 match result {
2014 Err(e) => assert!(e.contains("not found")),
2015 Ok(_) => panic!("expected error"),
2016 }
2017 }
2018
2019 #[tokio::test]
2020 async fn test_is_streaming_false_for_regular() {
2021 let mut router = Router::new();
2022 router.register("regular", || async { "ok".to_string() });
2023 assert!(!router.is_streaming("regular"));
2024 }
2025
2026 #[tokio::test]
2027 async fn test_mixed_router() {
2028 let mut router = Router::new();
2029 router.register("get_user", || async { "user".to_string() });
2030 router.register_streaming("stream_updates", |tx: StreamSender| async move {
2031 tx.send("update".to_string()).await.ok();
2032 "done".to_string()
2033 });
2034
2035 let result = router.execute("get_user").await;
2037 assert_eq!(result, Ok("user".to_string()));
2038
2039 let (mut rx, fut) = router.call_streaming_handler("stream_updates", "{}").unwrap();
2041 let result = fut.await;
2042 assert_eq!(result, Ok("done".to_string()));
2043 assert_eq!(rx.recv().await, Some("update".to_string()));
2044
2045 assert!(!router.is_streaming("get_user"));
2047 assert!(router.call_streaming_handler("get_user", "{}").is_err());
2048 }
2049
2050 #[tokio::test]
2051 async fn test_register_streaming_with_state() {
2052 struct AppState {
2053 prefix: String,
2054 }
2055
2056 #[derive(serde::Deserialize)]
2057 struct Input {
2058 name: String,
2059 }
2060
2061 let mut router = Router::new().with_state(AppState {
2062 prefix: "Hello".to_string(),
2063 });
2064 router.register_streaming_with_state::<AppState, Input, _, _, _>(
2065 "greet_stream",
2066 |state: State<Arc<AppState>>, args: Input, tx: StreamSender| async move {
2067 tx.send(format!("{} {}", state.prefix, args.name))
2068 .await
2069 .ok();
2070 "done".to_string()
2071 },
2072 );
2073
2074 let (mut rx, fut) = router
2075 .call_streaming_handler("greet_stream", r#"{"name":"Alice"}"#)
2076 .unwrap();
2077 let result = fut.await;
2078
2079 assert_eq!(result, Ok("done".to_string()));
2080 assert_eq!(rx.recv().await, Some("Hello Alice".to_string()));
2081 }
2082
2083 #[tokio::test]
2084 async fn test_register_streaming_with_state_only() {
2085 struct AppState {
2086 items: Vec<String>,
2087 }
2088
2089 let mut router = Router::new().with_state(AppState {
2090 items: vec!["x".to_string(), "y".to_string()],
2091 });
2092 router.register_streaming_with_state_only::<AppState, _, _, _>(
2093 "list_stream",
2094 |state: State<Arc<AppState>>, tx: StreamSender| async move {
2095 for item in &state.items {
2096 tx.send(item.clone()).await.ok();
2097 }
2098 format!("listed {}", state.items.len())
2099 },
2100 );
2101
2102 let (mut rx, fut) = router
2103 .call_streaming_handler("list_stream", "{}")
2104 .unwrap();
2105 let result = fut.await;
2106
2107 assert_eq!(result, Ok("listed 2".to_string()));
2108 assert_eq!(rx.recv().await, Some("x".to_string()));
2109 assert_eq!(rx.recv().await, Some("y".to_string()));
2110 }
2111
2112 #[tokio::test]
2115 async fn test_register_stream_no_args() {
2116 let mut router = Router::new();
2117 router.register_stream("items", || async {
2118 tokio_stream::iter(vec!["a".to_string(), "b".to_string(), "c".to_string()])
2119 });
2120
2121 assert!(router.is_streaming("items"));
2122
2123 let (mut rx, fut) = router.call_streaming_handler("items", "{}").unwrap();
2124 let _result = fut.await;
2125
2126 assert_eq!(rx.recv().await, Some("a".to_string()));
2127 assert_eq!(rx.recv().await, Some("b".to_string()));
2128 assert_eq!(rx.recv().await, Some("c".to_string()));
2129 }
2130
2131 #[tokio::test]
2132 async fn test_register_stream_with_args() {
2133 #[derive(serde::Deserialize)]
2134 struct Input {
2135 count: usize,
2136 }
2137
2138 let mut router = Router::new();
2139 router.register_stream_with_args("counting", |args: Input| async move {
2140 tokio_stream::iter((0..args.count).map(|i| format!("{i}")))
2141 });
2142
2143 assert!(router.is_streaming("counting"));
2144
2145 let (mut rx, fut) = router
2146 .call_streaming_handler("counting", r#"{"count":3}"#)
2147 .unwrap();
2148 let _result = fut.await;
2149
2150 assert_eq!(rx.recv().await, Some("0".to_string()));
2151 assert_eq!(rx.recv().await, Some("1".to_string()));
2152 assert_eq!(rx.recv().await, Some("2".to_string()));
2153 }
2154
2155 #[tokio::test]
2156 async fn test_register_stream_with_state() {
2157 struct AppState {
2158 items: Vec<String>,
2159 }
2160
2161 let mut router = Router::new().with_state(AppState {
2162 items: vec!["x".to_string(), "y".to_string()],
2163 });
2164 router.register_stream_with_state::<AppState, serde_json::Value, _, _, _, _>(
2165 "state_stream",
2166 |state: State<Arc<AppState>>, _args: serde_json::Value| {
2167 let items = state.items.clone();
2168 async move { tokio_stream::iter(items) }
2169 },
2170 );
2171
2172 assert!(router.is_streaming("state_stream"));
2173 }
2174
2175 #[tokio::test]
2176 async fn test_stream_adapter_shows_in_is_streaming() {
2177 let mut router = Router::new();
2178 router.register_stream("my_stream", || async {
2179 tokio_stream::iter(vec!["done".to_string()])
2180 });
2181
2182 assert!(router.is_streaming("my_stream"));
2183 assert!(!router.is_streaming("nonexistent"));
2184 }
2185
2186 #[tokio::test]
2187 async fn test_multiple_state_types() {
2188 struct DbPool {
2189 url: String,
2190 }
2191 struct AppConfig {
2192 name: String,
2193 }
2194
2195 #[derive(serde::Deserialize)]
2196 struct Input {
2197 key: String,
2198 }
2199
2200 let mut router = Router::new()
2201 .with_state(DbPool {
2202 url: "postgres://localhost".to_string(),
2203 })
2204 .with_state(AppConfig {
2205 name: "MyApp".to_string(),
2206 });
2207
2208 router.register_with_state::<DbPool, Input, _, _>(
2210 "db_query",
2211 |state: State<Arc<DbPool>>, args: Input| async move {
2212 format!("{}:{}", state.url, args.key)
2213 },
2214 );
2215
2216 router.register_with_state_only::<AppConfig, _, _>(
2218 "app_name",
2219 |state: State<Arc<AppConfig>>| async move { state.name.clone() },
2220 );
2221
2222 let result = router.call_handler("db_query", r#"{"key":"users"}"#).await;
2223 assert_eq!(result, Ok("postgres://localhost:users".to_string()));
2224
2225 let result = router.call_handler("app_name", "{}").await;
2226 assert_eq!(result, Ok("MyApp".to_string()));
2227 }
2228
2229 #[tokio::test]
2230 async fn test_inject_state_after_construction() {
2231 struct LateState {
2232 value: String,
2233 }
2234
2235 let mut router = Router::new();
2236 router.inject_state(LateState {
2237 value: "injected".to_string(),
2238 });
2239 router.register_with_state_only::<LateState, _, _>(
2240 "get_value",
2241 |state: State<Arc<LateState>>| async move { state.value.clone() },
2242 );
2243
2244 let result = router.call_handler("get_value", "{}").await;
2245 assert_eq!(result, Ok("injected".to_string()));
2246 }
2247
2248 #[tokio::test]
2249 async fn test_multiple_state_streaming() {
2250 struct StreamConfig {
2251 prefix: String,
2252 }
2253
2254 let mut router = Router::new().with_state(StreamConfig {
2255 prefix: "stream".to_string(),
2256 });
2257
2258 router.register_streaming_with_state_only::<StreamConfig, _, _, _>(
2259 "prefixed_stream",
2260 |state: State<Arc<StreamConfig>>, tx: StreamSender| async move {
2261 tx.send(format!("{}:item", state.prefix)).await.ok();
2262 "done".to_string()
2263 },
2264 );
2265
2266 let (mut rx, fut) = router
2267 .call_streaming_handler("prefixed_stream", "{}")
2268 .unwrap();
2269 let result = fut.await;
2270 assert_eq!(result, Ok("done".to_string()));
2271 assert_eq!(rx.recv().await, Some("stream:item".to_string()));
2272 }
2273
2274 #[tokio::test]
2275 async fn test_with_state_duplicate_type_last_wins() {
2276 let mut router = Router::new()
2278 .with_state("first".to_string())
2279 .with_state("second".to_string());
2280
2281 router.register_with_state_only::<String, _, _>(
2282 "get",
2283 |state: State<Arc<String>>| async move { (**state).clone() },
2284 );
2285
2286 let result = router.call_handler("get", "{}").await;
2287 assert_eq!(result, Ok("second".to_string()));
2288 }
2289
2290 mod macro_test_handlers {
2294 use super::{State, StreamSender};
2295 use std::sync::Arc;
2296
2297 pub async fn health() -> String {
2298 "ok".to_string()
2299 }
2300
2301 pub async fn echo(args: EchoArgs) -> String {
2302 args.message
2303 }
2304
2305 #[derive(serde::Deserialize)]
2306 pub struct EchoArgs {
2307 pub message: String,
2308 }
2309
2310 pub async fn ticker(tx: StreamSender) -> String {
2311 tx.send("tick".to_string()).await.ok();
2312 "done".to_string()
2313 }
2314
2315 pub async fn search(args: SearchArgs, tx: StreamSender) -> String {
2316 tx.send(format!("found:{}", args.query)).await.ok();
2317 "complete".to_string()
2318 }
2319
2320 #[derive(serde::Deserialize)]
2321 pub struct SearchArgs {
2322 pub query: String,
2323 }
2324
2325 pub async fn get_status(state: State<Arc<String>>) -> String {
2327 format!("status:{}", *state)
2328 }
2329
2330 pub async fn save_key(state: State<Arc<String>>, args: SaveArgs) -> String {
2331 format!("{}:{}", *state, args.key)
2332 }
2333
2334 #[derive(serde::Deserialize)]
2335 pub struct SaveArgs {
2336 pub key: String,
2337 }
2338
2339 pub async fn state_stream(state: State<Arc<String>>, tx: StreamSender) -> String {
2340 tx.send(format!("{}:chunk", *state)).await.ok();
2341 "done".to_string()
2342 }
2343
2344 pub async fn state_search(
2345 state: State<Arc<String>>,
2346 args: SearchArgs,
2347 tx: StreamSender,
2348 ) -> String {
2349 tx.send(format!("{}:{}", *state, args.query)).await.ok();
2350 "complete".to_string()
2351 }
2352 }
2353
2354 #[tokio::test]
2355 async fn test_register_handlers_basic() {
2356 let mut router = Router::new();
2357 register_handlers!(router, [
2358 "health" => macro_test_handlers::health,
2359 ]);
2360 assert_eq!(router.handlers_count(), 1);
2361 let result = router.call_handler("health", "{}").await;
2362 assert_eq!(result, Ok("ok".to_string()));
2363 }
2364
2365 #[tokio::test]
2366 async fn test_register_handlers_with_args() {
2367 let mut router = Router::new();
2368 register_handlers!(router, [
2369 args "echo" => macro_test_handlers::echo,
2370 ]);
2371 assert_eq!(router.handlers_count(), 1);
2372 let result = router
2373 .call_handler("echo", r#"{"message":"hello"}"#)
2374 .await;
2375 assert_eq!(result, Ok("hello".to_string()));
2376 }
2377
2378 #[tokio::test]
2379 async fn test_register_handlers_streaming() {
2380 let mut router = Router::new();
2381 register_handlers!(router, [
2382 streaming "ticker" => macro_test_handlers::ticker,
2383 ]);
2384 assert!(router.is_streaming("ticker"));
2385 let (mut rx, fut) = router.call_streaming_handler("ticker", "{}").unwrap();
2386 let result = fut.await;
2387 assert_eq!(result, Ok("done".to_string()));
2388 assert_eq!(rx.recv().await, Some("tick".to_string()));
2389 }
2390
2391 #[tokio::test]
2392 async fn test_register_handlers_streaming_with_args() {
2393 let mut router = Router::new();
2394 register_handlers!(router, [
2395 streaming args "search" => macro_test_handlers::search,
2396 ]);
2397 assert!(router.is_streaming("search"));
2398 let (mut rx, fut) = router
2399 .call_streaming_handler("search", r#"{"query":"rust"}"#)
2400 .unwrap();
2401 let result = fut.await;
2402 assert_eq!(result, Ok("complete".to_string()));
2403 assert_eq!(rx.recv().await, Some("found:rust".to_string()));
2404 }
2405
2406 #[tokio::test]
2407 async fn test_register_handlers_mixed() {
2408 let mut router = Router::new();
2409 register_handlers!(router, [
2410 "health" => macro_test_handlers::health,
2411 args "echo" => macro_test_handlers::echo,
2412 streaming "ticker" => macro_test_handlers::ticker,
2413 streaming args "search" => macro_test_handlers::search,
2414 ]);
2415
2416 assert_eq!(router.handlers_count(), 2);
2418 assert_eq!(router.list_handlers().len(), 4);
2419
2420 assert_eq!(
2422 router.call_handler("health", "{}").await,
2423 Ok("ok".to_string())
2424 );
2425 assert_eq!(
2426 router
2427 .call_handler("echo", r#"{"message":"hi"}"#)
2428 .await,
2429 Ok("hi".to_string())
2430 );
2431
2432 assert!(router.is_streaming("ticker"));
2434 assert!(router.is_streaming("search"));
2435 }
2436
2437 #[tokio::test]
2438 async fn test_register_handlers_empty() {
2439 let router = Router::new();
2440 register_handlers!(router, []);
2441 assert_eq!(router.handlers_count(), 0);
2442 }
2443
2444 #[tokio::test]
2445 async fn test_register_handlers_state_only() {
2446 let mut router = Router::new().with_state("active".to_string());
2447 register_handlers!(router, [
2448 state "get_status" => macro_test_handlers::get_status,
2449 ]);
2450 let result = router.call_handler("get_status", "{}").await;
2451 assert_eq!(result, Ok("status:active".to_string()));
2452 }
2453
2454 #[tokio::test]
2455 async fn test_register_handlers_state_args() {
2456 let mut router = Router::new().with_state("ns".to_string());
2457 register_handlers!(router, [
2458 state args "save_key" => macro_test_handlers::save_key,
2459 ]);
2460 let result = router
2461 .call_handler("save_key", r#"{"key":"api_token"}"#)
2462 .await;
2463 assert_eq!(result, Ok("ns:api_token".to_string()));
2464 }
2465
2466 #[tokio::test]
2467 async fn test_register_handlers_state_streaming() {
2468 let mut router = Router::new().with_state("ctx".to_string());
2469 register_handlers!(router, [
2470 state streaming "state_stream" => macro_test_handlers::state_stream,
2471 ]);
2472 assert!(router.is_streaming("state_stream"));
2473 let (mut rx, fut) = router
2474 .call_streaming_handler("state_stream", "{}")
2475 .unwrap();
2476 let result = fut.await;
2477 assert_eq!(result, Ok("done".to_string()));
2478 assert_eq!(rx.recv().await, Some("ctx:chunk".to_string()));
2479 }
2480
2481 #[tokio::test]
2482 async fn test_register_handlers_state_streaming_args() {
2483 let mut router = Router::new().with_state("db".to_string());
2484 register_handlers!(router, [
2485 state streaming args "state_search" => macro_test_handlers::state_search,
2486 ]);
2487 assert!(router.is_streaming("state_search"));
2488 let (mut rx, fut) = router
2489 .call_streaming_handler("state_search", r#"{"query":"rust"}"#)
2490 .unwrap();
2491 let result = fut.await;
2492 assert_eq!(result, Ok("complete".to_string()));
2493 assert_eq!(rx.recv().await, Some("db:rust".to_string()));
2494 }
2495
2496 #[tokio::test]
2497 async fn test_register_handlers_mixed_with_state() {
2498 let mut router = Router::new().with_state("app".to_string());
2499 register_handlers!(router, [
2500 "health" => macro_test_handlers::health,
2501 args "echo" => macro_test_handlers::echo,
2502 state "get_status" => macro_test_handlers::get_status,
2503 state args "save_key" => macro_test_handlers::save_key,
2504 streaming "ticker" => macro_test_handlers::ticker,
2505 state streaming "state_stream" => macro_test_handlers::state_stream,
2506 state streaming args "state_search" => macro_test_handlers::state_search,
2507 ]);
2508
2509 assert_eq!(
2511 router.call_handler("health", "{}").await,
2512 Ok("ok".to_string())
2513 );
2514 assert_eq!(
2515 router.call_handler("get_status", "{}").await,
2516 Ok("status:app".to_string())
2517 );
2518 assert_eq!(
2519 router
2520 .call_handler("save_key", r#"{"key":"secret"}"#)
2521 .await,
2522 Ok("app:secret".to_string())
2523 );
2524
2525 assert!(router.is_streaming("ticker"));
2527 assert!(router.is_streaming("state_stream"));
2528 assert!(router.is_streaming("state_search"));
2529 }
2530
2531 #[test]
2534 fn test_camel_to_snake_basic() {
2535 assert_eq!(camel_to_snake("workflowId"), "workflow_id");
2536 assert_eq!(camel_to_snake("actionLabel"), "action_label");
2537 assert_eq!(camel_to_snake("simple"), "simple");
2538 assert_eq!(camel_to_snake("alreadySnake"), "already_snake");
2539 assert_eq!(camel_to_snake("ABC"), "a_b_c");
2540 }
2541
2542 #[test]
2543 fn test_camel_to_snake_single_char() {
2544 assert_eq!(camel_to_snake("a"), "a");
2545 assert_eq!(camel_to_snake("A"), "a");
2546 }
2547
2548 #[test]
2549 fn test_camel_to_snake_empty() {
2550 assert_eq!(camel_to_snake(""), "");
2551 }
2552
2553 #[test]
2554 fn test_camel_to_snake_already_snake() {
2555 assert_eq!(camel_to_snake("already_snake_case"), "already_snake_case");
2556 }
2557
2558 #[test]
2559 fn test_transform_keys_flat_object() {
2560 let input: Value = serde_json::json!({
2561 "workflowId": "abc",
2562 "actionLabel": "run"
2563 });
2564 let result = transform_keys(input, KeyTransform::CamelToSnake);
2565 assert_eq!(result, serde_json::json!({
2566 "workflow_id": "abc",
2567 "action_label": "run"
2568 }));
2569 }
2570
2571 #[test]
2572 fn test_transform_keys_nested_object() {
2573 let input: Value = serde_json::json!({
2574 "outerKey": {
2575 "innerKey": "value"
2576 }
2577 });
2578 let result = transform_keys(input, KeyTransform::CamelToSnake);
2579 assert_eq!(result, serde_json::json!({
2580 "outer_key": {
2581 "inner_key": "value"
2582 }
2583 }));
2584 }
2585
2586 #[test]
2587 fn test_transform_keys_array_of_objects() {
2588 let input: Value = serde_json::json!([
2589 {"firstName": "Alice"},
2590 {"firstName": "Bob"}
2591 ]);
2592 let result = transform_keys(input, KeyTransform::CamelToSnake);
2593 assert_eq!(result, serde_json::json!([
2594 {"first_name": "Alice"},
2595 {"first_name": "Bob"}
2596 ]));
2597 }
2598
2599 #[test]
2600 fn test_transform_keys_primitive_passthrough() {
2601 assert_eq!(transform_keys(Value::Null, KeyTransform::CamelToSnake), Value::Null);
2602 assert_eq!(transform_keys(serde_json::json!(42), KeyTransform::CamelToSnake), serde_json::json!(42));
2603 assert_eq!(transform_keys(serde_json::json!("hello"), KeyTransform::CamelToSnake), serde_json::json!("hello"));
2604 }
2605
2606 #[tokio::test]
2607 async fn test_router_with_key_transform_camel_to_snake() {
2608 #[derive(serde::Deserialize)]
2609 struct Input {
2610 workflow_name: String,
2611 is_active: bool,
2612 }
2613
2614 let mut router = Router::new()
2615 .with_key_transform(KeyTransform::CamelToSnake);
2616 router.register_with_args("test", |args: Input| async move {
2617 format!("{}:{}", args.workflow_name, args.is_active)
2618 });
2619
2620 let result = router
2622 .call_handler("test", r#"{"workflowName":"deploy","isActive":true}"#)
2623 .await;
2624 assert_eq!(result, Ok("deploy:true".to_string()));
2625 }
2626
2627 #[tokio::test]
2628 async fn test_router_with_key_transform_already_snake() {
2629 #[derive(serde::Deserialize)]
2630 struct Input {
2631 workflow_name: String,
2632 }
2633
2634 let mut router = Router::new()
2635 .with_key_transform(KeyTransform::CamelToSnake);
2636 router.register_with_args("test", |args: Input| async move {
2637 args.workflow_name
2638 });
2639
2640 let result = router
2642 .call_handler("test", r#"{"workflow_name":"deploy"}"#)
2643 .await;
2644 assert_eq!(result, Ok("deploy".to_string()));
2645 }
2646
2647 #[tokio::test]
2648 async fn test_router_without_key_transform() {
2649 #[derive(serde::Deserialize)]
2650 struct Input {
2651 workflow_name: String,
2652 }
2653
2654 let mut router = Router::new(); router.register_with_args("test", |args: Input| async move {
2656 args.workflow_name
2657 });
2658
2659 let result = router
2661 .call_handler("test", r#"{"workflowName":"deploy"}"#)
2662 .await;
2663 assert!(result.is_err());
2664 assert!(result.unwrap_err().contains("Failed to deserialize"));
2665 }
2666
2667 #[tokio::test]
2668 async fn test_router_key_transform_streaming_handler() {
2669 #[derive(serde::Deserialize)]
2670 struct Input {
2671 item_count: usize,
2672 }
2673
2674 let mut router = Router::new()
2675 .with_key_transform(KeyTransform::CamelToSnake);
2676 router.register_streaming_with_args("stream", |args: Input, tx: StreamSender| async move {
2677 for i in 0..args.item_count {
2678 tx.send(format!("{i}")).await.ok();
2679 }
2680 "done".to_string()
2681 });
2682
2683 let (mut rx, fut) = router
2684 .call_streaming_handler("stream", r#"{"itemCount":2}"#)
2685 .unwrap();
2686 let result = fut.await;
2687
2688 assert_eq!(result, Ok("done".to_string()));
2689 assert_eq!(rx.recv().await, Some("0".to_string()));
2690 assert_eq!(rx.recv().await, Some("1".to_string()));
2691 }
2692
2693 #[tokio::test]
2694 async fn test_router_key_transform_with_state() {
2695 struct AppState {
2696 prefix: String,
2697 }
2698
2699 #[derive(serde::Deserialize)]
2700 struct Input {
2701 user_name: String,
2702 }
2703
2704 let mut router = Router::new()
2705 .with_key_transform(KeyTransform::CamelToSnake)
2706 .with_state(AppState { prefix: "Hello".to_string() });
2707
2708 router.register_with_state::<AppState, Input, _, _>(
2709 "greet",
2710 |state: State<Arc<AppState>>, args: Input| async move {
2711 format!("{} {}", state.prefix, args.user_name)
2712 },
2713 );
2714
2715 let result = router
2716 .call_handler("greet", r#"{"userName":"Alice"}"#)
2717 .await;
2718 assert_eq!(result, Ok("Hello Alice".to_string()));
2719 }
2720
2721 #[tokio::test]
2722 async fn test_router_key_transform_zero_arg_handler_unaffected() {
2723 let mut router = Router::new()
2724 .with_key_transform(KeyTransform::CamelToSnake);
2725 router.register("health", || async { "ok".to_string() });
2726
2727 let result = router.call_handler("health", "{}").await;
2728 assert_eq!(result, Ok("ok".to_string()));
2729 }
2730
2731 #[tokio::test]
2734 async fn test_register_erased_no_args() {
2735 let mut router = Router::new();
2736 router.register_erased("health", crate::erase_handler!(|| async { "ok".to_string() }));
2737 let result = router.call_handler("health", "{}").await;
2738 assert_eq!(result, Ok("ok".to_string()));
2739 }
2740
2741 #[tokio::test]
2742 async fn test_register_erased_with_args() {
2743 #[derive(serde::Deserialize)]
2744 struct Args { greeting: String }
2745
2746 async fn greet(args: Args) -> String {
2747 format!("hello {}", args.greeting)
2748 }
2749
2750 let mut router = Router::new();
2751 router.register_erased("greet", crate::erase_handler_with_args!(greet, Args));
2752 let result = router.call_handler("greet", r#"{"greeting":"world"}"#).await;
2753 assert_eq!(result, Ok("hello world".to_string()));
2754 }
2755
2756 #[tokio::test]
2757 async fn test_register_erased_with_state() {
2758 #[derive(serde::Deserialize)]
2759 struct Args { #[allow(dead_code)] key: String }
2760
2761 async fn with_state(state: handler::State<std::sync::Arc<String>>, _args: Args) -> String {
2762 format!("state={}", *state)
2763 }
2764
2765 let mut router = Router::new().with_state("mystate".to_string());
2766 let states = router.shared_states();
2767 router.register_erased(
2768 "stateful",
2769 crate::erase_handler_with_state!(with_state, String, Args, states),
2770 );
2771 let result = router.call_handler("stateful", r#"{"key":"v"}"#).await;
2772 assert_eq!(result, Ok("state=mystate".to_string()));
2773 }
2774
2775 #[tokio::test]
2776 async fn test_register_erased_with_state_only() {
2777 async fn check(state: handler::State<std::sync::Arc<u32>>) -> String {
2778 format!("n={}", *state)
2779 }
2780
2781 let mut router = Router::new().with_state(42u32);
2782 let states = router.shared_states();
2783 router.register_erased(
2784 "check",
2785 crate::erase_handler_with_state_only!(check, u32, states),
2786 );
2787 let result = router.call_handler("check", "{}").await;
2788 assert_eq!(result, Ok("n=42".to_string()));
2789 }
2790
2791 #[tokio::test]
2792 async fn test_register_streaming_erased() {
2793 let mut router = Router::new();
2794 router.register_streaming_erased(
2795 "stream",
2796 crate::erase_streaming_handler!(|tx: handler::StreamSender| async move {
2797 tx.send("chunk".to_string()).await.ok();
2798 "done".to_string()
2799 }),
2800 );
2801 let (mut rx, fut) = router.call_streaming_handler("stream", "{}").unwrap();
2802 let result = fut.await;
2803 assert_eq!(result, Ok("done".to_string()));
2804 assert_eq!(rx.recv().await, Some("chunk".to_string()));
2805 }
2806
2807 #[tokio::test]
2808 async fn test_register_handlers_erased_macro() {
2809 #[derive(serde::Deserialize)]
2810 struct GreetArgs { name: String }
2811
2812 async fn health() -> String { "ok".into() }
2813 async fn greet(args: GreetArgs) -> String { format!("hi {}", args.name) }
2814
2815 let mut router = Router::new();
2816 crate::register_handlers_erased!(router, {
2817 "health" => health(),
2818 "greet" => greet(args: GreetArgs),
2819 });
2820
2821 assert_eq!(router.call_handler("health", "{}").await, Ok("ok".to_string()));
2822 assert_eq!(
2823 router.call_handler("greet", r#"{"name":"Alice"}"#).await,
2824 Ok("hi Alice".to_string()),
2825 );
2826 }
2827}