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 Handler, HandlerFn, HandlerWithArgs, HandlerWithState, HandlerWithStateOnly,
114 IntoHandlerResult, IntoStreamItem, Json, SharedStateMap, State, StreamError, StreamHandler,
115 StreamReceiver, StreamSender, StreamingHandlerFn, StreamingHandlerWithArgs,
116 StreamingHandlerWithState, StreamingHandlerWithStateOnly, DEFAULT_STREAM_CAPACITY,
117};
118pub use metadata::RouteMetadata;
119pub use method::Method;
120pub use openapi::{OpenApiGenerator, OpenApiServer};
121pub use rest::{RestAdapter, RestRequest, RestResponse, RestRoute};
122pub use scalar::{scalar_html, ScalarConfig, ScalarLayout, ScalarTheme};
123pub use schema::ToJsonSchema;
124pub use ts_codegen::{generate_ts_client, HandlerMeta, TsField, TsType};
125
126async fn drive_stream<T, St>(stream: St, tx: &StreamSender) -> String
131where
132 T: IntoStreamItem,
133 St: Stream<Item = T> + Send,
134{
135 tokio::pin!(stream);
136 loop {
137 let next = std::future::poll_fn(|cx| stream.as_mut().poll_next(cx)).await;
138 match next {
139 Some(item) => match tx.send(item).await {
140 Ok(()) => {}
141 Err(StreamError::Closed) => break,
142 Err(StreamError::Serialize(e)) => {
143 return serde_json::json!({"error": e}).to_string();
144 }
145 },
146 None => break,
147 }
148 }
149 "null".to_string()
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub enum KeyTransform {
169 CamelToSnake,
171}
172
173fn camel_to_snake(s: &str) -> String {
175 let mut result = String::with_capacity(s.len() + 4);
176 for (i, ch) in s.chars().enumerate() {
177 if ch.is_ascii_uppercase() {
178 if i > 0 {
179 result.push('_');
180 }
181 result.push(ch.to_ascii_lowercase());
182 } else {
183 result.push(ch);
184 }
185 }
186 result
187}
188
189fn transform_keys(value: Value, transform: KeyTransform) -> Value {
191 match value {
192 Value::Object(map) => {
193 let new_map: Map<String, Value> = map
194 .into_iter()
195 .map(|(k, v)| {
196 let new_key = match transform {
197 KeyTransform::CamelToSnake => camel_to_snake(&k),
198 };
199 (new_key, transform_keys(v, transform))
200 })
201 .collect();
202 Value::Object(new_map)
203 }
204 Value::Array(arr) => {
205 Value::Array(arr.into_iter().map(|v| transform_keys(v, transform)).collect())
206 }
207 other => other,
208 }
209}
210
211fn apply_key_transform(args: &str, transform: KeyTransform) -> String {
216 match serde_json::from_str::<Value>(args) {
217 Ok(value) => transform_keys(value, transform).to_string(),
218 Err(_) => args.to_string(),
219 }
220}
221
222pub struct Router {
227 handlers: HashMap<String, Box<dyn Handler>>,
228 streaming_handlers: HashMap<String, Box<dyn StreamHandler>>,
229 adapters: HashMap<String, Box<dyn ProtocolAdapter>>,
230 routes: Vec<RouteMetadata>,
231 states: SharedStateMap,
232 handler_metas: HashMap<String, HandlerMeta>,
233 key_transform: Option<KeyTransform>,
234 #[cfg(feature = "router")]
235 #[allow(dead_code)]
236 config: Option<RouterConfig>,
237}
238
239impl Router {
240 pub fn new() -> Self {
242 Self {
243 handlers: HashMap::new(),
244 streaming_handlers: HashMap::new(),
245 adapters: HashMap::new(),
246 routes: Vec::new(),
247 states: Arc::new(std::sync::RwLock::new(HashMap::new())),
248 handler_metas: HashMap::new(),
249 key_transform: None,
250 #[cfg(feature = "router")]
251 config: None,
252 }
253 }
254
255 #[cfg(feature = "router")]
257 pub fn with_config(config: RouterConfig) -> Self {
258 let mut router = Self {
259 handlers: HashMap::new(),
260 streaming_handlers: HashMap::new(),
261 adapters: HashMap::new(),
262 routes: Vec::new(),
263 states: Arc::new(std::sync::RwLock::new(HashMap::new())),
264 handler_metas: HashMap::new(),
265 key_transform: None,
266 config: Some(config.clone()),
267 };
268
269 if config.has_protocol("rest") {
271 router.add_adapter(Box::new(RestAdapter::new()));
272 }
273 if config.has_protocol("graphql") {
274 router.add_adapter(Box::new(GraphQLAdapter::new()));
275 }
276 if config.has_protocol("grpc") {
277 router.add_adapter(Box::new(GrpcAdapter::new()));
278 }
279
280 router
281 }
282
283 pub fn with_key_transform(mut self, transform: KeyTransform) -> Self {
300 self.key_transform = Some(transform);
301 self
302 }
303
304 pub fn with_state<S: Send + Sync + 'static>(mut self, state: S) -> Self {
316 self.insert_state::<S>(state);
317 self
318 }
319
320 pub fn inject_state<S: Send + Sync + 'static>(&mut self, state: S) {
327 self.insert_state::<S>(state);
328 }
329
330 fn insert_state<S: Send + Sync + 'static>(&mut self, state: S) {
331 let id = std::any::TypeId::of::<S>();
332 let mut map = self.states.write().expect("state lock poisoned");
333 if map.contains_key(&id) {
334 #[cfg(debug_assertions)]
335 eprintln!(
336 "allframe: with_state called twice for type `{}` — previous value replaced",
337 std::any::type_name::<S>()
338 );
339 }
340 map.insert(id, Arc::new(state));
341 }
342
343 pub fn shared_states(&self) -> SharedStateMap {
349 self.states.clone()
350 }
351
352 pub fn register<F, Fut>(&mut self, name: &str, handler: F)
354 where
355 F: Fn() -> Fut + Send + Sync + 'static,
356 Fut: Future<Output = String> + Send + 'static,
357 {
358 self.handlers
359 .insert(name.to_string(), Box::new(HandlerFn::new(handler)));
360 }
361
362 pub fn register_with_args<T, F, Fut>(&mut self, name: &str, handler: F)
364 where
365 T: DeserializeOwned + Send + 'static,
366 F: Fn(T) -> Fut + Send + Sync + 'static,
367 Fut: Future<Output = String> + Send + 'static,
368 {
369 self.handlers
370 .insert(name.to_string(), Box::new(HandlerWithArgs::new(handler)));
371 }
372
373 pub fn register_with_state<S, T, F, Fut>(&mut self, name: &str, handler: F)
379 where
380 S: Send + Sync + 'static,
381 T: DeserializeOwned + Send + 'static,
382 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
383 Fut: Future<Output = String> + Send + 'static,
384 {
385 let state = self.states.clone();
386 self.handlers
387 .insert(name.to_string(), Box::new(HandlerWithState::new(handler, state)));
388 }
389
390 pub fn register_with_state_only<S, F, Fut>(&mut self, name: &str, handler: F)
396 where
397 S: Send + Sync + 'static,
398 F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
399 Fut: Future<Output = String> + Send + 'static,
400 {
401 let state = self.states.clone();
402 self.handlers
403 .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(handler, state)));
404 }
405
406 pub fn register_typed<R, F, Fut>(&mut self, name: &str, handler: F)
410 where
411 R: Serialize + Send + 'static,
412 F: Fn() -> Fut + Send + Sync + 'static,
413 Fut: Future<Output = R> + Send + 'static,
414 {
415 let wrapped = move || {
416 let fut = handler();
417 async move { Json(fut.await) }
418 };
419 self.handlers
420 .insert(name.to_string(), Box::new(HandlerFn::new(wrapped)));
421 }
422
423 pub fn register_typed_with_args<T, R, F, Fut>(&mut self, name: &str, handler: F)
425 where
426 T: DeserializeOwned + Send + 'static,
427 R: Serialize + Send + 'static,
428 F: Fn(T) -> Fut + Send + Sync + 'static,
429 Fut: Future<Output = R> + Send + 'static,
430 {
431 let wrapped = move |args: T| {
432 let fut = handler(args);
433 async move { Json(fut.await) }
434 };
435 self.handlers
436 .insert(name.to_string(), Box::new(HandlerWithArgs::new(wrapped)));
437 }
438
439 pub fn register_typed_with_state<S, T, R, F, Fut>(&mut self, name: &str, handler: F)
441 where
442 S: Send + Sync + 'static,
443 T: DeserializeOwned + Send + 'static,
444 R: Serialize + Send + 'static,
445 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
446 Fut: Future<Output = R> + Send + 'static,
447 {
448 let state = self.states.clone();
449 let wrapped = move |s: State<Arc<S>>, args: T| {
450 let fut = handler(s, args);
451 async move { Json(fut.await) }
452 };
453 self.handlers
454 .insert(name.to_string(), Box::new(HandlerWithState::new(wrapped, state)));
455 }
456
457 pub fn register_typed_with_state_only<S, R, F, Fut>(&mut self, name: &str, handler: F)
459 where
460 S: Send + Sync + 'static,
461 R: Serialize + Send + 'static,
462 F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
463 Fut: Future<Output = R> + Send + 'static,
464 {
465 let state = self.states.clone();
466 let wrapped = move |s: State<Arc<S>>| {
467 let fut = handler(s);
468 async move { Json(fut.await) }
469 };
470 self.handlers
471 .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(wrapped, state)));
472 }
473
474 pub fn register_result<R, E, F, Fut>(&mut self, name: &str, handler: F)
481 where
482 R: Serialize + Send + 'static,
483 E: std::fmt::Display + Send + 'static,
484 F: Fn() -> Fut + Send + Sync + 'static,
485 Fut: Future<Output = Result<R, E>> + Send + 'static,
486 {
487 self.handlers
488 .insert(name.to_string(), Box::new(HandlerFn::new(handler)));
489 }
490
491 pub fn register_result_with_args<T, R, E, F, Fut>(&mut self, name: &str, handler: F)
493 where
494 T: DeserializeOwned + Send + 'static,
495 R: Serialize + Send + 'static,
496 E: std::fmt::Display + Send + 'static,
497 F: Fn(T) -> Fut + Send + Sync + 'static,
498 Fut: Future<Output = Result<R, E>> + Send + 'static,
499 {
500 self.handlers
501 .insert(name.to_string(), Box::new(HandlerWithArgs::new(handler)));
502 }
503
504 pub fn register_result_with_state<S, T, R, E, F, Fut>(&mut self, name: &str, handler: F)
506 where
507 S: Send + Sync + 'static,
508 T: DeserializeOwned + Send + 'static,
509 R: Serialize + Send + 'static,
510 E: std::fmt::Display + Send + 'static,
511 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
512 Fut: Future<Output = Result<R, E>> + Send + 'static,
513 {
514 let state = self.states.clone();
515 self.handlers
516 .insert(name.to_string(), Box::new(HandlerWithState::new(handler, state)));
517 }
518
519 pub fn register_result_with_state_only<S, R, E, F, Fut>(&mut self, name: &str, handler: F)
521 where
522 S: Send + Sync + 'static,
523 R: Serialize + Send + 'static,
524 E: std::fmt::Display + Send + 'static,
525 F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
526 Fut: Future<Output = Result<R, E>> + Send + 'static,
527 {
528 let state = self.states.clone();
529 self.handlers
530 .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(handler, state)));
531 }
532
533 pub fn handlers_count(&self) -> usize {
535 self.handlers.len()
536 }
537
538 pub fn register_streaming<F, Fut, R>(&mut self, name: &str, handler: F)
542 where
543 F: Fn(StreamSender) -> Fut + Send + Sync + 'static,
544 Fut: Future<Output = R> + Send + 'static,
545 R: IntoHandlerResult + 'static,
546 {
547 self.streaming_handlers
548 .insert(name.to_string(), Box::new(StreamingHandlerFn::new(handler)));
549 }
550
551 pub fn register_streaming_with_args<T, F, Fut, R>(&mut self, name: &str, handler: F)
553 where
554 T: DeserializeOwned + Send + 'static,
555 F: Fn(T, StreamSender) -> Fut + Send + Sync + 'static,
556 Fut: Future<Output = R> + Send + 'static,
557 R: IntoHandlerResult + 'static,
558 {
559 self.streaming_handlers
560 .insert(name.to_string(), Box::new(StreamingHandlerWithArgs::new(handler)));
561 }
562
563 pub fn register_streaming_with_state<S, T, F, Fut, R>(&mut self, name: &str, handler: F)
565 where
566 S: Send + Sync + 'static,
567 T: DeserializeOwned + Send + 'static,
568 F: Fn(State<Arc<S>>, T, StreamSender) -> Fut + Send + Sync + 'static,
569 Fut: Future<Output = R> + Send + 'static,
570 R: IntoHandlerResult + 'static,
571 {
572 let state = self.states.clone();
573 self.streaming_handlers
574 .insert(name.to_string(), Box::new(StreamingHandlerWithState::new(handler, state)));
575 }
576
577 pub fn register_streaming_with_state_only<S, F, Fut, R>(&mut self, name: &str, handler: F)
579 where
580 S: Send + Sync + 'static,
581 F: Fn(State<Arc<S>>, StreamSender) -> Fut + Send + Sync + 'static,
582 Fut: Future<Output = R> + Send + 'static,
583 R: IntoHandlerResult + 'static,
584 {
585 let state = self.states.clone();
586 self.streaming_handlers
587 .insert(name.to_string(), Box::new(StreamingHandlerWithStateOnly::new(handler, state)));
588 }
589
590 pub fn register_stream<T, St, F, Fut>(&mut self, name: &str, handler: F)
599 where
600 T: IntoStreamItem + 'static,
601 St: Stream<Item = T> + Send + 'static,
602 F: Fn() -> Fut + Send + Sync + 'static,
603 Fut: Future<Output = St> + Send + 'static,
604 {
605 self.register_streaming(name, move |tx: StreamSender| {
606 let stream_fut = handler();
607 async move {
608 drive_stream(stream_fut.await, &tx).await
609 }
610 });
611 }
612
613 pub fn register_stream_with_args<T, Item, St, F, Fut>(&mut self, name: &str, handler: F)
615 where
616 T: DeserializeOwned + Send + 'static,
617 Item: IntoStreamItem + 'static,
618 St: Stream<Item = Item> + Send + 'static,
619 F: Fn(T) -> Fut + Send + Sync + 'static,
620 Fut: Future<Output = St> + Send + 'static,
621 {
622 self.register_streaming_with_args::<T, _, _, _>(name, move |args: T, tx: StreamSender| {
623 let stream_fut = handler(args);
624 async move {
625 drive_stream(stream_fut.await, &tx).await
626 }
627 });
628 }
629
630 pub fn register_stream_with_state<S, T, Item, St, F, Fut>(&mut self, name: &str, handler: F)
632 where
633 S: Send + Sync + 'static,
634 T: DeserializeOwned + Send + 'static,
635 Item: IntoStreamItem + 'static,
636 St: Stream<Item = Item> + Send + 'static,
637 F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
638 Fut: Future<Output = St> + Send + 'static,
639 {
640 self.register_streaming_with_state::<S, T, _, _, _>(name, move |state: State<Arc<S>>, args: T, tx: StreamSender| {
641 let stream_fut = handler(state, args);
642 async move {
643 drive_stream(stream_fut.await, &tx).await
644 }
645 });
646 }
647
648 pub fn is_streaming(&self, name: &str) -> bool {
650 self.streaming_handlers.contains_key(name)
651 }
652
653 #[allow(clippy::type_complexity)]
663 pub fn call_streaming_handler(
664 &self,
665 name: &str,
666 args: &str,
667 ) -> Result<
668 (
669 StreamReceiver,
670 Pin<Box<dyn Future<Output = Result<String, String>> + Send + '_>>,
671 ),
672 String,
673 > {
674 let handler = self
675 .streaming_handlers
676 .get(name)
677 .ok_or_else(|| format!("Streaming handler '{}' not found", name))?;
678
679 let transformed = self.maybe_transform_args(args);
680 let args = transformed.as_deref().unwrap_or(args);
681
682 let (tx, rx) = StreamSender::channel();
683 let fut = handler.call_streaming(args, tx);
684 Ok((rx, fut))
685 }
686
687 #[allow(clippy::type_complexity)]
692 pub fn spawn_streaming_handler(
693 self: &Arc<Self>,
694 name: &str,
695 args: &str,
696 ) -> Result<
697 (
698 StreamReceiver,
699 tokio::task::JoinHandle<Result<String, String>>,
700 ),
701 String,
702 > {
703 if !self.streaming_handlers.contains_key(name) {
704 return Err(format!("Streaming handler '{}' not found", name));
705 }
706
707 let router = self.clone();
708 let name = name.to_string();
709 let args = match self.maybe_transform_args(args) {
710 Some(t) => t,
711 None => args.to_string(),
712 };
713
714 let (tx, rx) = StreamSender::channel();
715
716 let handle = tokio::spawn(async move {
717 let handler = router
718 .streaming_handlers
719 .get(&name)
720 .expect("handler verified to exist");
721 handler.call_streaming(&args, tx).await
722 });
723
724 Ok((rx, handle))
725 }
726
727 pub fn describe_handler(
747 &mut self,
748 name: &str,
749 args: Vec<TsField>,
750 returns: TsType,
751 ) {
752 assert!(
753 self.handlers.contains_key(name),
754 "describe_handler: handler '{}' not registered",
755 name
756 );
757 self.handler_metas
758 .insert(name.to_string(), HandlerMeta::new(args, returns));
759 }
760
761 pub fn describe_streaming_handler(
763 &mut self,
764 name: &str,
765 args: Vec<TsField>,
766 item_type: TsType,
767 final_type: TsType,
768 ) {
769 assert!(
770 self.streaming_handlers.contains_key(name),
771 "describe_streaming_handler: streaming handler '{}' not registered",
772 name
773 );
774 self.handler_metas
775 .insert(name.to_string(), HandlerMeta::streaming(args, item_type, final_type));
776 }
777
778 pub fn generate_ts_client(&self) -> String {
801 generate_ts_client(&self.handler_metas)
802 }
803
804 pub fn handler_meta(&self, name: &str) -> Option<&HandlerMeta> {
806 self.handler_metas.get(name)
807 }
808
809 pub fn add_adapter(&mut self, adapter: Box<dyn ProtocolAdapter>) {
811 self.adapters.insert(adapter.name().to_string(), adapter);
812 }
813
814 pub fn has_adapter(&self, name: &str) -> bool {
816 self.adapters.contains_key(name)
817 }
818
819 pub fn get_adapter(&self, name: &str) -> Option<&dyn ProtocolAdapter> {
821 self.adapters.get(name).map(|b| &**b)
822 }
823
824 pub async fn route_request(&self, protocol: &str, request: &str) -> Result<String, String> {
826 let adapter = self
827 .get_adapter(protocol)
828 .ok_or_else(|| format!("Adapter not found: {}", protocol))?;
829
830 adapter.handle(request).await
831 }
832
833 pub async fn execute(&self, name: &str) -> Result<String, String> {
835 self.execute_with_args(name, "{}").await
836 }
837
838 fn maybe_transform_args(&self, args: &str) -> Option<String> {
840 self.key_transform.map(|t| apply_key_transform(args, t))
841 }
842
843 pub async fn execute_with_args(&self, name: &str, args: &str) -> Result<String, String> {
845 let transformed;
846 let args = match self.maybe_transform_args(args) {
847 Some(t) => {
848 transformed = t;
849 &transformed
850 }
851 None => args,
852 };
853 match self.handlers.get(name) {
854 Some(handler) => handler.call(args).await,
855 None => Err(format!("Handler '{}' not found", name)),
856 }
857 }
858
859 pub fn list_handlers(&self) -> Vec<String> {
864 let mut names: Vec<String> = self.handlers.keys().cloned().collect();
865 names.extend(self.streaming_handlers.keys().cloned());
866 names
867 }
868
869 pub async fn call_handler(&self, name: &str, request: &str) -> Result<String, String> {
874 self.execute_with_args(name, request).await
875 }
876
877 pub fn can_handle_rest(&self, _name: &str) -> bool {
879 self.has_adapter("rest")
880 }
881
882 pub fn can_handle_graphql(&self, _name: &str) -> bool {
884 self.has_adapter("graphql")
885 }
886
887 pub fn can_handle_grpc(&self, _name: &str) -> bool {
889 self.has_adapter("grpc")
890 }
891
892 pub fn enabled_protocols(&self) -> Vec<String> {
894 self.adapters.keys().cloned().collect()
895 }
896
897 pub fn add_route(&mut self, metadata: RouteMetadata) {
902 self.routes.push(metadata);
903 }
904
905 pub fn routes(&self) -> &[RouteMetadata] {
910 &self.routes
911 }
912
913 pub fn get<F, Fut>(&mut self, path: &str, handler: F)
919 where
920 F: Fn() -> Fut + Send + Sync + 'static,
921 Fut: Future<Output = String> + Send + 'static,
922 {
923 let handler_name = format!("GET:{}", path);
924 self.register(&handler_name, handler);
925 self.add_route(RouteMetadata::new(path, Method::GET, "rest"));
926 }
927
928 pub fn post<F, Fut>(&mut self, path: &str, handler: F)
934 where
935 F: Fn() -> Fut + Send + Sync + 'static,
936 Fut: Future<Output = String> + Send + 'static,
937 {
938 let handler_name = format!("POST:{}", path);
939 self.register(&handler_name, handler);
940 self.add_route(RouteMetadata::new(path, Method::POST, "rest"));
941 }
942
943 pub fn put<F, Fut>(&mut self, path: &str, handler: F)
949 where
950 F: Fn() -> Fut + Send + Sync + 'static,
951 Fut: Future<Output = String> + Send + 'static,
952 {
953 let handler_name = format!("PUT:{}", path);
954 self.register(&handler_name, handler);
955 self.add_route(RouteMetadata::new(path, Method::PUT, "rest"));
956 }
957
958 pub fn delete<F, Fut>(&mut self, path: &str, handler: F)
964 where
965 F: Fn() -> Fut + Send + Sync + 'static,
966 Fut: Future<Output = String> + Send + 'static,
967 {
968 let handler_name = format!("DELETE:{}", path);
969 self.register(&handler_name, handler);
970 self.add_route(RouteMetadata::new(path, Method::DELETE, "rest"));
971 }
972
973 pub fn patch<F, Fut>(&mut self, path: &str, handler: F)
979 where
980 F: Fn() -> Fut + Send + Sync + 'static,
981 Fut: Future<Output = String> + Send + 'static,
982 {
983 let handler_name = format!("PATCH:{}", path);
984 self.register(&handler_name, handler);
985 self.add_route(RouteMetadata::new(path, Method::PATCH, "rest"));
986 }
987
988 pub fn head<F, Fut>(&mut self, path: &str, handler: F)
994 where
995 F: Fn() -> Fut + Send + Sync + 'static,
996 Fut: Future<Output = String> + Send + 'static,
997 {
998 let handler_name = format!("HEAD:{}", path);
999 self.register(&handler_name, handler);
1000 self.add_route(RouteMetadata::new(path, Method::HEAD, "rest"));
1001 }
1002
1003 pub fn options<F, Fut>(&mut self, path: &str, handler: F)
1009 where
1010 F: Fn() -> Fut + Send + Sync + 'static,
1011 Fut: Future<Output = String> + Send + 'static,
1012 {
1013 let handler_name = format!("OPTIONS:{}", path);
1014 self.register(&handler_name, handler);
1015 self.add_route(RouteMetadata::new(path, Method::OPTIONS, "rest"));
1016 }
1017
1018 pub async fn call_rest(&self, method: &str, path: &str) -> Result<String, String> {
1020 let adapter = self
1021 .adapters
1022 .get("rest")
1023 .ok_or_else(|| "REST adapter not enabled".to_string())?;
1024
1025 let request = format!("{} {}", method, path);
1026 adapter.handle(&request).await
1027 }
1028
1029 pub async fn call_graphql(&self, query: &str) -> Result<String, String> {
1031 let adapter = self
1032 .adapters
1033 .get("graphql")
1034 .ok_or_else(|| "GraphQL adapter not enabled".to_string())?;
1035
1036 adapter.handle(query).await
1037 }
1038
1039 pub async fn call_grpc(&self, method: &str, request: &str) -> Result<String, String> {
1041 let adapter = self
1042 .adapters
1043 .get("grpc")
1044 .ok_or_else(|| "gRPC adapter not enabled".to_string())?;
1045
1046 let grpc_request = format!("{}:{}", method, request);
1047 adapter.handle(&grpc_request).await
1048 }
1049
1050 pub fn scalar(&self, title: &str, version: &str) -> String {
1072 let config = scalar::ScalarConfig::default();
1073 self.scalar_docs(config, title, version)
1074 }
1075
1076 pub fn scalar_docs(&self, config: scalar::ScalarConfig, title: &str, version: &str) -> String {
1102 let spec = OpenApiGenerator::new(title, version).generate(self);
1104 let spec_json = serde_json::to_string(&spec).unwrap_or_else(|_| "{}".to_string());
1105
1106 scalar::scalar_html(&config, title, &spec_json)
1108 }
1109}
1110
1111impl Default for Router {
1112 fn default() -> Self {
1113 Self::new()
1114 }
1115}
1116
1117#[macro_export]
1176macro_rules! register_handlers {
1177 ($router:expr, [ $($entry:tt)* ]) => {
1178 $crate::register_handlers!(@entries $router, $($entry)*)
1179 };
1180
1181 (@entries $router:expr, ) => {};
1183
1184 (@entries $router:expr, $name:literal => $handler:path, $($rest:tt)*) => {
1186 $router.register($name, $handler);
1187 $crate::register_handlers!(@entries $router, $($rest)*)
1188 };
1189
1190 (@entries $router:expr, args $name:literal => $handler:path, $($rest:tt)*) => {
1192 $router.register_with_args($name, $handler);
1193 $crate::register_handlers!(@entries $router, $($rest)*)
1194 };
1195
1196 (@entries $router:expr, streaming $name:literal => $handler:path, $($rest:tt)*) => {
1198 $router.register_streaming($name, $handler);
1199 $crate::register_handlers!(@entries $router, $($rest)*)
1200 };
1201
1202 (@entries $router:expr, streaming args $name:literal => $handler:path, $($rest:tt)*) => {
1204 $router.register_streaming_with_args($name, $handler);
1205 $crate::register_handlers!(@entries $router, $($rest)*)
1206 };
1207
1208 (@entries $router:expr, state $name:literal => $handler:path, $($rest:tt)*) => {
1210 $router.register_with_state_only($name, $handler);
1211 $crate::register_handlers!(@entries $router, $($rest)*)
1212 };
1213
1214 (@entries $router:expr, state args $name:literal => $handler:path, $($rest:tt)*) => {
1216 $router.register_with_state($name, $handler);
1217 $crate::register_handlers!(@entries $router, $($rest)*)
1218 };
1219
1220 (@entries $router:expr, state streaming $name:literal => $handler:path, $($rest:tt)*) => {
1222 $router.register_streaming_with_state_only($name, $handler);
1223 $crate::register_handlers!(@entries $router, $($rest)*)
1224 };
1225
1226 (@entries $router:expr, state streaming args $name:literal => $handler:path, $($rest:tt)*) => {
1228 $router.register_streaming_with_state($name, $handler);
1229 $crate::register_handlers!(@entries $router, $($rest)*)
1230 };
1231}
1232
1233#[cfg(test)]
1234mod tests {
1235 use super::*;
1236
1237 #[tokio::test]
1238 async fn test_router_creation() {
1239 let router = Router::new();
1240 assert_eq!(router.handlers_count(), 0);
1241 }
1242
1243 #[tokio::test]
1244 async fn test_handler_registration() {
1245 let mut router = Router::new();
1246 router.register("test", || async { "Hello".to_string() });
1247 assert_eq!(router.handlers_count(), 1);
1248 }
1249
1250 #[tokio::test]
1251 async fn test_handler_execution() {
1252 let mut router = Router::new();
1253 router.register("test", || async { "Hello".to_string() });
1254 let result = router.execute("test").await;
1255 assert_eq!(result, Ok("Hello".to_string()));
1256 }
1257
1258 #[tokio::test]
1260 async fn test_router_starts_with_no_routes() {
1261 let router = Router::new();
1262 let routes = router.routes();
1263 assert_eq!(routes.len(), 0);
1264 }
1265
1266 #[tokio::test]
1267 async fn test_add_route_metadata() {
1268 let mut router = Router::new();
1269 let metadata = RouteMetadata::new("/users", "GET", "rest");
1270
1271 router.add_route(metadata.clone());
1272
1273 let routes = router.routes();
1274 assert_eq!(routes.len(), 1);
1275 assert_eq!(routes[0].path, "/users");
1276 assert_eq!(routes[0].method, "GET");
1277 assert_eq!(routes[0].protocol, "rest");
1278 }
1279
1280 #[tokio::test]
1281 async fn test_add_multiple_routes() {
1282 let mut router = Router::new();
1283
1284 router.add_route(RouteMetadata::new("/users", "GET", "rest"));
1285 router.add_route(RouteMetadata::new("/users", "POST", "rest"));
1286 router.add_route(RouteMetadata::new("/posts", "GET", "rest"));
1287
1288 let routes = router.routes();
1289 assert_eq!(routes.len(), 3);
1290 }
1291
1292 #[tokio::test]
1293 async fn test_routes_with_different_protocols() {
1294 let mut router = Router::new();
1295
1296 router.add_route(RouteMetadata::new("/users", "GET", "rest"));
1297 router.add_route(RouteMetadata::new("users", "query", "graphql"));
1298 router.add_route(RouteMetadata::new("UserService.GetUser", "unary", "grpc"));
1299
1300 let routes = router.routes();
1301 assert_eq!(routes.len(), 3);
1302
1303 assert_eq!(routes[0].protocol, "rest");
1304 assert_eq!(routes[1].protocol, "graphql");
1305 assert_eq!(routes[2].protocol, "grpc");
1306 }
1307
1308 #[tokio::test]
1309 async fn test_routes_returns_immutable_reference() {
1310 let mut router = Router::new();
1311 router.add_route(RouteMetadata::new("/test", "GET", "rest"));
1312
1313 let routes1 = router.routes();
1314 let routes2 = router.routes();
1315
1316 assert_eq!(routes1.len(), routes2.len());
1318 assert_eq!(routes1[0].path, routes2[0].path);
1319 }
1320
1321 #[tokio::test]
1323 async fn test_route_get_method() {
1324 let mut router = Router::new();
1325 router.get("/users", || async { "User list".to_string() });
1326
1327 let routes = router.routes();
1328 assert_eq!(routes.len(), 1);
1329 assert_eq!(routes[0].path, "/users");
1330 assert_eq!(routes[0].method, "GET");
1331 assert_eq!(routes[0].protocol, "rest");
1332 }
1333
1334 #[tokio::test]
1335 async fn test_route_post_method() {
1336 let mut router = Router::new();
1337 router.post("/users", || async { "User created".to_string() });
1338
1339 let routes = router.routes();
1340 assert_eq!(routes.len(), 1);
1341 assert_eq!(routes[0].path, "/users");
1342 assert_eq!(routes[0].method, "POST");
1343 assert_eq!(routes[0].protocol, "rest");
1344 }
1345
1346 #[tokio::test]
1347 async fn test_route_put_method() {
1348 let mut router = Router::new();
1349 router.put("/users/1", || async { "User updated".to_string() });
1350
1351 let routes = router.routes();
1352 assert_eq!(routes.len(), 1);
1353 assert_eq!(routes[0].method, "PUT");
1354 }
1355
1356 #[tokio::test]
1357 async fn test_route_delete_method() {
1358 let mut router = Router::new();
1359 router.delete("/users/1", || async { "User deleted".to_string() });
1360
1361 let routes = router.routes();
1362 assert_eq!(routes.len(), 1);
1363 assert_eq!(routes[0].method, "DELETE");
1364 }
1365
1366 #[tokio::test]
1367 async fn test_route_patch_method() {
1368 let mut router = Router::new();
1369 router.patch("/users/1", || async { "User patched".to_string() });
1370
1371 let routes = router.routes();
1372 assert_eq!(routes.len(), 1);
1373 assert_eq!(routes[0].method, "PATCH");
1374 }
1375
1376 #[tokio::test]
1377 async fn test_multiple_routes_different_methods() {
1378 let mut router = Router::new();
1379 router.get("/users", || async { "List".to_string() });
1380 router.post("/users", || async { "Create".to_string() });
1381 router.put("/users/1", || async { "Update".to_string() });
1382 router.delete("/users/1", || async { "Delete".to_string() });
1383
1384 let routes = router.routes();
1385 assert_eq!(routes.len(), 4);
1386
1387 assert_eq!(routes[0].method, "GET");
1388 assert_eq!(routes[1].method, "POST");
1389 assert_eq!(routes[2].method, "PUT");
1390 assert_eq!(routes[3].method, "DELETE");
1391 }
1392
1393 #[tokio::test]
1394 async fn test_route_method_with_path_params() {
1395 let mut router = Router::new();
1396 router.get("/users/{id}", || async { "User details".to_string() });
1397 router.get("/users/{id}/posts/{post_id}", || async {
1398 "Post details".to_string()
1399 });
1400
1401 let routes = router.routes();
1402 assert_eq!(routes.len(), 2);
1403 assert_eq!(routes[0].path, "/users/{id}");
1404 assert_eq!(routes[1].path, "/users/{id}/posts/{post_id}");
1405 }
1406
1407 #[tokio::test]
1408 async fn test_route_registration_and_execution() {
1409 let mut router = Router::new();
1410 router.get("/test", || async { "GET response".to_string() });
1411 router.post("/test", || async { "POST response".to_string() });
1412
1413 assert_eq!(router.routes().len(), 2);
1415 assert_eq!(router.handlers_count(), 2);
1416
1417 let result1 = router.execute("GET:/test").await;
1419 let result2 = router.execute("POST:/test").await;
1420
1421 assert_eq!(result1, Ok("GET response".to_string()));
1422 assert_eq!(result2, Ok("POST response".to_string()));
1423 }
1424
1425 #[tokio::test]
1427 async fn test_scalar_generates_html() {
1428 let mut router = Router::new();
1429 router.get("/users", || async { "Users".to_string() });
1430
1431 let html = router.scalar("Test API", "1.0.0");
1432
1433 assert!(html.contains("<!DOCTYPE html>"));
1434 assert!(html.contains("<title>Test API - API Documentation</title>"));
1435 assert!(html.contains("@scalar/api-reference"));
1436 }
1437
1438 #[tokio::test]
1439 async fn test_scalar_contains_openapi_spec() {
1440 let mut router = Router::new();
1441 router.get("/users", || async { "Users".to_string() });
1442 router.post("/users", || async { "User created".to_string() });
1443
1444 let html = router.scalar("Test API", "1.0.0");
1445
1446 assert!(html.contains("openapi"));
1448 assert!(html.contains("Test API"));
1449 assert!(html.contains("1.0.0"));
1450 }
1451
1452 #[tokio::test]
1453 async fn test_scalar_docs_with_custom_config() {
1454 let mut router = Router::new();
1455 router.get("/users", || async { "Users".to_string() });
1456
1457 let config = scalar::ScalarConfig::new()
1458 .theme(scalar::ScalarTheme::Light)
1459 .show_sidebar(false);
1460
1461 let html = router.scalar_docs(config, "Custom API", "2.0.0");
1462
1463 assert!(html.contains("Custom API"));
1464 assert!(html.contains(r#""theme":"light""#));
1465 assert!(html.contains(r#""showSidebar":false"#));
1466 }
1467
1468 #[tokio::test]
1469 async fn test_scalar_docs_with_custom_css() {
1470 let mut router = Router::new();
1471 router.get("/test", || async { "Test".to_string() });
1472
1473 let config = scalar::ScalarConfig::new().custom_css("body { font-family: 'Inter'; }");
1474
1475 let html = router.scalar_docs(config, "API", "1.0");
1476
1477 assert!(html.contains("<style>body { font-family: 'Inter'; }</style>"));
1478 }
1479
1480 #[tokio::test]
1481 async fn test_scalar_with_multiple_routes() {
1482 let mut router = Router::new();
1483 router.get("/users", || async { "Users".to_string() });
1484 router.post("/users", || async { "Create".to_string() });
1485 router.get("/users/{id}", || async { "User details".to_string() });
1486 router.delete("/users/{id}", || async { "Delete".to_string() });
1487
1488 let html = router.scalar("API", "1.0.0");
1489
1490 assert!(html.contains("/users"));
1492 }
1493
1494 #[tokio::test]
1496 async fn test_get_adapter_returns_adapter() {
1497 let mut router = Router::new();
1498 router.add_adapter(Box::new(RestAdapter::new()));
1499
1500 let adapter = router.get_adapter("rest");
1501 assert!(adapter.is_some());
1502 assert_eq!(adapter.unwrap().name(), "rest");
1503 }
1504
1505 #[tokio::test]
1506 async fn test_get_adapter_returns_none_for_missing() {
1507 let router = Router::new();
1508 let adapter = router.get_adapter("rest");
1509 assert!(adapter.is_none());
1510 }
1511
1512 #[tokio::test]
1513 async fn test_route_request_success() {
1514 let mut router = Router::new();
1515 router.register("test_handler", || async { "Success!".to_string() });
1516
1517 let mut rest_adapter = RestAdapter::new();
1519 rest_adapter.route("GET", "/test", "test_handler");
1520 router.add_adapter(Box::new(rest_adapter));
1521
1522 let result = router.route_request("rest", "GET /test").await;
1523 assert!(result.is_ok());
1524 let response = result.unwrap();
1525 assert!(response.contains("HTTP 200") || response.contains("test_handler"));
1526 }
1527
1528 #[tokio::test]
1529 async fn test_route_request_unknown_adapter() {
1530 let router = Router::new();
1531 let result = router.route_request("unknown", "test").await;
1532 assert!(result.is_err());
1533 assert!(result.unwrap_err().contains("Adapter not found"));
1534 }
1535
1536 #[tokio::test]
1537 async fn test_enabled_protocols_empty() {
1538 let router = Router::new();
1539 let protocols = router.enabled_protocols();
1540 assert_eq!(protocols.len(), 0);
1541 }
1542
1543 #[tokio::test]
1544 async fn test_enabled_protocols_multiple() {
1545 let mut router = Router::new();
1546 router.add_adapter(Box::new(RestAdapter::new()));
1547 router.add_adapter(Box::new(GraphQLAdapter::new()));
1548 router.add_adapter(Box::new(GrpcAdapter::new()));
1549
1550 let protocols = router.enabled_protocols();
1551 assert_eq!(protocols.len(), 3);
1552 assert!(protocols.contains(&"rest".to_string()));
1553 assert!(protocols.contains(&"graphql".to_string()));
1554 assert!(protocols.contains(&"grpc".to_string()));
1555 }
1556
1557 #[tokio::test]
1558 async fn test_can_handle_rest() {
1559 let mut router = Router::new();
1560 assert!(!router.can_handle_rest("test"));
1561
1562 router.add_adapter(Box::new(RestAdapter::new()));
1563 assert!(router.can_handle_rest("test"));
1564 }
1565
1566 #[tokio::test]
1567 async fn test_can_handle_graphql() {
1568 let mut router = Router::new();
1569 assert!(!router.can_handle_graphql("test"));
1570
1571 router.add_adapter(Box::new(GraphQLAdapter::new()));
1572 assert!(router.can_handle_graphql("test"));
1573 }
1574
1575 #[tokio::test]
1576 async fn test_can_handle_grpc() {
1577 let mut router = Router::new();
1578 assert!(!router.can_handle_grpc("test"));
1579
1580 router.add_adapter(Box::new(GrpcAdapter::new()));
1581 assert!(router.can_handle_grpc("test"));
1582 }
1583
1584 #[tokio::test]
1587 async fn test_integration_single_handler_rest() {
1588 let mut router = Router::new();
1590 router.register("get_user", || async { "User data".to_string() });
1591
1592 let mut rest = RestAdapter::new();
1594 rest.route("GET", "/users/:id", "get_user");
1595 router.add_adapter(Box::new(rest));
1596
1597 let response = router.route_request("rest", "GET /users/42").await;
1599 assert!(response.is_ok());
1600 assert!(response.unwrap().contains("get_user"));
1601 }
1602
1603 #[tokio::test]
1604 async fn test_integration_single_handler_graphql() {
1605 let mut router = Router::new();
1607 router.register("get_user", || async { "User data".to_string() });
1608
1609 let mut graphql = GraphQLAdapter::new();
1611 graphql.query("user", "get_user");
1612 router.add_adapter(Box::new(graphql));
1613
1614 let response = router.route_request("graphql", "query { user }").await;
1616 assert!(response.is_ok());
1617 assert!(response.unwrap().contains("get_user"));
1618 }
1619
1620 #[tokio::test]
1621 async fn test_integration_single_handler_grpc() {
1622 let mut router = Router::new();
1624 router.register("get_user", || async { "User data".to_string() });
1625
1626 let mut grpc = GrpcAdapter::new();
1628 grpc.unary("UserService", "GetUser", "get_user");
1629 router.add_adapter(Box::new(grpc));
1630
1631 let response = router
1633 .route_request("grpc", "UserService.GetUser:{\"id\":42}")
1634 .await;
1635 assert!(response.is_ok());
1636 assert!(response.unwrap().contains("get_user"));
1637 }
1638
1639 #[tokio::test]
1640 async fn test_integration_single_handler_all_protocols() {
1641 let mut router = Router::new();
1643 router.register("get_user", || async { "User data".to_string() });
1644
1645 let mut rest = RestAdapter::new();
1647 rest.route("GET", "/users/:id", "get_user");
1648 router.add_adapter(Box::new(rest));
1649
1650 let mut graphql = GraphQLAdapter::new();
1652 graphql.query("user", "get_user");
1653 router.add_adapter(Box::new(graphql));
1654
1655 let mut grpc = GrpcAdapter::new();
1657 grpc.unary("UserService", "GetUser", "get_user");
1658 router.add_adapter(Box::new(grpc));
1659
1660 let rest_response = router.route_request("rest", "GET /users/42").await;
1662 assert!(rest_response.is_ok());
1663 assert!(rest_response.unwrap().contains("get_user"));
1664
1665 let graphql_response = router.route_request("graphql", "query { user }").await;
1667 assert!(graphql_response.is_ok());
1668 assert!(graphql_response.unwrap().contains("get_user"));
1669
1670 let grpc_response = router
1672 .route_request("grpc", "UserService.GetUser:{\"id\":42}")
1673 .await;
1674 assert!(grpc_response.is_ok());
1675 assert!(grpc_response.unwrap().contains("get_user"));
1676 }
1677
1678 #[tokio::test]
1679 async fn test_integration_multiple_handlers_all_protocols() {
1680 let mut router = Router::new();
1682 router.register("get_user", || async { "User data".to_string() });
1683 router.register("list_users", || async { "Users list".to_string() });
1684 router.register("create_user", || async { "Created user".to_string() });
1685
1686 let mut rest = RestAdapter::new();
1688 rest.route("GET", "/users/:id", "get_user");
1689 rest.route("GET", "/users", "list_users");
1690 rest.route("POST", "/users", "create_user");
1691 router.add_adapter(Box::new(rest));
1692
1693 let mut graphql = GraphQLAdapter::new();
1695 graphql.query("user", "get_user");
1696 graphql.query("users", "list_users");
1697 graphql.mutation("createUser", "create_user");
1698 router.add_adapter(Box::new(graphql));
1699
1700 let mut grpc = GrpcAdapter::new();
1702 grpc.unary("UserService", "GetUser", "get_user");
1703 grpc.unary("UserService", "ListUsers", "list_users");
1704 grpc.unary("UserService", "CreateUser", "create_user");
1705 router.add_adapter(Box::new(grpc));
1706
1707 assert!(router
1709 .route_request("rest", "GET /users/42")
1710 .await
1711 .unwrap()
1712 .contains("get_user"));
1713 assert!(router
1714 .route_request("graphql", "query { user }")
1715 .await
1716 .unwrap()
1717 .contains("get_user"));
1718 assert!(router
1719 .route_request("grpc", "UserService.GetUser:{}")
1720 .await
1721 .unwrap()
1722 .contains("get_user"));
1723 }
1724
1725 #[tokio::test]
1726 async fn test_integration_error_handling_rest_404() {
1727 let mut router = Router::new();
1729
1730 let mut rest = RestAdapter::new();
1731 rest.route("GET", "/users/:id", "get_user");
1732 router.add_adapter(Box::new(rest));
1733
1734 let response = router.route_request("rest", "GET /posts/42").await;
1735 assert!(response.is_ok());
1736 assert!(response.unwrap().contains("HTTP 404"));
1737 }
1738
1739 #[tokio::test]
1740 async fn test_integration_error_handling_graphql_not_found() {
1741 let mut router = Router::new();
1743
1744 let mut graphql = GraphQLAdapter::new();
1745 graphql.query("user", "get_user");
1746 router.add_adapter(Box::new(graphql));
1747
1748 let response = router.route_request("graphql", "query { post }").await;
1749 assert!(response.is_ok());
1750 assert!(response.unwrap().contains("errors"));
1751 }
1752
1753 #[tokio::test]
1754 async fn test_integration_error_handling_grpc_unimplemented() {
1755 let mut router = Router::new();
1757
1758 let mut grpc = GrpcAdapter::new();
1759 grpc.unary("UserService", "GetUser", "get_user");
1760 router.add_adapter(Box::new(grpc));
1761
1762 let response = router.route_request("grpc", "UserService.GetPost:{}").await;
1763 assert!(response.is_ok());
1764 assert!(response.unwrap().contains("grpc-status: 12")); }
1766
1767 #[tokio::test]
1768 async fn test_integration_unknown_protocol() {
1769 let router = Router::new();
1771
1772 let response = router.route_request("unknown", "request").await;
1773 assert!(response.is_err());
1774 assert!(response.unwrap_err().contains("Adapter not found"));
1775 }
1776
1777 #[tokio::test]
1778 async fn test_integration_protocol_specific_features_rest_methods() {
1779 let mut router = Router::new();
1781 router.register("get_users", || async { "Users".to_string() });
1782 router.register("create_user", || async { "Created".to_string() });
1783 router.register("update_user", || async { "Updated".to_string() });
1784 router.register("delete_user", || async { "Deleted".to_string() });
1785
1786 let mut rest = RestAdapter::new();
1787 rest.route("GET", "/users", "get_users");
1788 rest.route("POST", "/users", "create_user");
1789 rest.route("PUT", "/users/:id", "update_user");
1790 rest.route("DELETE", "/users/:id", "delete_user");
1791 router.add_adapter(Box::new(rest));
1792
1793 assert!(router
1795 .route_request("rest", "GET /users")
1796 .await
1797 .unwrap()
1798 .contains("get_users"));
1799 assert!(router
1800 .route_request("rest", "POST /users")
1801 .await
1802 .unwrap()
1803 .contains("create_user"));
1804 assert!(router
1805 .route_request("rest", "PUT /users/42")
1806 .await
1807 .unwrap()
1808 .contains("update_user"));
1809 assert!(router
1810 .route_request("rest", "DELETE /users/42")
1811 .await
1812 .unwrap()
1813 .contains("delete_user"));
1814 }
1815
1816 #[tokio::test]
1817 async fn test_integration_protocol_specific_features_graphql_types() {
1818 let mut router = Router::new();
1820 router.register("get_user", || async { "User".to_string() });
1821 router.register("create_user", || async { "Created".to_string() });
1822
1823 let mut graphql = GraphQLAdapter::new();
1824 graphql.query("user", "get_user");
1825 graphql.mutation("createUser", "create_user");
1826 router.add_adapter(Box::new(graphql));
1827
1828 assert!(router
1830 .route_request("graphql", "query { user }")
1831 .await
1832 .unwrap()
1833 .contains("get_user"));
1834
1835 assert!(router
1837 .route_request("graphql", "mutation { createUser }")
1838 .await
1839 .unwrap()
1840 .contains("create_user"));
1841 }
1842
1843 #[tokio::test]
1844 async fn test_integration_protocol_specific_features_grpc_streaming() {
1845 let mut router = Router::new();
1847 router.register("get_user", || async { "User".to_string() });
1848 router.register("list_users", || async { "Users".to_string() });
1849
1850 let mut grpc = GrpcAdapter::new();
1851 grpc.unary("UserService", "GetUser", "get_user");
1852 grpc.server_streaming("UserService", "ListUsers", "list_users");
1853 router.add_adapter(Box::new(grpc));
1854
1855 let unary_response = router
1857 .route_request("grpc", "UserService.GetUser:{}")
1858 .await
1859 .unwrap();
1860 assert!(unary_response.contains("unary"));
1861
1862 let streaming_response = router
1864 .route_request("grpc", "UserService.ListUsers:{}")
1865 .await
1866 .unwrap();
1867 assert!(streaming_response.contains("server_streaming"));
1868 }
1869
1870 #[tokio::test]
1873 async fn test_register_streaming_handler() {
1874 let mut router = Router::new();
1875 router.register_streaming("stream_data", |tx: StreamSender| async move {
1876 tx.send("item".to_string()).await.ok();
1877 "done".to_string()
1878 });
1879 assert!(router.is_streaming("stream_data"));
1880 assert!(!router.is_streaming("nonexistent"));
1881 }
1882
1883 #[tokio::test]
1884 async fn test_register_streaming_with_args() {
1885 #[derive(serde::Deserialize)]
1886 struct Input {
1887 count: usize,
1888 }
1889
1890 let mut router = Router::new();
1891 router.register_streaming_with_args("stream_items", |args: Input, tx: StreamSender| async move {
1892 for i in 0..args.count {
1893 tx.send(format!("item-{i}")).await.ok();
1894 }
1895 "done".to_string()
1896 });
1897 assert!(router.is_streaming("stream_items"));
1898 }
1899
1900 #[tokio::test]
1901 async fn test_streaming_handler_not_in_regular_handlers() {
1902 let mut router = Router::new();
1903 router.register_streaming("stream", |_tx: StreamSender| async move {
1904 "done".to_string()
1905 });
1906 assert_eq!(router.handlers_count(), 0);
1908 }
1909
1910 #[tokio::test]
1911 async fn test_list_handlers_includes_streaming() {
1912 let mut router = Router::new();
1913 router.register("regular", || async { "ok".to_string() });
1914 router.register_streaming("stream", |_tx: StreamSender| async move {
1915 "ok".to_string()
1916 });
1917
1918 let handlers = router.list_handlers();
1919 assert_eq!(handlers.len(), 2);
1920 assert!(handlers.contains(&"regular".to_string()));
1921 assert!(handlers.contains(&"stream".to_string()));
1922 }
1923
1924 #[tokio::test]
1925 async fn test_call_streaming_handler() {
1926 let mut router = Router::new();
1927 router.register_streaming("stream", |tx: StreamSender| async move {
1928 tx.send("a".to_string()).await.ok();
1929 tx.send("b".to_string()).await.ok();
1930 "final".to_string()
1931 });
1932
1933 let (mut rx, fut) = router.call_streaming_handler("stream", "{}").unwrap();
1934 let result = fut.await;
1935
1936 assert_eq!(result, Ok("final".to_string()));
1937 assert_eq!(rx.recv().await, Some("a".to_string()));
1938 assert_eq!(rx.recv().await, Some("b".to_string()));
1939 }
1940
1941 #[tokio::test]
1942 async fn test_call_streaming_handler_with_args() {
1943 #[derive(serde::Deserialize)]
1944 struct Input {
1945 n: usize,
1946 }
1947
1948 let mut router = Router::new();
1949 router.register_streaming_with_args("count", |args: Input, tx: StreamSender| async move {
1950 for i in 0..args.n {
1951 tx.send(format!("{i}")).await.ok();
1952 }
1953 format!("counted to {}", args.n)
1954 });
1955
1956 let (mut rx, fut) = router.call_streaming_handler("count", r#"{"n":3}"#).unwrap();
1957 let result = fut.await;
1958
1959 assert_eq!(result, Ok("counted to 3".to_string()));
1960 assert_eq!(rx.recv().await, Some("0".to_string()));
1961 assert_eq!(rx.recv().await, Some("1".to_string()));
1962 assert_eq!(rx.recv().await, Some("2".to_string()));
1963 }
1964
1965 #[tokio::test]
1966 async fn test_call_streaming_handler_not_found() {
1967 let router = Router::new();
1968 let result = router.call_streaming_handler("missing", "{}");
1969 assert!(result.is_err());
1970 match result {
1971 Err(e) => assert!(e.contains("not found")),
1972 Ok(_) => panic!("expected error"),
1973 }
1974 }
1975
1976 #[tokio::test]
1977 async fn test_is_streaming_false_for_regular() {
1978 let mut router = Router::new();
1979 router.register("regular", || async { "ok".to_string() });
1980 assert!(!router.is_streaming("regular"));
1981 }
1982
1983 #[tokio::test]
1984 async fn test_mixed_router() {
1985 let mut router = Router::new();
1986 router.register("get_user", || async { "user".to_string() });
1987 router.register_streaming("stream_updates", |tx: StreamSender| async move {
1988 tx.send("update".to_string()).await.ok();
1989 "done".to_string()
1990 });
1991
1992 let result = router.execute("get_user").await;
1994 assert_eq!(result, Ok("user".to_string()));
1995
1996 let (mut rx, fut) = router.call_streaming_handler("stream_updates", "{}").unwrap();
1998 let result = fut.await;
1999 assert_eq!(result, Ok("done".to_string()));
2000 assert_eq!(rx.recv().await, Some("update".to_string()));
2001
2002 assert!(!router.is_streaming("get_user"));
2004 assert!(router.call_streaming_handler("get_user", "{}").is_err());
2005 }
2006
2007 #[tokio::test]
2008 async fn test_register_streaming_with_state() {
2009 struct AppState {
2010 prefix: String,
2011 }
2012
2013 #[derive(serde::Deserialize)]
2014 struct Input {
2015 name: String,
2016 }
2017
2018 let mut router = Router::new().with_state(AppState {
2019 prefix: "Hello".to_string(),
2020 });
2021 router.register_streaming_with_state::<AppState, Input, _, _, _>(
2022 "greet_stream",
2023 |state: State<Arc<AppState>>, args: Input, tx: StreamSender| async move {
2024 tx.send(format!("{} {}", state.prefix, args.name))
2025 .await
2026 .ok();
2027 "done".to_string()
2028 },
2029 );
2030
2031 let (mut rx, fut) = router
2032 .call_streaming_handler("greet_stream", r#"{"name":"Alice"}"#)
2033 .unwrap();
2034 let result = fut.await;
2035
2036 assert_eq!(result, Ok("done".to_string()));
2037 assert_eq!(rx.recv().await, Some("Hello Alice".to_string()));
2038 }
2039
2040 #[tokio::test]
2041 async fn test_register_streaming_with_state_only() {
2042 struct AppState {
2043 items: Vec<String>,
2044 }
2045
2046 let mut router = Router::new().with_state(AppState {
2047 items: vec!["x".to_string(), "y".to_string()],
2048 });
2049 router.register_streaming_with_state_only::<AppState, _, _, _>(
2050 "list_stream",
2051 |state: State<Arc<AppState>>, tx: StreamSender| async move {
2052 for item in &state.items {
2053 tx.send(item.clone()).await.ok();
2054 }
2055 format!("listed {}", state.items.len())
2056 },
2057 );
2058
2059 let (mut rx, fut) = router
2060 .call_streaming_handler("list_stream", "{}")
2061 .unwrap();
2062 let result = fut.await;
2063
2064 assert_eq!(result, Ok("listed 2".to_string()));
2065 assert_eq!(rx.recv().await, Some("x".to_string()));
2066 assert_eq!(rx.recv().await, Some("y".to_string()));
2067 }
2068
2069 #[tokio::test]
2072 async fn test_register_stream_no_args() {
2073 let mut router = Router::new();
2074 router.register_stream("items", || async {
2075 tokio_stream::iter(vec!["a".to_string(), "b".to_string(), "c".to_string()])
2076 });
2077
2078 assert!(router.is_streaming("items"));
2079
2080 let (mut rx, fut) = router.call_streaming_handler("items", "{}").unwrap();
2081 let _result = fut.await;
2082
2083 assert_eq!(rx.recv().await, Some("a".to_string()));
2084 assert_eq!(rx.recv().await, Some("b".to_string()));
2085 assert_eq!(rx.recv().await, Some("c".to_string()));
2086 }
2087
2088 #[tokio::test]
2089 async fn test_register_stream_with_args() {
2090 #[derive(serde::Deserialize)]
2091 struct Input {
2092 count: usize,
2093 }
2094
2095 let mut router = Router::new();
2096 router.register_stream_with_args("counting", |args: Input| async move {
2097 tokio_stream::iter((0..args.count).map(|i| format!("{i}")))
2098 });
2099
2100 assert!(router.is_streaming("counting"));
2101
2102 let (mut rx, fut) = router
2103 .call_streaming_handler("counting", r#"{"count":3}"#)
2104 .unwrap();
2105 let _result = fut.await;
2106
2107 assert_eq!(rx.recv().await, Some("0".to_string()));
2108 assert_eq!(rx.recv().await, Some("1".to_string()));
2109 assert_eq!(rx.recv().await, Some("2".to_string()));
2110 }
2111
2112 #[tokio::test]
2113 async fn test_register_stream_with_state() {
2114 struct AppState {
2115 items: Vec<String>,
2116 }
2117
2118 let mut router = Router::new().with_state(AppState {
2119 items: vec!["x".to_string(), "y".to_string()],
2120 });
2121 router.register_stream_with_state::<AppState, serde_json::Value, _, _, _, _>(
2122 "state_stream",
2123 |state: State<Arc<AppState>>, _args: serde_json::Value| {
2124 let items = state.items.clone();
2125 async move { tokio_stream::iter(items) }
2126 },
2127 );
2128
2129 assert!(router.is_streaming("state_stream"));
2130 }
2131
2132 #[tokio::test]
2133 async fn test_stream_adapter_shows_in_is_streaming() {
2134 let mut router = Router::new();
2135 router.register_stream("my_stream", || async {
2136 tokio_stream::iter(vec!["done".to_string()])
2137 });
2138
2139 assert!(router.is_streaming("my_stream"));
2140 assert!(!router.is_streaming("nonexistent"));
2141 }
2142
2143 #[tokio::test]
2144 async fn test_multiple_state_types() {
2145 struct DbPool {
2146 url: String,
2147 }
2148 struct AppConfig {
2149 name: String,
2150 }
2151
2152 #[derive(serde::Deserialize)]
2153 struct Input {
2154 key: String,
2155 }
2156
2157 let mut router = Router::new()
2158 .with_state(DbPool {
2159 url: "postgres://localhost".to_string(),
2160 })
2161 .with_state(AppConfig {
2162 name: "MyApp".to_string(),
2163 });
2164
2165 router.register_with_state::<DbPool, Input, _, _>(
2167 "db_query",
2168 |state: State<Arc<DbPool>>, args: Input| async move {
2169 format!("{}:{}", state.url, args.key)
2170 },
2171 );
2172
2173 router.register_with_state_only::<AppConfig, _, _>(
2175 "app_name",
2176 |state: State<Arc<AppConfig>>| async move { state.name.clone() },
2177 );
2178
2179 let result = router.call_handler("db_query", r#"{"key":"users"}"#).await;
2180 assert_eq!(result, Ok("postgres://localhost:users".to_string()));
2181
2182 let result = router.call_handler("app_name", "{}").await;
2183 assert_eq!(result, Ok("MyApp".to_string()));
2184 }
2185
2186 #[tokio::test]
2187 async fn test_inject_state_after_construction() {
2188 struct LateState {
2189 value: String,
2190 }
2191
2192 let mut router = Router::new();
2193 router.inject_state(LateState {
2194 value: "injected".to_string(),
2195 });
2196 router.register_with_state_only::<LateState, _, _>(
2197 "get_value",
2198 |state: State<Arc<LateState>>| async move { state.value.clone() },
2199 );
2200
2201 let result = router.call_handler("get_value", "{}").await;
2202 assert_eq!(result, Ok("injected".to_string()));
2203 }
2204
2205 #[tokio::test]
2206 async fn test_multiple_state_streaming() {
2207 struct StreamConfig {
2208 prefix: String,
2209 }
2210
2211 let mut router = Router::new().with_state(StreamConfig {
2212 prefix: "stream".to_string(),
2213 });
2214
2215 router.register_streaming_with_state_only::<StreamConfig, _, _, _>(
2216 "prefixed_stream",
2217 |state: State<Arc<StreamConfig>>, tx: StreamSender| async move {
2218 tx.send(format!("{}:item", state.prefix)).await.ok();
2219 "done".to_string()
2220 },
2221 );
2222
2223 let (mut rx, fut) = router
2224 .call_streaming_handler("prefixed_stream", "{}")
2225 .unwrap();
2226 let result = fut.await;
2227 assert_eq!(result, Ok("done".to_string()));
2228 assert_eq!(rx.recv().await, Some("stream:item".to_string()));
2229 }
2230
2231 #[tokio::test]
2232 async fn test_with_state_duplicate_type_last_wins() {
2233 let mut router = Router::new()
2235 .with_state("first".to_string())
2236 .with_state("second".to_string());
2237
2238 router.register_with_state_only::<String, _, _>(
2239 "get",
2240 |state: State<Arc<String>>| async move { (**state).clone() },
2241 );
2242
2243 let result = router.call_handler("get", "{}").await;
2244 assert_eq!(result, Ok("second".to_string()));
2245 }
2246
2247 mod macro_test_handlers {
2251 use super::{State, StreamSender};
2252 use std::sync::Arc;
2253
2254 pub async fn health() -> String {
2255 "ok".to_string()
2256 }
2257
2258 pub async fn echo(args: EchoArgs) -> String {
2259 args.message
2260 }
2261
2262 #[derive(serde::Deserialize)]
2263 pub struct EchoArgs {
2264 pub message: String,
2265 }
2266
2267 pub async fn ticker(tx: StreamSender) -> String {
2268 tx.send("tick".to_string()).await.ok();
2269 "done".to_string()
2270 }
2271
2272 pub async fn search(args: SearchArgs, tx: StreamSender) -> String {
2273 tx.send(format!("found:{}", args.query)).await.ok();
2274 "complete".to_string()
2275 }
2276
2277 #[derive(serde::Deserialize)]
2278 pub struct SearchArgs {
2279 pub query: String,
2280 }
2281
2282 pub async fn get_status(state: State<Arc<String>>) -> String {
2284 format!("status:{}", *state)
2285 }
2286
2287 pub async fn save_key(state: State<Arc<String>>, args: SaveArgs) -> String {
2288 format!("{}:{}", *state, args.key)
2289 }
2290
2291 #[derive(serde::Deserialize)]
2292 pub struct SaveArgs {
2293 pub key: String,
2294 }
2295
2296 pub async fn state_stream(state: State<Arc<String>>, tx: StreamSender) -> String {
2297 tx.send(format!("{}:chunk", *state)).await.ok();
2298 "done".to_string()
2299 }
2300
2301 pub async fn state_search(
2302 state: State<Arc<String>>,
2303 args: SearchArgs,
2304 tx: StreamSender,
2305 ) -> String {
2306 tx.send(format!("{}:{}", *state, args.query)).await.ok();
2307 "complete".to_string()
2308 }
2309 }
2310
2311 #[tokio::test]
2312 async fn test_register_handlers_basic() {
2313 let mut router = Router::new();
2314 register_handlers!(router, [
2315 "health" => macro_test_handlers::health,
2316 ]);
2317 assert_eq!(router.handlers_count(), 1);
2318 let result = router.call_handler("health", "{}").await;
2319 assert_eq!(result, Ok("ok".to_string()));
2320 }
2321
2322 #[tokio::test]
2323 async fn test_register_handlers_with_args() {
2324 let mut router = Router::new();
2325 register_handlers!(router, [
2326 args "echo" => macro_test_handlers::echo,
2327 ]);
2328 assert_eq!(router.handlers_count(), 1);
2329 let result = router
2330 .call_handler("echo", r#"{"message":"hello"}"#)
2331 .await;
2332 assert_eq!(result, Ok("hello".to_string()));
2333 }
2334
2335 #[tokio::test]
2336 async fn test_register_handlers_streaming() {
2337 let mut router = Router::new();
2338 register_handlers!(router, [
2339 streaming "ticker" => macro_test_handlers::ticker,
2340 ]);
2341 assert!(router.is_streaming("ticker"));
2342 let (mut rx, fut) = router.call_streaming_handler("ticker", "{}").unwrap();
2343 let result = fut.await;
2344 assert_eq!(result, Ok("done".to_string()));
2345 assert_eq!(rx.recv().await, Some("tick".to_string()));
2346 }
2347
2348 #[tokio::test]
2349 async fn test_register_handlers_streaming_with_args() {
2350 let mut router = Router::new();
2351 register_handlers!(router, [
2352 streaming args "search" => macro_test_handlers::search,
2353 ]);
2354 assert!(router.is_streaming("search"));
2355 let (mut rx, fut) = router
2356 .call_streaming_handler("search", r#"{"query":"rust"}"#)
2357 .unwrap();
2358 let result = fut.await;
2359 assert_eq!(result, Ok("complete".to_string()));
2360 assert_eq!(rx.recv().await, Some("found:rust".to_string()));
2361 }
2362
2363 #[tokio::test]
2364 async fn test_register_handlers_mixed() {
2365 let mut router = Router::new();
2366 register_handlers!(router, [
2367 "health" => macro_test_handlers::health,
2368 args "echo" => macro_test_handlers::echo,
2369 streaming "ticker" => macro_test_handlers::ticker,
2370 streaming args "search" => macro_test_handlers::search,
2371 ]);
2372
2373 assert_eq!(router.handlers_count(), 2);
2375 assert_eq!(router.list_handlers().len(), 4);
2376
2377 assert_eq!(
2379 router.call_handler("health", "{}").await,
2380 Ok("ok".to_string())
2381 );
2382 assert_eq!(
2383 router
2384 .call_handler("echo", r#"{"message":"hi"}"#)
2385 .await,
2386 Ok("hi".to_string())
2387 );
2388
2389 assert!(router.is_streaming("ticker"));
2391 assert!(router.is_streaming("search"));
2392 }
2393
2394 #[tokio::test]
2395 async fn test_register_handlers_empty() {
2396 let router = Router::new();
2397 register_handlers!(router, []);
2398 assert_eq!(router.handlers_count(), 0);
2399 }
2400
2401 #[tokio::test]
2402 async fn test_register_handlers_state_only() {
2403 let mut router = Router::new().with_state("active".to_string());
2404 register_handlers!(router, [
2405 state "get_status" => macro_test_handlers::get_status,
2406 ]);
2407 let result = router.call_handler("get_status", "{}").await;
2408 assert_eq!(result, Ok("status:active".to_string()));
2409 }
2410
2411 #[tokio::test]
2412 async fn test_register_handlers_state_args() {
2413 let mut router = Router::new().with_state("ns".to_string());
2414 register_handlers!(router, [
2415 state args "save_key" => macro_test_handlers::save_key,
2416 ]);
2417 let result = router
2418 .call_handler("save_key", r#"{"key":"api_token"}"#)
2419 .await;
2420 assert_eq!(result, Ok("ns:api_token".to_string()));
2421 }
2422
2423 #[tokio::test]
2424 async fn test_register_handlers_state_streaming() {
2425 let mut router = Router::new().with_state("ctx".to_string());
2426 register_handlers!(router, [
2427 state streaming "state_stream" => macro_test_handlers::state_stream,
2428 ]);
2429 assert!(router.is_streaming("state_stream"));
2430 let (mut rx, fut) = router
2431 .call_streaming_handler("state_stream", "{}")
2432 .unwrap();
2433 let result = fut.await;
2434 assert_eq!(result, Ok("done".to_string()));
2435 assert_eq!(rx.recv().await, Some("ctx:chunk".to_string()));
2436 }
2437
2438 #[tokio::test]
2439 async fn test_register_handlers_state_streaming_args() {
2440 let mut router = Router::new().with_state("db".to_string());
2441 register_handlers!(router, [
2442 state streaming args "state_search" => macro_test_handlers::state_search,
2443 ]);
2444 assert!(router.is_streaming("state_search"));
2445 let (mut rx, fut) = router
2446 .call_streaming_handler("state_search", r#"{"query":"rust"}"#)
2447 .unwrap();
2448 let result = fut.await;
2449 assert_eq!(result, Ok("complete".to_string()));
2450 assert_eq!(rx.recv().await, Some("db:rust".to_string()));
2451 }
2452
2453 #[tokio::test]
2454 async fn test_register_handlers_mixed_with_state() {
2455 let mut router = Router::new().with_state("app".to_string());
2456 register_handlers!(router, [
2457 "health" => macro_test_handlers::health,
2458 args "echo" => macro_test_handlers::echo,
2459 state "get_status" => macro_test_handlers::get_status,
2460 state args "save_key" => macro_test_handlers::save_key,
2461 streaming "ticker" => macro_test_handlers::ticker,
2462 state streaming "state_stream" => macro_test_handlers::state_stream,
2463 state streaming args "state_search" => macro_test_handlers::state_search,
2464 ]);
2465
2466 assert_eq!(
2468 router.call_handler("health", "{}").await,
2469 Ok("ok".to_string())
2470 );
2471 assert_eq!(
2472 router.call_handler("get_status", "{}").await,
2473 Ok("status:app".to_string())
2474 );
2475 assert_eq!(
2476 router
2477 .call_handler("save_key", r#"{"key":"secret"}"#)
2478 .await,
2479 Ok("app:secret".to_string())
2480 );
2481
2482 assert!(router.is_streaming("ticker"));
2484 assert!(router.is_streaming("state_stream"));
2485 assert!(router.is_streaming("state_search"));
2486 }
2487
2488 #[test]
2491 fn test_camel_to_snake_basic() {
2492 assert_eq!(camel_to_snake("workflowId"), "workflow_id");
2493 assert_eq!(camel_to_snake("actionLabel"), "action_label");
2494 assert_eq!(camel_to_snake("simple"), "simple");
2495 assert_eq!(camel_to_snake("alreadySnake"), "already_snake");
2496 assert_eq!(camel_to_snake("ABC"), "a_b_c");
2497 }
2498
2499 #[test]
2500 fn test_camel_to_snake_single_char() {
2501 assert_eq!(camel_to_snake("a"), "a");
2502 assert_eq!(camel_to_snake("A"), "a");
2503 }
2504
2505 #[test]
2506 fn test_camel_to_snake_empty() {
2507 assert_eq!(camel_to_snake(""), "");
2508 }
2509
2510 #[test]
2511 fn test_camel_to_snake_already_snake() {
2512 assert_eq!(camel_to_snake("already_snake_case"), "already_snake_case");
2513 }
2514
2515 #[test]
2516 fn test_transform_keys_flat_object() {
2517 let input: Value = serde_json::json!({
2518 "workflowId": "abc",
2519 "actionLabel": "run"
2520 });
2521 let result = transform_keys(input, KeyTransform::CamelToSnake);
2522 assert_eq!(result, serde_json::json!({
2523 "workflow_id": "abc",
2524 "action_label": "run"
2525 }));
2526 }
2527
2528 #[test]
2529 fn test_transform_keys_nested_object() {
2530 let input: Value = serde_json::json!({
2531 "outerKey": {
2532 "innerKey": "value"
2533 }
2534 });
2535 let result = transform_keys(input, KeyTransform::CamelToSnake);
2536 assert_eq!(result, serde_json::json!({
2537 "outer_key": {
2538 "inner_key": "value"
2539 }
2540 }));
2541 }
2542
2543 #[test]
2544 fn test_transform_keys_array_of_objects() {
2545 let input: Value = serde_json::json!([
2546 {"firstName": "Alice"},
2547 {"firstName": "Bob"}
2548 ]);
2549 let result = transform_keys(input, KeyTransform::CamelToSnake);
2550 assert_eq!(result, serde_json::json!([
2551 {"first_name": "Alice"},
2552 {"first_name": "Bob"}
2553 ]));
2554 }
2555
2556 #[test]
2557 fn test_transform_keys_primitive_passthrough() {
2558 assert_eq!(transform_keys(Value::Null, KeyTransform::CamelToSnake), Value::Null);
2559 assert_eq!(transform_keys(serde_json::json!(42), KeyTransform::CamelToSnake), serde_json::json!(42));
2560 assert_eq!(transform_keys(serde_json::json!("hello"), KeyTransform::CamelToSnake), serde_json::json!("hello"));
2561 }
2562
2563 #[tokio::test]
2564 async fn test_router_with_key_transform_camel_to_snake() {
2565 #[derive(serde::Deserialize)]
2566 struct Input {
2567 workflow_name: String,
2568 is_active: bool,
2569 }
2570
2571 let mut router = Router::new()
2572 .with_key_transform(KeyTransform::CamelToSnake);
2573 router.register_with_args("test", |args: Input| async move {
2574 format!("{}:{}", args.workflow_name, args.is_active)
2575 });
2576
2577 let result = router
2579 .call_handler("test", r#"{"workflowName":"deploy","isActive":true}"#)
2580 .await;
2581 assert_eq!(result, Ok("deploy:true".to_string()));
2582 }
2583
2584 #[tokio::test]
2585 async fn test_router_with_key_transform_already_snake() {
2586 #[derive(serde::Deserialize)]
2587 struct Input {
2588 workflow_name: String,
2589 }
2590
2591 let mut router = Router::new()
2592 .with_key_transform(KeyTransform::CamelToSnake);
2593 router.register_with_args("test", |args: Input| async move {
2594 args.workflow_name
2595 });
2596
2597 let result = router
2599 .call_handler("test", r#"{"workflow_name":"deploy"}"#)
2600 .await;
2601 assert_eq!(result, Ok("deploy".to_string()));
2602 }
2603
2604 #[tokio::test]
2605 async fn test_router_without_key_transform() {
2606 #[derive(serde::Deserialize)]
2607 struct Input {
2608 workflow_name: String,
2609 }
2610
2611 let mut router = Router::new(); router.register_with_args("test", |args: Input| async move {
2613 args.workflow_name
2614 });
2615
2616 let result = router
2618 .call_handler("test", r#"{"workflowName":"deploy"}"#)
2619 .await;
2620 assert!(result.is_err());
2621 assert!(result.unwrap_err().contains("Failed to deserialize"));
2622 }
2623
2624 #[tokio::test]
2625 async fn test_router_key_transform_streaming_handler() {
2626 #[derive(serde::Deserialize)]
2627 struct Input {
2628 item_count: usize,
2629 }
2630
2631 let mut router = Router::new()
2632 .with_key_transform(KeyTransform::CamelToSnake);
2633 router.register_streaming_with_args("stream", |args: Input, tx: StreamSender| async move {
2634 for i in 0..args.item_count {
2635 tx.send(format!("{i}")).await.ok();
2636 }
2637 "done".to_string()
2638 });
2639
2640 let (mut rx, fut) = router
2641 .call_streaming_handler("stream", r#"{"itemCount":2}"#)
2642 .unwrap();
2643 let result = fut.await;
2644
2645 assert_eq!(result, Ok("done".to_string()));
2646 assert_eq!(rx.recv().await, Some("0".to_string()));
2647 assert_eq!(rx.recv().await, Some("1".to_string()));
2648 }
2649
2650 #[tokio::test]
2651 async fn test_router_key_transform_with_state() {
2652 struct AppState {
2653 prefix: String,
2654 }
2655
2656 #[derive(serde::Deserialize)]
2657 struct Input {
2658 user_name: String,
2659 }
2660
2661 let mut router = Router::new()
2662 .with_key_transform(KeyTransform::CamelToSnake)
2663 .with_state(AppState { prefix: "Hello".to_string() });
2664
2665 router.register_with_state::<AppState, Input, _, _>(
2666 "greet",
2667 |state: State<Arc<AppState>>, args: Input| async move {
2668 format!("{} {}", state.prefix, args.user_name)
2669 },
2670 );
2671
2672 let result = router
2673 .call_handler("greet", r#"{"userName":"Alice"}"#)
2674 .await;
2675 assert_eq!(result, Ok("Hello Alice".to_string()));
2676 }
2677
2678 #[tokio::test]
2679 async fn test_router_key_transform_zero_arg_handler_unaffected() {
2680 let mut router = Router::new()
2681 .with_key_transform(KeyTransform::CamelToSnake);
2682 router.register("health", || async { "ok".to_string() });
2683
2684 let result = router.call_handler("health", "{}").await;
2685 assert_eq!(result, Ok("ok".to_string()));
2686 }
2687}