Skip to main content

allframe_core/router/
mod.rs

1//! # Protocol-Agnostic Router
2//!
3//! Write handlers once, expose them via REST, GraphQL, and gRPC.
4//!
5//! This is AllFrame's core differentiator - the same handler can serve
6//! multiple protocols without code changes.
7//!
8//! ## Quick Start
9//!
10//! ```rust
11//! use allframe_core::router::{Router, RestAdapter, GraphQLAdapter, GrpcAdapter};
12//!
13//! // Create router and register handlers
14//! let mut router = Router::new();
15//! router.register("get_user", || async {
16//!     r#"{"id": 42, "name": "Alice"}"#.to_string()
17//! });
18//!
19//! // Expose via REST
20//! let mut rest = RestAdapter::new();
21//! rest.route("GET", "/users/:id", "get_user");
22//!
23//! // Expose via GraphQL
24//! let mut graphql = GraphQLAdapter::new();
25//! graphql.query("user", "get_user");
26//!
27//! // Expose via gRPC
28//! let mut grpc = GrpcAdapter::new();
29//! grpc.unary("UserService", "GetUser", "get_user");
30//! ```
31//!
32//! ## Key Types
33//!
34//! - `Router` - Central handler registry
35//! - `RestAdapter` - REST protocol adapter
36//! - `GraphQLAdapter` - GraphQL protocol adapter
37//! - `GrpcAdapter` - gRPC protocol adapter
38//! - `ProtocolAdapter` - Trait for custom protocol adapters
39//!
40//! ## API Documentation
41//!
42//! Generate beautiful API documentation automatically:
43//!
44//! - `scalar_html` - Scalar UI for REST APIs (<50KB)
45//! - `graphiql_html` - GraphiQL playground for GraphQL
46//! - `grpc_explorer_html` - gRPC Explorer for gRPC services
47//! - `OpenApiGenerator` - OpenAPI 3.1 spec generation
48//!
49//! ## Configuration-Driven Protocol Selection
50//!
51//! Use TOML configuration to select protocols without code changes:
52//!
53//! ```toml
54//! [server]
55//! protocols = ["rest", "graphql", "grpc"]
56//!
57//! [server.rest]
58//! port = 8080
59//!
60//! [server.graphql]
61//! port = 8081
62//! ```
63
64use serde::de::DeserializeOwned;
65use 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// Production adapters (optional features)
90#[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// Re-export production adapters when features are enabled
106#[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
129/// Drive a `Stream` to completion, forwarding items through a `StreamSender`.
130///
131/// Returns a JSON result: `"null"` on success, or a JSON error string if a
132/// serialization error occurred.
133async 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/// Key transformation strategy applied to JSON args before deserialization.
156///
157/// When a frontend sends camelCase keys (e.g., Tauri 2 convention) but
158/// Rust handler structs use snake_case field names, the router can
159/// automatically transform keys to avoid `#[serde(rename_all)]` on
160/// every input struct.
161///
162/// # Example
163///
164/// ```rust
165/// use allframe_core::router::{Router, KeyTransform};
166///
167/// let router = Router::new()
168///     .with_key_transform(KeyTransform::CamelToSnake);
169/// ```
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum KeyTransform {
172    /// Convert camelCase keys to snake_case (e.g., `workflowId` → `workflow_id`).
173    CamelToSnake,
174}
175
176/// Convert a camelCase string to snake_case.
177fn 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
192/// Recursively transform all object keys in a JSON value.
193fn 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
214/// Apply a key transform to a JSON string, returning the transformed string.
215///
216/// If parsing fails, returns the original string unchanged (the handler's
217/// own deserialization will produce the appropriate error).
218fn 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
225/// Router manages handler registration and protocol adapters
226///
227/// The router allows you to register handlers once and expose them via
228/// multiple protocols based on configuration.
229pub 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    /// Create a new router
244    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    /// Create a new router with configuration
259    #[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        // Auto-register adapters based on config
273        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    /// Set a key transformation applied to JSON args before deserialization.
287    ///
288    /// When enabled, all handler args are transformed before being passed
289    /// to `serde_json::from_str`. This is a single opt-in call that fixes
290    /// key naming mismatches for all handlers.
291    ///
292    /// # Example
293    ///
294    /// ```rust
295    /// use allframe_core::router::{Router, KeyTransform};
296    ///
297    /// let router = Router::new()
298    ///     .with_key_transform(KeyTransform::CamelToSnake);
299    /// // Now `{"workflowId": "abc"}` is transformed to `{"workflow_id": "abc"}`
300    /// // before any handler deserializes it.
301    /// ```
302    pub fn with_key_transform(mut self, transform: KeyTransform) -> Self {
303        self.key_transform = Some(transform);
304        self
305    }
306
307    /// Add shared state for dependency injection (builder pattern).
308    ///
309    /// Can be called multiple times with different types. Each state type
310    /// is stored independently and looked up by `TypeId` at registration time.
311    /// Calling twice with the same `S` replaces the previous value.
312    ///
313    /// ```rust,ignore
314    /// let router = Router::new()
315    ///     .with_state(db_pool)       // handlers can request State<Arc<DbPool>>
316    ///     .with_state(app_handle);   // handlers can request State<Arc<AppHandle>>
317    /// ```
318    pub fn with_state<S: Send + Sync + 'static>(mut self, state: S) -> Self {
319        self.insert_state::<S>(state);
320        self
321    }
322
323    /// Add shared state for dependency injection (mutable, non-builder).
324    ///
325    /// Like `with_state` but takes `&mut self` instead of consuming `self`.
326    /// Useful when the state is not available at router construction time
327    /// (e.g., Tauri's `AppHandle` which is only available during plugin setup).
328    /// Calling twice with the same `S` replaces the previous value.
329    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    /// Returns a handle to the shared state map.
347    ///
348    /// This is the same `Arc` used internally, so state injected through
349    /// the returned handle is visible to handlers at call time. Used by
350    /// `BootContext` to inject state during async boot.
351    pub fn shared_states(&self) -> SharedStateMap {
352        self.states.clone()
353    }
354
355    /// Register a handler with a name (zero-arg, backward compatible)
356    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    /// Register a handler that receives typed, deserialized args
366    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    /// Register a handler that receives injected state and typed args
377    ///
378    /// # Panics
379    ///
380    /// Panics at registration time if `with_state::<S>()` was not called.
381    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    /// Register a handler that receives only injected state (no args)
394    ///
395    /// # Panics
396    ///
397    /// Panics at registration time if `with_state::<S>()` was not called.
398    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    // ─── Typed return registration (auto-serialize via Json wrapper) ─────
410
411    /// Register a handler that returns `R: Serialize` (auto-serialized to JSON)
412    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    /// Register a handler that accepts typed args and returns `R: Serialize`
427    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    /// Register a handler that receives state + typed args and returns `R: Serialize`
443    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    /// Register a handler that receives state only and returns `R: Serialize`
461    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    // ─── Result return registration ─────────────────────────────────────
478
479    /// Register a handler returning `Result<R, E>` (no args)
480    ///
481    /// On `Ok(value)`, `value` is serialized to JSON. On `Err(e)`, the error
482    /// is returned as a string.
483    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    /// Register a handler returning `Result<R, E>` with typed args
495    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    /// Register a handler returning `Result<R, E>` with state + typed args
508    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    /// Register a handler returning `Result<R, E>` with state only
523    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    /// Get the number of registered handlers (request/response only)
537    pub fn handlers_count(&self) -> usize {
538        self.handlers.len()
539    }
540
541    // ─── Non-generic (erased) registration ─────────────────────────────
542    //
543    // These methods accept pre-erased handlers and involve **zero** generic
544    // monomorphization at the call site. Use the `erase_handler!` family of
545    // macros to build the `ErasedHandler` / `ErasedStreamHandler` value.
546    //
547    // This is the recommended path when registering hundreds of handlers,
548    // where cumulative trait-resolution pressure can trigger E0275.
549
550    /// Register a pre-erased handler (non-generic, zero monomorphization).
551    ///
552    /// Pair with one of:
553    /// - [`erase_handler!(handler)`](crate::erase_handler)
554    /// - [`erase_handler_with_args!(handler, ArgsType)`](crate::erase_handler_with_args)
555    /// - [`erase_handler_with_state!(handler, StateType, ArgsType, states)`](crate::erase_handler_with_state)
556    /// - [`erase_handler_with_state_only!(handler, StateType, states)`](crate::erase_handler_with_state_only)
557    ///
558    /// # Example
559    ///
560    /// ```rust,ignore
561    /// use allframe_core::{erase_handler_with_args, router::Router};
562    ///
563    /// router.register_erased(
564    ///     "get_user",
565    ///     erase_handler_with_args!(get_user, GetUserArgs),
566    /// );
567    /// ```
568    pub fn register_erased(&mut self, name: &str, handler: ErasedHandler) {
569        self.handlers.insert(name.to_string(), Box::new(handler));
570    }
571
572    /// Register a pre-erased streaming handler (non-generic).
573    ///
574    /// See [`register_erased`](Self::register_erased) for rationale.
575    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    // ─── Streaming handler registration ─────────────────────────────────
582
583    /// Register a streaming handler (no args, receives StreamSender)
584    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    /// Register a streaming handler with typed args
595    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    /// Register a streaming handler with state and typed args
607    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    /// Register a streaming handler with state only (no args)
621    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    /// Register a handler that returns a `Stream` of items (no args).
634    ///
635    /// The stream is internally bridged to a `StreamSender` channel.
636    /// Items are forwarded through a bounded channel (default capacity 64).
637    /// Register a handler that returns a `Stream` of items (no args).
638    ///
639    /// The stream is internally bridged to a `StreamSender` channel.
640    /// Items are forwarded through a bounded channel (default capacity 64).
641    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    /// Register a handler that takes typed args and returns a `Stream` of items.
657    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    /// Register a handler that takes state + typed args and returns a `Stream` of items.
674    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    /// Check if a handler is a streaming handler
692    pub fn is_streaming(&self, name: &str) -> bool {
693        self.streaming_handlers.contains_key(name)
694    }
695
696    /// Call a streaming handler by name.
697    ///
698    /// Creates a bounded channel, passes the sender to the handler,
699    /// and returns `(receiver, completion_future)`. The completion future
700    /// resolves with the handler's final result.
701    ///
702    /// Note: the returned future borrows `self`. For use in contexts where
703    /// a `'static` future is needed (e.g., `tokio::spawn`), use
704    /// `spawn_streaming_handler` instead.
705    #[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    /// Spawn a streaming handler as a tokio task.
731    ///
732    /// Like `call_streaming_handler` but returns a `'static` receiver and
733    /// `JoinHandle`, suitable for use with `tokio::spawn`.
734    #[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    // ─── TypeScript codegen metadata ────────────────────────────────────
771
772    /// Attach type metadata to a handler for TypeScript client generation
773    ///
774    /// The metadata describes the handler's argument fields and return type,
775    /// which `generate_ts_client()` uses to generate typed async functions.
776    ///
777    /// # Example
778    ///
779    /// ```rust
780    /// use allframe_core::router::{Router, TsField, TsType};
781    ///
782    /// let mut router = Router::new();
783    /// router.register("get_user", || async { r#"{"id":1}"#.to_string() });
784    /// router.describe_handler("get_user", vec![], TsType::Object(vec![
785    ///     TsField::new("id", TsType::Number),
786    ///     TsField::new("name", TsType::String),
787    /// ]));
788    /// ```
789    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    /// Attach type metadata to a streaming handler for TypeScript client generation
805    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    /// Generate a complete TypeScript client module from all described handlers
822    ///
823    /// Returns a string containing TypeScript code with typed async functions
824    /// for each handler that has metadata attached via `describe_handler()`.
825    ///
826    /// # Example
827    ///
828    /// ```rust
829    /// use allframe_core::router::{Router, TsField, TsType};
830    ///
831    /// let mut router = Router::new();
832    /// router.register("get_user", || async { r#"{"id":1}"#.to_string() });
833    /// router.describe_handler("get_user", vec![
834    ///     TsField::new("id", TsType::Number),
835    /// ], TsType::Object(vec![
836    ///     TsField::new("id", TsType::Number),
837    ///     TsField::new("name", TsType::String),
838    /// ]));
839    ///
840    /// let ts_code = router.generate_ts_client();
841    /// assert!(ts_code.contains("export async function getUser"));
842    /// ```
843    pub fn generate_ts_client(&self) -> String {
844        generate_ts_client(&self.handler_metas)
845    }
846
847    /// Get handler metadata (for inspection/testing)
848    pub fn handler_meta(&self, name: &str) -> Option<&HandlerMeta> {
849        self.handler_metas.get(name)
850    }
851
852    /// Add a protocol adapter
853    pub fn add_adapter(&mut self, adapter: Box<dyn ProtocolAdapter>) {
854        self.adapters.insert(adapter.name().to_string(), adapter);
855    }
856
857    /// Check if an adapter is registered
858    pub fn has_adapter(&self, name: &str) -> bool {
859        self.adapters.contains_key(name)
860    }
861
862    /// Get an adapter by name
863    pub fn get_adapter(&self, name: &str) -> Option<&dyn ProtocolAdapter> {
864        self.adapters.get(name).map(|b| &**b)
865    }
866
867    /// Route a request through the appropriate protocol adapter
868    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    /// Execute a handler by name (zero-arg shorthand)
877    pub async fn execute(&self, name: &str) -> Result<String, String> {
878        self.execute_with_args(name, "{}").await
879    }
880
881    /// Apply the configured key transform to args, if any.
882    fn maybe_transform_args(&self, args: &str) -> Option<String> {
883        self.key_transform.map(|t| apply_key_transform(args, t))
884    }
885
886    /// Execute a handler by name with JSON args
887    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    /// List all registered handler names (both request/response and streaming)
903    ///
904    /// Returns a vector of all handler names that have been registered
905    /// with this router. Used by MCP server for tool discovery.
906    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    /// Call a handler by name with request data (JSON args)
913    ///
914    /// Forwards the request string as args to the handler.
915    /// Used by MCP server and Tauri plugin.
916    pub async fn call_handler(&self, name: &str, request: &str) -> Result<String, String> {
917        self.execute_with_args(name, request).await
918    }
919
920    /// Check if handler can be called via REST
921    pub fn can_handle_rest(&self, _name: &str) -> bool {
922        self.has_adapter("rest")
923    }
924
925    /// Check if handler can be called via GraphQL
926    pub fn can_handle_graphql(&self, _name: &str) -> bool {
927        self.has_adapter("graphql")
928    }
929
930    /// Check if handler can be called via gRPC
931    pub fn can_handle_grpc(&self, _name: &str) -> bool {
932        self.has_adapter("grpc")
933    }
934
935    /// Get list of enabled protocols
936    pub fn enabled_protocols(&self) -> Vec<String> {
937        self.adapters.keys().cloned().collect()
938    }
939
940    /// Add a route with metadata
941    ///
942    /// This stores route metadata that can be used to generate
943    /// documentation (OpenAPI, GraphQL schemas, gRPC reflection).
944    pub fn add_route(&mut self, metadata: RouteMetadata) {
945        self.routes.push(metadata);
946    }
947
948    /// Get all registered routes
949    ///
950    /// Returns an immutable reference to all route metadata.
951    /// This is used for documentation generation.
952    pub fn routes(&self) -> &[RouteMetadata] {
953        &self.routes
954    }
955
956    /// Register a GET route
957    ///
958    /// This is a convenience method that registers both a handler and route
959    /// metadata for a GET request. The handler name is automatically
960    /// generated as "GET:{path}".
961    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    /// Register a POST route
972    ///
973    /// This is a convenience method that registers both a handler and route
974    /// metadata for a POST request. The handler name is automatically
975    /// generated as "POST:{path}".
976    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    /// Register a PUT route
987    ///
988    /// This is a convenience method that registers both a handler and route
989    /// metadata for a PUT request. The handler name is automatically
990    /// generated as "PUT:{path}".
991    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    /// Register a DELETE route
1002    ///
1003    /// This is a convenience method that registers both a handler and route
1004    /// metadata for a DELETE request. The handler name is automatically
1005    /// generated as "DELETE:{path}".
1006    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    /// Register a PATCH route
1017    ///
1018    /// This is a convenience method that registers both a handler and route
1019    /// metadata for a PATCH request. The handler name is automatically
1020    /// generated as "PATCH:{path}".
1021    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    /// Register a HEAD route
1032    ///
1033    /// This is a convenience method that registers both a handler and route
1034    /// metadata for a HEAD request. The handler name is automatically
1035    /// generated as "HEAD:{path}".
1036    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    /// Register an OPTIONS route
1047    ///
1048    /// This is a convenience method that registers both a handler and route
1049    /// metadata for an OPTIONS request. The handler name is automatically
1050    /// generated as "OPTIONS:{path}".
1051    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    /// Call handler via REST
1062    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    /// Call handler via GraphQL
1073    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    /// Call handler via gRPC
1083    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    /// Generate Scalar documentation HTML with default configuration
1094    ///
1095    /// This is a convenience method that generates a complete HTML page
1096    /// with Scalar UI for interactive API documentation.
1097    ///
1098    /// # Arguments
1099    ///
1100    /// * `title` - API title
1101    /// * `version` - API version
1102    ///
1103    /// # Example
1104    ///
1105    /// ```rust
1106    /// use allframe_core::router::Router;
1107    ///
1108    /// let mut router = Router::new();
1109    /// router.get("/users", || async { "Users".to_string() });
1110    ///
1111    /// let html = router.scalar("My API", "1.0.0");
1112    /// // Serve this HTML at /docs endpoint
1113    /// ```
1114    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    /// Generate Scalar documentation HTML with custom configuration
1120    ///
1121    /// This method allows full customization of the Scalar UI appearance
1122    /// and behavior.
1123    ///
1124    /// # Arguments
1125    ///
1126    /// * `config` - Scalar configuration
1127    /// * `title` - API title
1128    /// * `version` - API version
1129    ///
1130    /// # Example
1131    ///
1132    /// ```rust
1133    /// use allframe_core::router::{Router, ScalarConfig, ScalarTheme};
1134    ///
1135    /// let mut router = Router::new();
1136    /// router.get("/users", || async { "Users".to_string() });
1137    ///
1138    /// let config = ScalarConfig::new()
1139    ///     .theme(ScalarTheme::Light)
1140    ///     .show_sidebar(false);
1141    ///
1142    /// let html = router.scalar_docs(config, "My API", "1.0.0");
1143    /// ```
1144    pub fn scalar_docs(&self, config: scalar::ScalarConfig, title: &str, version: &str) -> String {
1145        // Generate OpenAPI spec
1146        let spec = OpenApiGenerator::new(title, version).generate(self);
1147        let spec_json = serde_json::to_string(&spec).unwrap_or_else(|_| "{}".to_string());
1148
1149        // Generate Scalar HTML
1150        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/// Bulk-register handler functions with a Router.
1161///
1162/// Each function is referenced by path in the expansion, so the compiler
1163/// sees it as used — eliminating false `dead_code` warnings that occur when
1164/// handler functions are only referenced through closure-based registration.
1165///
1166/// # Syntax
1167///
1168/// ```rust,ignore
1169/// use allframe_core::register_handlers;
1170///
1171/// register_handlers!(router, [
1172///     // Basic handler (zero-arg, returns String)
1173///     "health" => health_check,
1174///
1175///     // Handler with typed, deserializable args
1176///     args "greet" => greet,
1177///
1178///     // Streaming handler (receives StreamSender)
1179///     streaming "feed" => live_feed,
1180///
1181///     // Streaming handler with typed args
1182///     streaming args "search" => search_stream,
1183///
1184///     // State-only handler (receives State<Arc<S>>)
1185///     state "get_status" => get_ollama_status,
1186///
1187///     // State with typed args
1188///     state args "save_key" => save_api_key,
1189///
1190///     // State streaming (receives State<Arc<S>> + StreamSender)
1191///     state streaming "stream_chat" => stream_chat,
1192///
1193///     // State streaming with typed args
1194///     state streaming args "start_job" => start_orchestrator,
1195/// ]);
1196/// ```
1197///
1198/// # Expands to
1199///
1200/// ```rust,ignore
1201/// router.register("health", health_check);
1202/// router.register_with_args("greet", greet);
1203/// router.register_streaming("feed", live_feed);
1204/// router.register_streaming_with_args("search", search_stream);
1205/// router.register_with_state_only("get_status", get_ollama_status);
1206/// router.register_with_state("save_key", save_api_key);
1207/// router.register_streaming_with_state_only("stream_chat", stream_chat);
1208/// router.register_streaming_with_state("start_job", start_orchestrator);
1209/// ```
1210///
1211/// # Why this helps
1212///
1213/// When handler functions are defined in separate modules and only called via
1214/// `router.register("name", || async { handler().await })`, the compiler
1215/// cannot always trace the usage chain and reports `dead_code` warnings.
1216/// This macro passes each function directly as a function item, making the
1217/// reference visible to `rustc`'s dead-code analysis.
1218#[macro_export]
1219macro_rules! register_handlers {
1220    ($router:expr, [ $($entry:tt)* ]) => {
1221        $crate::register_handlers!(@entries $router, $($entry)*)
1222    };
1223
1224    // Terminal case
1225    (@entries $router:expr, ) => {};
1226
1227    // Basic: "name" => handler,
1228    (@entries $router:expr, $name:literal => $handler:path, $($rest:tt)*) => {
1229        $router.register($name, $handler);
1230        $crate::register_handlers!(@entries $router, $($rest)*)
1231    };
1232
1233    // With args: args "name" => handler,
1234    (@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    // Streaming: streaming "name" => handler,
1240    (@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    // Streaming with args: streaming args "name" => handler,
1246    (@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    // State only: state "name" => handler,
1252    (@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    // State with args: state args "name" => handler,
1258    (@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    // State streaming: state streaming "name" => handler,
1264    (@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    // State streaming with args: state streaming args "name" => handler,
1270    (@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    // New tests for route metadata tracking
1302    #[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        // Both should have the same data
1360        assert_eq!(routes1.len(), routes2.len());
1361        assert_eq!(routes1[0].path, routes2[0].path);
1362    }
1363
1364    // Tests for type-safe route registration
1365    #[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        // Both route metadata and handler should be registered
1457        assert_eq!(router.routes().len(), 2);
1458        assert_eq!(router.handlers_count(), 2);
1459
1460        // Handlers should be executable
1461        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    // Tests for Scalar integration
1469    #[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        // Should contain the OpenAPI spec
1490        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        // Should contain all routes in the OpenAPI spec
1534        assert!(html.contains("/users"));
1535    }
1536
1537    // Tests for protocol adapter management
1538    #[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        // Register adapter with a route
1561        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    // ===== Integration Tests: Multi-Protocol Routing =====
1628
1629    #[tokio::test]
1630    async fn test_integration_single_handler_rest() {
1631        // Test: Single handler exposed via REST
1632        let mut router = Router::new();
1633        router.register("get_user", || async { "User data".to_string() });
1634
1635        // Configure REST adapter
1636        let mut rest = RestAdapter::new();
1637        rest.route("GET", "/users/:id", "get_user");
1638        router.add_adapter(Box::new(rest));
1639
1640        // Route REST request
1641        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        // Test: Single handler exposed via GraphQL
1649        let mut router = Router::new();
1650        router.register("get_user", || async { "User data".to_string() });
1651
1652        // Configure GraphQL adapter
1653        let mut graphql = GraphQLAdapter::new();
1654        graphql.query("user", "get_user");
1655        router.add_adapter(Box::new(graphql));
1656
1657        // Route GraphQL request
1658        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        // Test: Single handler exposed via gRPC
1666        let mut router = Router::new();
1667        router.register("get_user", || async { "User data".to_string() });
1668
1669        // Configure gRPC adapter
1670        let mut grpc = GrpcAdapter::new();
1671        grpc.unary("UserService", "GetUser", "get_user");
1672        router.add_adapter(Box::new(grpc));
1673
1674        // Route gRPC request
1675        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        // Test: Single handler exposed via ALL protocols
1685        let mut router = Router::new();
1686        router.register("get_user", || async { "User data".to_string() });
1687
1688        // Configure REST adapter
1689        let mut rest = RestAdapter::new();
1690        rest.route("GET", "/users/:id", "get_user");
1691        router.add_adapter(Box::new(rest));
1692
1693        // Configure GraphQL adapter
1694        let mut graphql = GraphQLAdapter::new();
1695        graphql.query("user", "get_user");
1696        router.add_adapter(Box::new(graphql));
1697
1698        // Configure gRPC adapter
1699        let mut grpc = GrpcAdapter::new();
1700        grpc.unary("UserService", "GetUser", "get_user");
1701        router.add_adapter(Box::new(grpc));
1702
1703        // Test REST
1704        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        // Test GraphQL
1709        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        // Test gRPC
1714        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        // Test: Multiple handlers, each exposed via all protocols
1724        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        // Configure REST adapter
1730        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        // Configure GraphQL adapter
1737        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        // Configure gRPC adapter
1744        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        // Test each handler via each protocol
1751        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        // Test: REST 404 error
1771        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        // Test: GraphQL operation not found
1785        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        // Test: gRPC method not implemented
1799        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")); // UNIMPLEMENTED
1808    }
1809
1810    #[tokio::test]
1811    async fn test_integration_unknown_protocol() {
1812        // Test: Unknown protocol error
1813        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        // Test: REST-specific HTTP methods
1823        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        // Test different HTTP methods
1837        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        // Test: GraphQL-specific query vs mutation
1862        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        // Test query
1872        assert!(router
1873            .route_request("graphql", "query { user }")
1874            .await
1875            .unwrap()
1876            .contains("get_user"));
1877
1878        // Test mutation
1879        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        // Test: gRPC-specific streaming modes
1889        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        // Test unary
1899        let unary_response = router
1900            .route_request("grpc", "UserService.GetUser:{}")
1901            .await
1902            .unwrap();
1903        assert!(unary_response.contains("unary"));
1904
1905        // Test server streaming
1906        let streaming_response = router
1907            .route_request("grpc", "UserService.ListUsers:{}")
1908            .await
1909            .unwrap();
1910        assert!(streaming_response.contains("server_streaming"));
1911    }
1912
1913    // ===== Streaming handler registration tests =====
1914
1915    #[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        // Streaming handlers are NOT in the regular handlers map
1950        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        // Regular handler works
2036        let result = router.execute("get_user").await;
2037        assert_eq!(result, Ok("user".to_string()));
2038
2039        // Streaming handler works
2040        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        // Regular handler not found in streaming
2046        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    // ===== Stream return adapter tests =====
2113
2114    #[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        // Handler using DbPool
2209        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        // Handler using AppConfig
2217        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        // Calling with_state twice with the same type replaces the previous value.
2277        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    // ─── register_handlers! macro tests ─────────────────────────────────
2291
2292    // Define handler functions at module scope so they're visible as paths
2293    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        // State handler functions for register_handlers! state variants
2326        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        // All 4 registered (2 regular + 2 streaming)
2417        assert_eq!(router.handlers_count(), 2);
2418        assert_eq!(router.list_handlers().len(), 4);
2419
2420        // Regular handlers work
2421        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        // Streaming handlers work
2433        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        // Regular handlers
2510        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        // Streaming handlers
2526        assert!(router.is_streaming("ticker"));
2527        assert!(router.is_streaming("state_stream"));
2528        assert!(router.is_streaming("state_search"));
2529    }
2530
2531    // ─── KeyTransform tests ─────────────────────────────────────────────
2532
2533    #[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        // camelCase keys should be transformed to snake_case
2621        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        // snake_case keys should pass through unchanged
2641        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(); // no key transform
2655        router.register_with_args("test", |args: Input| async move {
2656            args.workflow_name
2657        });
2658
2659        // camelCase keys should fail without transform
2660        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    // ─── Erased registration tests ──────────────────────────────────��──
2732
2733    #[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}