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    Handler, HandlerFn, HandlerWithArgs, HandlerWithState, HandlerWithStateOnly,
114    IntoHandlerResult, IntoStreamItem, Json, SharedStateMap, State, StreamError, StreamHandler,
115    StreamReceiver, StreamSender, StreamingHandlerFn, StreamingHandlerWithArgs,
116    StreamingHandlerWithState, StreamingHandlerWithStateOnly, DEFAULT_STREAM_CAPACITY,
117};
118pub use metadata::RouteMetadata;
119pub use method::Method;
120pub use openapi::{OpenApiGenerator, OpenApiServer};
121pub use rest::{RestAdapter, RestRequest, RestResponse, RestRoute};
122pub use scalar::{scalar_html, ScalarConfig, ScalarLayout, ScalarTheme};
123pub use schema::ToJsonSchema;
124pub use ts_codegen::{generate_ts_client, HandlerMeta, TsField, TsType};
125
126/// Drive a `Stream` to completion, forwarding items through a `StreamSender`.
127///
128/// Returns a JSON result: `"null"` on success, or a JSON error string if a
129/// serialization error occurred.
130async fn drive_stream<T, St>(stream: St, tx: &StreamSender) -> String
131where
132    T: IntoStreamItem,
133    St: Stream<Item = T> + Send,
134{
135    tokio::pin!(stream);
136    loop {
137        let next = std::future::poll_fn(|cx| stream.as_mut().poll_next(cx)).await;
138        match next {
139            Some(item) => match tx.send(item).await {
140                Ok(()) => {}
141                Err(StreamError::Closed) => break,
142                Err(StreamError::Serialize(e)) => {
143                    return serde_json::json!({"error": e}).to_string();
144                }
145            },
146            None => break,
147        }
148    }
149    "null".to_string()
150}
151
152/// Key transformation strategy applied to JSON args before deserialization.
153///
154/// When a frontend sends camelCase keys (e.g., Tauri 2 convention) but
155/// Rust handler structs use snake_case field names, the router can
156/// automatically transform keys to avoid `#[serde(rename_all)]` on
157/// every input struct.
158///
159/// # Example
160///
161/// ```rust
162/// use allframe_core::router::{Router, KeyTransform};
163///
164/// let router = Router::new()
165///     .with_key_transform(KeyTransform::CamelToSnake);
166/// ```
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub enum KeyTransform {
169    /// Convert camelCase keys to snake_case (e.g., `workflowId` → `workflow_id`).
170    CamelToSnake,
171}
172
173/// Convert a camelCase string to snake_case.
174fn camel_to_snake(s: &str) -> String {
175    let mut result = String::with_capacity(s.len() + 4);
176    for (i, ch) in s.chars().enumerate() {
177        if ch.is_ascii_uppercase() {
178            if i > 0 {
179                result.push('_');
180            }
181            result.push(ch.to_ascii_lowercase());
182        } else {
183            result.push(ch);
184        }
185    }
186    result
187}
188
189/// Recursively transform all object keys in a JSON value.
190fn transform_keys(value: Value, transform: KeyTransform) -> Value {
191    match value {
192        Value::Object(map) => {
193            let new_map: Map<String, Value> = map
194                .into_iter()
195                .map(|(k, v)| {
196                    let new_key = match transform {
197                        KeyTransform::CamelToSnake => camel_to_snake(&k),
198                    };
199                    (new_key, transform_keys(v, transform))
200                })
201                .collect();
202            Value::Object(new_map)
203        }
204        Value::Array(arr) => {
205            Value::Array(arr.into_iter().map(|v| transform_keys(v, transform)).collect())
206        }
207        other => other,
208    }
209}
210
211/// Apply a key transform to a JSON string, returning the transformed string.
212///
213/// If parsing fails, returns the original string unchanged (the handler's
214/// own deserialization will produce the appropriate error).
215fn apply_key_transform(args: &str, transform: KeyTransform) -> String {
216    match serde_json::from_str::<Value>(args) {
217        Ok(value) => transform_keys(value, transform).to_string(),
218        Err(_) => args.to_string(),
219    }
220}
221
222/// Router manages handler registration and protocol adapters
223///
224/// The router allows you to register handlers once and expose them via
225/// multiple protocols based on configuration.
226pub struct Router {
227    handlers: HashMap<String, Box<dyn Handler>>,
228    streaming_handlers: HashMap<String, Box<dyn StreamHandler>>,
229    adapters: HashMap<String, Box<dyn ProtocolAdapter>>,
230    routes: Vec<RouteMetadata>,
231    states: SharedStateMap,
232    handler_metas: HashMap<String, HandlerMeta>,
233    key_transform: Option<KeyTransform>,
234    #[cfg(feature = "router")]
235    #[allow(dead_code)]
236    config: Option<RouterConfig>,
237}
238
239impl Router {
240    /// Create a new router
241    pub fn new() -> Self {
242        Self {
243            handlers: HashMap::new(),
244            streaming_handlers: HashMap::new(),
245            adapters: HashMap::new(),
246            routes: Vec::new(),
247            states: Arc::new(std::sync::RwLock::new(HashMap::new())),
248            handler_metas: HashMap::new(),
249            key_transform: None,
250            #[cfg(feature = "router")]
251            config: None,
252        }
253    }
254
255    /// Create a new router with configuration
256    #[cfg(feature = "router")]
257    pub fn with_config(config: RouterConfig) -> Self {
258        let mut router = Self {
259            handlers: HashMap::new(),
260            streaming_handlers: HashMap::new(),
261            adapters: HashMap::new(),
262            routes: Vec::new(),
263            states: Arc::new(std::sync::RwLock::new(HashMap::new())),
264            handler_metas: HashMap::new(),
265            key_transform: None,
266            config: Some(config.clone()),
267        };
268
269        // Auto-register adapters based on config
270        if config.has_protocol("rest") {
271            router.add_adapter(Box::new(RestAdapter::new()));
272        }
273        if config.has_protocol("graphql") {
274            router.add_adapter(Box::new(GraphQLAdapter::new()));
275        }
276        if config.has_protocol("grpc") {
277            router.add_adapter(Box::new(GrpcAdapter::new()));
278        }
279
280        router
281    }
282
283    /// Set a key transformation applied to JSON args before deserialization.
284    ///
285    /// When enabled, all handler args are transformed before being passed
286    /// to `serde_json::from_str`. This is a single opt-in call that fixes
287    /// key naming mismatches for all handlers.
288    ///
289    /// # Example
290    ///
291    /// ```rust
292    /// use allframe_core::router::{Router, KeyTransform};
293    ///
294    /// let router = Router::new()
295    ///     .with_key_transform(KeyTransform::CamelToSnake);
296    /// // Now `{"workflowId": "abc"}` is transformed to `{"workflow_id": "abc"}`
297    /// // before any handler deserializes it.
298    /// ```
299    pub fn with_key_transform(mut self, transform: KeyTransform) -> Self {
300        self.key_transform = Some(transform);
301        self
302    }
303
304    /// Add shared state for dependency injection (builder pattern).
305    ///
306    /// Can be called multiple times with different types. Each state type
307    /// is stored independently and looked up by `TypeId` at registration time.
308    /// Calling twice with the same `S` replaces the previous value.
309    ///
310    /// ```rust,ignore
311    /// let router = Router::new()
312    ///     .with_state(db_pool)       // handlers can request State<Arc<DbPool>>
313    ///     .with_state(app_handle);   // handlers can request State<Arc<AppHandle>>
314    /// ```
315    pub fn with_state<S: Send + Sync + 'static>(mut self, state: S) -> Self {
316        self.insert_state::<S>(state);
317        self
318    }
319
320    /// Add shared state for dependency injection (mutable, non-builder).
321    ///
322    /// Like `with_state` but takes `&mut self` instead of consuming `self`.
323    /// Useful when the state is not available at router construction time
324    /// (e.g., Tauri's `AppHandle` which is only available during plugin setup).
325    /// Calling twice with the same `S` replaces the previous value.
326    pub fn inject_state<S: Send + Sync + 'static>(&mut self, state: S) {
327        self.insert_state::<S>(state);
328    }
329
330    fn insert_state<S: Send + Sync + 'static>(&mut self, state: S) {
331        let id = std::any::TypeId::of::<S>();
332        let mut map = self.states.write().expect("state lock poisoned");
333        if map.contains_key(&id) {
334            #[cfg(debug_assertions)]
335            eprintln!(
336                "allframe: with_state called twice for type `{}` — previous value replaced",
337                std::any::type_name::<S>()
338            );
339        }
340        map.insert(id, Arc::new(state));
341    }
342
343    /// Returns a handle to the shared state map.
344    ///
345    /// This is the same `Arc` used internally, so state injected through
346    /// the returned handle is visible to handlers at call time. Used by
347    /// `BootContext` to inject state during async boot.
348    pub fn shared_states(&self) -> SharedStateMap {
349        self.states.clone()
350    }
351
352    /// Register a handler with a name (zero-arg, backward compatible)
353    pub fn register<F, Fut>(&mut self, name: &str, handler: F)
354    where
355        F: Fn() -> Fut + Send + Sync + 'static,
356        Fut: Future<Output = String> + Send + 'static,
357    {
358        self.handlers
359            .insert(name.to_string(), Box::new(HandlerFn::new(handler)));
360    }
361
362    /// Register a handler that receives typed, deserialized args
363    pub fn register_with_args<T, F, Fut>(&mut self, name: &str, handler: F)
364    where
365        T: DeserializeOwned + Send + 'static,
366        F: Fn(T) -> Fut + Send + Sync + 'static,
367        Fut: Future<Output = String> + Send + 'static,
368    {
369        self.handlers
370            .insert(name.to_string(), Box::new(HandlerWithArgs::new(handler)));
371    }
372
373    /// Register a handler that receives injected state and typed args
374    ///
375    /// # Panics
376    ///
377    /// Panics at registration time if `with_state::<S>()` was not called.
378    pub fn register_with_state<S, T, F, Fut>(&mut self, name: &str, handler: F)
379    where
380        S: Send + Sync + 'static,
381        T: DeserializeOwned + Send + 'static,
382        F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
383        Fut: Future<Output = String> + Send + 'static,
384    {
385        let state = self.states.clone();
386        self.handlers
387            .insert(name.to_string(), Box::new(HandlerWithState::new(handler, state)));
388    }
389
390    /// Register a handler that receives only injected state (no args)
391    ///
392    /// # Panics
393    ///
394    /// Panics at registration time if `with_state::<S>()` was not called.
395    pub fn register_with_state_only<S, F, Fut>(&mut self, name: &str, handler: F)
396    where
397        S: Send + Sync + 'static,
398        F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
399        Fut: Future<Output = String> + Send + 'static,
400    {
401        let state = self.states.clone();
402        self.handlers
403            .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(handler, state)));
404    }
405
406    // ─── Typed return registration (auto-serialize via Json wrapper) ─────
407
408    /// Register a handler that returns `R: Serialize` (auto-serialized to JSON)
409    pub fn register_typed<R, F, Fut>(&mut self, name: &str, handler: F)
410    where
411        R: Serialize + Send + 'static,
412        F: Fn() -> Fut + Send + Sync + 'static,
413        Fut: Future<Output = R> + Send + 'static,
414    {
415        let wrapped = move || {
416            let fut = handler();
417            async move { Json(fut.await) }
418        };
419        self.handlers
420            .insert(name.to_string(), Box::new(HandlerFn::new(wrapped)));
421    }
422
423    /// Register a handler that accepts typed args and returns `R: Serialize`
424    pub fn register_typed_with_args<T, R, F, Fut>(&mut self, name: &str, handler: F)
425    where
426        T: DeserializeOwned + Send + 'static,
427        R: Serialize + Send + 'static,
428        F: Fn(T) -> Fut + Send + Sync + 'static,
429        Fut: Future<Output = R> + Send + 'static,
430    {
431        let wrapped = move |args: T| {
432            let fut = handler(args);
433            async move { Json(fut.await) }
434        };
435        self.handlers
436            .insert(name.to_string(), Box::new(HandlerWithArgs::new(wrapped)));
437    }
438
439    /// Register a handler that receives state + typed args and returns `R: Serialize`
440    pub fn register_typed_with_state<S, T, R, F, Fut>(&mut self, name: &str, handler: F)
441    where
442        S: Send + Sync + 'static,
443        T: DeserializeOwned + Send + 'static,
444        R: Serialize + Send + 'static,
445        F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
446        Fut: Future<Output = R> + Send + 'static,
447    {
448        let state = self.states.clone();
449        let wrapped = move |s: State<Arc<S>>, args: T| {
450            let fut = handler(s, args);
451            async move { Json(fut.await) }
452        };
453        self.handlers
454            .insert(name.to_string(), Box::new(HandlerWithState::new(wrapped, state)));
455    }
456
457    /// Register a handler that receives state only and returns `R: Serialize`
458    pub fn register_typed_with_state_only<S, R, F, Fut>(&mut self, name: &str, handler: F)
459    where
460        S: Send + Sync + 'static,
461        R: Serialize + Send + 'static,
462        F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
463        Fut: Future<Output = R> + Send + 'static,
464    {
465        let state = self.states.clone();
466        let wrapped = move |s: State<Arc<S>>| {
467            let fut = handler(s);
468            async move { Json(fut.await) }
469        };
470        self.handlers
471            .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(wrapped, state)));
472    }
473
474    // ─── Result return registration ─────────────────────────────────────
475
476    /// Register a handler returning `Result<R, E>` (no args)
477    ///
478    /// On `Ok(value)`, `value` is serialized to JSON. On `Err(e)`, the error
479    /// is returned as a string.
480    pub fn register_result<R, E, F, Fut>(&mut self, name: &str, handler: F)
481    where
482        R: Serialize + Send + 'static,
483        E: std::fmt::Display + Send + 'static,
484        F: Fn() -> Fut + Send + Sync + 'static,
485        Fut: Future<Output = Result<R, E>> + Send + 'static,
486    {
487        self.handlers
488            .insert(name.to_string(), Box::new(HandlerFn::new(handler)));
489    }
490
491    /// Register a handler returning `Result<R, E>` with typed args
492    pub fn register_result_with_args<T, R, E, F, Fut>(&mut self, name: &str, handler: F)
493    where
494        T: DeserializeOwned + Send + 'static,
495        R: Serialize + Send + 'static,
496        E: std::fmt::Display + Send + 'static,
497        F: Fn(T) -> Fut + Send + Sync + 'static,
498        Fut: Future<Output = Result<R, E>> + Send + 'static,
499    {
500        self.handlers
501            .insert(name.to_string(), Box::new(HandlerWithArgs::new(handler)));
502    }
503
504    /// Register a handler returning `Result<R, E>` with state + typed args
505    pub fn register_result_with_state<S, T, R, E, F, Fut>(&mut self, name: &str, handler: F)
506    where
507        S: Send + Sync + 'static,
508        T: DeserializeOwned + Send + 'static,
509        R: Serialize + Send + 'static,
510        E: std::fmt::Display + Send + 'static,
511        F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
512        Fut: Future<Output = Result<R, E>> + Send + 'static,
513    {
514        let state = self.states.clone();
515        self.handlers
516            .insert(name.to_string(), Box::new(HandlerWithState::new(handler, state)));
517    }
518
519    /// Register a handler returning `Result<R, E>` with state only
520    pub fn register_result_with_state_only<S, R, E, F, Fut>(&mut self, name: &str, handler: F)
521    where
522        S: Send + Sync + 'static,
523        R: Serialize + Send + 'static,
524        E: std::fmt::Display + Send + 'static,
525        F: Fn(State<Arc<S>>) -> Fut + Send + Sync + 'static,
526        Fut: Future<Output = Result<R, E>> + Send + 'static,
527    {
528        let state = self.states.clone();
529        self.handlers
530            .insert(name.to_string(), Box::new(HandlerWithStateOnly::new(handler, state)));
531    }
532
533    /// Get the number of registered handlers (request/response only)
534    pub fn handlers_count(&self) -> usize {
535        self.handlers.len()
536    }
537
538    // ─── Streaming handler registration ─────────────────────────────────
539
540    /// Register a streaming handler (no args, receives StreamSender)
541    pub fn register_streaming<F, Fut, R>(&mut self, name: &str, handler: F)
542    where
543        F: Fn(StreamSender) -> Fut + Send + Sync + 'static,
544        Fut: Future<Output = R> + Send + 'static,
545        R: IntoHandlerResult + 'static,
546    {
547        self.streaming_handlers
548            .insert(name.to_string(), Box::new(StreamingHandlerFn::new(handler)));
549    }
550
551    /// Register a streaming handler with typed args
552    pub fn register_streaming_with_args<T, F, Fut, R>(&mut self, name: &str, handler: F)
553    where
554        T: DeserializeOwned + Send + 'static,
555        F: Fn(T, StreamSender) -> Fut + Send + Sync + 'static,
556        Fut: Future<Output = R> + Send + 'static,
557        R: IntoHandlerResult + 'static,
558    {
559        self.streaming_handlers
560            .insert(name.to_string(), Box::new(StreamingHandlerWithArgs::new(handler)));
561    }
562
563    /// Register a streaming handler with state and typed args
564    pub fn register_streaming_with_state<S, T, F, Fut, R>(&mut self, name: &str, handler: F)
565    where
566        S: Send + Sync + 'static,
567        T: DeserializeOwned + Send + 'static,
568        F: Fn(State<Arc<S>>, T, StreamSender) -> Fut + Send + Sync + 'static,
569        Fut: Future<Output = R> + Send + 'static,
570        R: IntoHandlerResult + 'static,
571    {
572        let state = self.states.clone();
573        self.streaming_handlers
574            .insert(name.to_string(), Box::new(StreamingHandlerWithState::new(handler, state)));
575    }
576
577    /// Register a streaming handler with state only (no args)
578    pub fn register_streaming_with_state_only<S, F, Fut, R>(&mut self, name: &str, handler: F)
579    where
580        S: Send + Sync + 'static,
581        F: Fn(State<Arc<S>>, StreamSender) -> Fut + Send + Sync + 'static,
582        Fut: Future<Output = R> + Send + 'static,
583        R: IntoHandlerResult + 'static,
584    {
585        let state = self.states.clone();
586        self.streaming_handlers
587            .insert(name.to_string(), Box::new(StreamingHandlerWithStateOnly::new(handler, state)));
588    }
589
590    /// Register a handler that returns a `Stream` of items (no args).
591    ///
592    /// The stream is internally bridged to a `StreamSender` channel.
593    /// Items are forwarded through a bounded channel (default capacity 64).
594    /// Register a handler that returns a `Stream` of items (no args).
595    ///
596    /// The stream is internally bridged to a `StreamSender` channel.
597    /// Items are forwarded through a bounded channel (default capacity 64).
598    pub fn register_stream<T, St, F, Fut>(&mut self, name: &str, handler: F)
599    where
600        T: IntoStreamItem + 'static,
601        St: Stream<Item = T> + Send + 'static,
602        F: Fn() -> Fut + Send + Sync + 'static,
603        Fut: Future<Output = St> + Send + 'static,
604    {
605        self.register_streaming(name, move |tx: StreamSender| {
606            let stream_fut = handler();
607            async move {
608                drive_stream(stream_fut.await, &tx).await
609            }
610        });
611    }
612
613    /// Register a handler that takes typed args and returns a `Stream` of items.
614    pub fn register_stream_with_args<T, Item, St, F, Fut>(&mut self, name: &str, handler: F)
615    where
616        T: DeserializeOwned + Send + 'static,
617        Item: IntoStreamItem + 'static,
618        St: Stream<Item = Item> + Send + 'static,
619        F: Fn(T) -> Fut + Send + Sync + 'static,
620        Fut: Future<Output = St> + Send + 'static,
621    {
622        self.register_streaming_with_args::<T, _, _, _>(name, move |args: T, tx: StreamSender| {
623            let stream_fut = handler(args);
624            async move {
625                drive_stream(stream_fut.await, &tx).await
626            }
627        });
628    }
629
630    /// Register a handler that takes state + typed args and returns a `Stream` of items.
631    pub fn register_stream_with_state<S, T, Item, St, F, Fut>(&mut self, name: &str, handler: F)
632    where
633        S: Send + Sync + 'static,
634        T: DeserializeOwned + Send + 'static,
635        Item: IntoStreamItem + 'static,
636        St: Stream<Item = Item> + Send + 'static,
637        F: Fn(State<Arc<S>>, T) -> Fut + Send + Sync + 'static,
638        Fut: Future<Output = St> + Send + 'static,
639    {
640        self.register_streaming_with_state::<S, T, _, _, _>(name, move |state: State<Arc<S>>, args: T, tx: StreamSender| {
641            let stream_fut = handler(state, args);
642            async move {
643                drive_stream(stream_fut.await, &tx).await
644            }
645        });
646    }
647
648    /// Check if a handler is a streaming handler
649    pub fn is_streaming(&self, name: &str) -> bool {
650        self.streaming_handlers.contains_key(name)
651    }
652
653    /// Call a streaming handler by name.
654    ///
655    /// Creates a bounded channel, passes the sender to the handler,
656    /// and returns `(receiver, completion_future)`. The completion future
657    /// resolves with the handler's final result.
658    ///
659    /// Note: the returned future borrows `self`. For use in contexts where
660    /// a `'static` future is needed (e.g., `tokio::spawn`), use
661    /// `spawn_streaming_handler` instead.
662    #[allow(clippy::type_complexity)]
663    pub fn call_streaming_handler(
664        &self,
665        name: &str,
666        args: &str,
667    ) -> Result<
668        (
669            StreamReceiver,
670            Pin<Box<dyn Future<Output = Result<String, String>> + Send + '_>>,
671        ),
672        String,
673    > {
674        let handler = self
675            .streaming_handlers
676            .get(name)
677            .ok_or_else(|| format!("Streaming handler '{}' not found", name))?;
678
679        let transformed = self.maybe_transform_args(args);
680        let args = transformed.as_deref().unwrap_or(args);
681
682        let (tx, rx) = StreamSender::channel();
683        let fut = handler.call_streaming(args, tx);
684        Ok((rx, fut))
685    }
686
687    /// Spawn a streaming handler as a tokio task.
688    ///
689    /// Like `call_streaming_handler` but returns a `'static` receiver and
690    /// `JoinHandle`, suitable for use with `tokio::spawn`.
691    #[allow(clippy::type_complexity)]
692    pub fn spawn_streaming_handler(
693        self: &Arc<Self>,
694        name: &str,
695        args: &str,
696    ) -> Result<
697        (
698            StreamReceiver,
699            tokio::task::JoinHandle<Result<String, String>>,
700        ),
701        String,
702    > {
703        if !self.streaming_handlers.contains_key(name) {
704            return Err(format!("Streaming handler '{}' not found", name));
705        }
706
707        let router = self.clone();
708        let name = name.to_string();
709        let args = match self.maybe_transform_args(args) {
710            Some(t) => t,
711            None => args.to_string(),
712        };
713
714        let (tx, rx) = StreamSender::channel();
715
716        let handle = tokio::spawn(async move {
717            let handler = router
718                .streaming_handlers
719                .get(&name)
720                .expect("handler verified to exist");
721            handler.call_streaming(&args, tx).await
722        });
723
724        Ok((rx, handle))
725    }
726
727    // ─── TypeScript codegen metadata ────────────────────────────────────
728
729    /// Attach type metadata to a handler for TypeScript client generation
730    ///
731    /// The metadata describes the handler's argument fields and return type,
732    /// which `generate_ts_client()` uses to generate typed async functions.
733    ///
734    /// # Example
735    ///
736    /// ```rust
737    /// use allframe_core::router::{Router, TsField, TsType};
738    ///
739    /// let mut router = Router::new();
740    /// router.register("get_user", || async { r#"{"id":1}"#.to_string() });
741    /// router.describe_handler("get_user", vec![], TsType::Object(vec![
742    ///     TsField::new("id", TsType::Number),
743    ///     TsField::new("name", TsType::String),
744    /// ]));
745    /// ```
746    pub fn describe_handler(
747        &mut self,
748        name: &str,
749        args: Vec<TsField>,
750        returns: TsType,
751    ) {
752        assert!(
753            self.handlers.contains_key(name),
754            "describe_handler: handler '{}' not registered",
755            name
756        );
757        self.handler_metas
758            .insert(name.to_string(), HandlerMeta::new(args, returns));
759    }
760
761    /// Attach type metadata to a streaming handler for TypeScript client generation
762    pub fn describe_streaming_handler(
763        &mut self,
764        name: &str,
765        args: Vec<TsField>,
766        item_type: TsType,
767        final_type: TsType,
768    ) {
769        assert!(
770            self.streaming_handlers.contains_key(name),
771            "describe_streaming_handler: streaming handler '{}' not registered",
772            name
773        );
774        self.handler_metas
775            .insert(name.to_string(), HandlerMeta::streaming(args, item_type, final_type));
776    }
777
778    /// Generate a complete TypeScript client module from all described handlers
779    ///
780    /// Returns a string containing TypeScript code with typed async functions
781    /// for each handler that has metadata attached via `describe_handler()`.
782    ///
783    /// # Example
784    ///
785    /// ```rust
786    /// use allframe_core::router::{Router, TsField, TsType};
787    ///
788    /// let mut router = Router::new();
789    /// router.register("get_user", || async { r#"{"id":1}"#.to_string() });
790    /// router.describe_handler("get_user", vec![
791    ///     TsField::new("id", TsType::Number),
792    /// ], TsType::Object(vec![
793    ///     TsField::new("id", TsType::Number),
794    ///     TsField::new("name", TsType::String),
795    /// ]));
796    ///
797    /// let ts_code = router.generate_ts_client();
798    /// assert!(ts_code.contains("export async function getUser"));
799    /// ```
800    pub fn generate_ts_client(&self) -> String {
801        generate_ts_client(&self.handler_metas)
802    }
803
804    /// Get handler metadata (for inspection/testing)
805    pub fn handler_meta(&self, name: &str) -> Option<&HandlerMeta> {
806        self.handler_metas.get(name)
807    }
808
809    /// Add a protocol adapter
810    pub fn add_adapter(&mut self, adapter: Box<dyn ProtocolAdapter>) {
811        self.adapters.insert(adapter.name().to_string(), adapter);
812    }
813
814    /// Check if an adapter is registered
815    pub fn has_adapter(&self, name: &str) -> bool {
816        self.adapters.contains_key(name)
817    }
818
819    /// Get an adapter by name
820    pub fn get_adapter(&self, name: &str) -> Option<&dyn ProtocolAdapter> {
821        self.adapters.get(name).map(|b| &**b)
822    }
823
824    /// Route a request through the appropriate protocol adapter
825    pub async fn route_request(&self, protocol: &str, request: &str) -> Result<String, String> {
826        let adapter = self
827            .get_adapter(protocol)
828            .ok_or_else(|| format!("Adapter not found: {}", protocol))?;
829
830        adapter.handle(request).await
831    }
832
833    /// Execute a handler by name (zero-arg shorthand)
834    pub async fn execute(&self, name: &str) -> Result<String, String> {
835        self.execute_with_args(name, "{}").await
836    }
837
838    /// Apply the configured key transform to args, if any.
839    fn maybe_transform_args(&self, args: &str) -> Option<String> {
840        self.key_transform.map(|t| apply_key_transform(args, t))
841    }
842
843    /// Execute a handler by name with JSON args
844    pub async fn execute_with_args(&self, name: &str, args: &str) -> Result<String, String> {
845        let transformed;
846        let args = match self.maybe_transform_args(args) {
847            Some(t) => {
848                transformed = t;
849                &transformed
850            }
851            None => args,
852        };
853        match self.handlers.get(name) {
854            Some(handler) => handler.call(args).await,
855            None => Err(format!("Handler '{}' not found", name)),
856        }
857    }
858
859    /// List all registered handler names (both request/response and streaming)
860    ///
861    /// Returns a vector of all handler names that have been registered
862    /// with this router. Used by MCP server for tool discovery.
863    pub fn list_handlers(&self) -> Vec<String> {
864        let mut names: Vec<String> = self.handlers.keys().cloned().collect();
865        names.extend(self.streaming_handlers.keys().cloned());
866        names
867    }
868
869    /// Call a handler by name with request data (JSON args)
870    ///
871    /// Forwards the request string as args to the handler.
872    /// Used by MCP server and Tauri plugin.
873    pub async fn call_handler(&self, name: &str, request: &str) -> Result<String, String> {
874        self.execute_with_args(name, request).await
875    }
876
877    /// Check if handler can be called via REST
878    pub fn can_handle_rest(&self, _name: &str) -> bool {
879        self.has_adapter("rest")
880    }
881
882    /// Check if handler can be called via GraphQL
883    pub fn can_handle_graphql(&self, _name: &str) -> bool {
884        self.has_adapter("graphql")
885    }
886
887    /// Check if handler can be called via gRPC
888    pub fn can_handle_grpc(&self, _name: &str) -> bool {
889        self.has_adapter("grpc")
890    }
891
892    /// Get list of enabled protocols
893    pub fn enabled_protocols(&self) -> Vec<String> {
894        self.adapters.keys().cloned().collect()
895    }
896
897    /// Add a route with metadata
898    ///
899    /// This stores route metadata that can be used to generate
900    /// documentation (OpenAPI, GraphQL schemas, gRPC reflection).
901    pub fn add_route(&mut self, metadata: RouteMetadata) {
902        self.routes.push(metadata);
903    }
904
905    /// Get all registered routes
906    ///
907    /// Returns an immutable reference to all route metadata.
908    /// This is used for documentation generation.
909    pub fn routes(&self) -> &[RouteMetadata] {
910        &self.routes
911    }
912
913    /// Register a GET route
914    ///
915    /// This is a convenience method that registers both a handler and route
916    /// metadata for a GET request. The handler name is automatically
917    /// generated as "GET:{path}".
918    pub fn get<F, Fut>(&mut self, path: &str, handler: F)
919    where
920        F: Fn() -> Fut + Send + Sync + 'static,
921        Fut: Future<Output = String> + Send + 'static,
922    {
923        let handler_name = format!("GET:{}", path);
924        self.register(&handler_name, handler);
925        self.add_route(RouteMetadata::new(path, Method::GET, "rest"));
926    }
927
928    /// Register a POST route
929    ///
930    /// This is a convenience method that registers both a handler and route
931    /// metadata for a POST request. The handler name is automatically
932    /// generated as "POST:{path}".
933    pub fn post<F, Fut>(&mut self, path: &str, handler: F)
934    where
935        F: Fn() -> Fut + Send + Sync + 'static,
936        Fut: Future<Output = String> + Send + 'static,
937    {
938        let handler_name = format!("POST:{}", path);
939        self.register(&handler_name, handler);
940        self.add_route(RouteMetadata::new(path, Method::POST, "rest"));
941    }
942
943    /// Register a PUT route
944    ///
945    /// This is a convenience method that registers both a handler and route
946    /// metadata for a PUT request. The handler name is automatically
947    /// generated as "PUT:{path}".
948    pub fn put<F, Fut>(&mut self, path: &str, handler: F)
949    where
950        F: Fn() -> Fut + Send + Sync + 'static,
951        Fut: Future<Output = String> + Send + 'static,
952    {
953        let handler_name = format!("PUT:{}", path);
954        self.register(&handler_name, handler);
955        self.add_route(RouteMetadata::new(path, Method::PUT, "rest"));
956    }
957
958    /// Register a DELETE route
959    ///
960    /// This is a convenience method that registers both a handler and route
961    /// metadata for a DELETE request. The handler name is automatically
962    /// generated as "DELETE:{path}".
963    pub fn delete<F, Fut>(&mut self, path: &str, handler: F)
964    where
965        F: Fn() -> Fut + Send + Sync + 'static,
966        Fut: Future<Output = String> + Send + 'static,
967    {
968        let handler_name = format!("DELETE:{}", path);
969        self.register(&handler_name, handler);
970        self.add_route(RouteMetadata::new(path, Method::DELETE, "rest"));
971    }
972
973    /// Register a PATCH route
974    ///
975    /// This is a convenience method that registers both a handler and route
976    /// metadata for a PATCH request. The handler name is automatically
977    /// generated as "PATCH:{path}".
978    pub fn patch<F, Fut>(&mut self, path: &str, handler: F)
979    where
980        F: Fn() -> Fut + Send + Sync + 'static,
981        Fut: Future<Output = String> + Send + 'static,
982    {
983        let handler_name = format!("PATCH:{}", path);
984        self.register(&handler_name, handler);
985        self.add_route(RouteMetadata::new(path, Method::PATCH, "rest"));
986    }
987
988    /// Register a HEAD route
989    ///
990    /// This is a convenience method that registers both a handler and route
991    /// metadata for a HEAD request. The handler name is automatically
992    /// generated as "HEAD:{path}".
993    pub fn head<F, Fut>(&mut self, path: &str, handler: F)
994    where
995        F: Fn() -> Fut + Send + Sync + 'static,
996        Fut: Future<Output = String> + Send + 'static,
997    {
998        let handler_name = format!("HEAD:{}", path);
999        self.register(&handler_name, handler);
1000        self.add_route(RouteMetadata::new(path, Method::HEAD, "rest"));
1001    }
1002
1003    /// Register an OPTIONS route
1004    ///
1005    /// This is a convenience method that registers both a handler and route
1006    /// metadata for an OPTIONS request. The handler name is automatically
1007    /// generated as "OPTIONS:{path}".
1008    pub fn options<F, Fut>(&mut self, path: &str, handler: F)
1009    where
1010        F: Fn() -> Fut + Send + Sync + 'static,
1011        Fut: Future<Output = String> + Send + 'static,
1012    {
1013        let handler_name = format!("OPTIONS:{}", path);
1014        self.register(&handler_name, handler);
1015        self.add_route(RouteMetadata::new(path, Method::OPTIONS, "rest"));
1016    }
1017
1018    /// Call handler via REST
1019    pub async fn call_rest(&self, method: &str, path: &str) -> Result<String, String> {
1020        let adapter = self
1021            .adapters
1022            .get("rest")
1023            .ok_or_else(|| "REST adapter not enabled".to_string())?;
1024
1025        let request = format!("{} {}", method, path);
1026        adapter.handle(&request).await
1027    }
1028
1029    /// Call handler via GraphQL
1030    pub async fn call_graphql(&self, query: &str) -> Result<String, String> {
1031        let adapter = self
1032            .adapters
1033            .get("graphql")
1034            .ok_or_else(|| "GraphQL adapter not enabled".to_string())?;
1035
1036        adapter.handle(query).await
1037    }
1038
1039    /// Call handler via gRPC
1040    pub async fn call_grpc(&self, method: &str, request: &str) -> Result<String, String> {
1041        let adapter = self
1042            .adapters
1043            .get("grpc")
1044            .ok_or_else(|| "gRPC adapter not enabled".to_string())?;
1045
1046        let grpc_request = format!("{}:{}", method, request);
1047        adapter.handle(&grpc_request).await
1048    }
1049
1050    /// Generate Scalar documentation HTML with default configuration
1051    ///
1052    /// This is a convenience method that generates a complete HTML page
1053    /// with Scalar UI for interactive API documentation.
1054    ///
1055    /// # Arguments
1056    ///
1057    /// * `title` - API title
1058    /// * `version` - API version
1059    ///
1060    /// # Example
1061    ///
1062    /// ```rust
1063    /// use allframe_core::router::Router;
1064    ///
1065    /// let mut router = Router::new();
1066    /// router.get("/users", || async { "Users".to_string() });
1067    ///
1068    /// let html = router.scalar("My API", "1.0.0");
1069    /// // Serve this HTML at /docs endpoint
1070    /// ```
1071    pub fn scalar(&self, title: &str, version: &str) -> String {
1072        let config = scalar::ScalarConfig::default();
1073        self.scalar_docs(config, title, version)
1074    }
1075
1076    /// Generate Scalar documentation HTML with custom configuration
1077    ///
1078    /// This method allows full customization of the Scalar UI appearance
1079    /// and behavior.
1080    ///
1081    /// # Arguments
1082    ///
1083    /// * `config` - Scalar configuration
1084    /// * `title` - API title
1085    /// * `version` - API version
1086    ///
1087    /// # Example
1088    ///
1089    /// ```rust
1090    /// use allframe_core::router::{Router, ScalarConfig, ScalarTheme};
1091    ///
1092    /// let mut router = Router::new();
1093    /// router.get("/users", || async { "Users".to_string() });
1094    ///
1095    /// let config = ScalarConfig::new()
1096    ///     .theme(ScalarTheme::Light)
1097    ///     .show_sidebar(false);
1098    ///
1099    /// let html = router.scalar_docs(config, "My API", "1.0.0");
1100    /// ```
1101    pub fn scalar_docs(&self, config: scalar::ScalarConfig, title: &str, version: &str) -> String {
1102        // Generate OpenAPI spec
1103        let spec = OpenApiGenerator::new(title, version).generate(self);
1104        let spec_json = serde_json::to_string(&spec).unwrap_or_else(|_| "{}".to_string());
1105
1106        // Generate Scalar HTML
1107        scalar::scalar_html(&config, title, &spec_json)
1108    }
1109}
1110
1111impl Default for Router {
1112    fn default() -> Self {
1113        Self::new()
1114    }
1115}
1116
1117/// Bulk-register handler functions with a Router.
1118///
1119/// Each function is referenced by path in the expansion, so the compiler
1120/// sees it as used — eliminating false `dead_code` warnings that occur when
1121/// handler functions are only referenced through closure-based registration.
1122///
1123/// # Syntax
1124///
1125/// ```rust,ignore
1126/// use allframe_core::register_handlers;
1127///
1128/// register_handlers!(router, [
1129///     // Basic handler (zero-arg, returns String)
1130///     "health" => health_check,
1131///
1132///     // Handler with typed, deserializable args
1133///     args "greet" => greet,
1134///
1135///     // Streaming handler (receives StreamSender)
1136///     streaming "feed" => live_feed,
1137///
1138///     // Streaming handler with typed args
1139///     streaming args "search" => search_stream,
1140///
1141///     // State-only handler (receives State<Arc<S>>)
1142///     state "get_status" => get_ollama_status,
1143///
1144///     // State with typed args
1145///     state args "save_key" => save_api_key,
1146///
1147///     // State streaming (receives State<Arc<S>> + StreamSender)
1148///     state streaming "stream_chat" => stream_chat,
1149///
1150///     // State streaming with typed args
1151///     state streaming args "start_job" => start_orchestrator,
1152/// ]);
1153/// ```
1154///
1155/// # Expands to
1156///
1157/// ```rust,ignore
1158/// router.register("health", health_check);
1159/// router.register_with_args("greet", greet);
1160/// router.register_streaming("feed", live_feed);
1161/// router.register_streaming_with_args("search", search_stream);
1162/// router.register_with_state_only("get_status", get_ollama_status);
1163/// router.register_with_state("save_key", save_api_key);
1164/// router.register_streaming_with_state_only("stream_chat", stream_chat);
1165/// router.register_streaming_with_state("start_job", start_orchestrator);
1166/// ```
1167///
1168/// # Why this helps
1169///
1170/// When handler functions are defined in separate modules and only called via
1171/// `router.register("name", || async { handler().await })`, the compiler
1172/// cannot always trace the usage chain and reports `dead_code` warnings.
1173/// This macro passes each function directly as a function item, making the
1174/// reference visible to `rustc`'s dead-code analysis.
1175#[macro_export]
1176macro_rules! register_handlers {
1177    ($router:expr, [ $($entry:tt)* ]) => {
1178        $crate::register_handlers!(@entries $router, $($entry)*)
1179    };
1180
1181    // Terminal case
1182    (@entries $router:expr, ) => {};
1183
1184    // Basic: "name" => handler,
1185    (@entries $router:expr, $name:literal => $handler:path, $($rest:tt)*) => {
1186        $router.register($name, $handler);
1187        $crate::register_handlers!(@entries $router, $($rest)*)
1188    };
1189
1190    // With args: args "name" => handler,
1191    (@entries $router:expr, args $name:literal => $handler:path, $($rest:tt)*) => {
1192        $router.register_with_args($name, $handler);
1193        $crate::register_handlers!(@entries $router, $($rest)*)
1194    };
1195
1196    // Streaming: streaming "name" => handler,
1197    (@entries $router:expr, streaming $name:literal => $handler:path, $($rest:tt)*) => {
1198        $router.register_streaming($name, $handler);
1199        $crate::register_handlers!(@entries $router, $($rest)*)
1200    };
1201
1202    // Streaming with args: streaming args "name" => handler,
1203    (@entries $router:expr, streaming args $name:literal => $handler:path, $($rest:tt)*) => {
1204        $router.register_streaming_with_args($name, $handler);
1205        $crate::register_handlers!(@entries $router, $($rest)*)
1206    };
1207
1208    // State only: state "name" => handler,
1209    (@entries $router:expr, state $name:literal => $handler:path, $($rest:tt)*) => {
1210        $router.register_with_state_only($name, $handler);
1211        $crate::register_handlers!(@entries $router, $($rest)*)
1212    };
1213
1214    // State with args: state args "name" => handler,
1215    (@entries $router:expr, state args $name:literal => $handler:path, $($rest:tt)*) => {
1216        $router.register_with_state($name, $handler);
1217        $crate::register_handlers!(@entries $router, $($rest)*)
1218    };
1219
1220    // State streaming: state streaming "name" => handler,
1221    (@entries $router:expr, state streaming $name:literal => $handler:path, $($rest:tt)*) => {
1222        $router.register_streaming_with_state_only($name, $handler);
1223        $crate::register_handlers!(@entries $router, $($rest)*)
1224    };
1225
1226    // State streaming with args: state streaming args "name" => handler,
1227    (@entries $router:expr, state streaming args $name:literal => $handler:path, $($rest:tt)*) => {
1228        $router.register_streaming_with_state($name, $handler);
1229        $crate::register_handlers!(@entries $router, $($rest)*)
1230    };
1231}
1232
1233#[cfg(test)]
1234mod tests {
1235    use super::*;
1236
1237    #[tokio::test]
1238    async fn test_router_creation() {
1239        let router = Router::new();
1240        assert_eq!(router.handlers_count(), 0);
1241    }
1242
1243    #[tokio::test]
1244    async fn test_handler_registration() {
1245        let mut router = Router::new();
1246        router.register("test", || async { "Hello".to_string() });
1247        assert_eq!(router.handlers_count(), 1);
1248    }
1249
1250    #[tokio::test]
1251    async fn test_handler_execution() {
1252        let mut router = Router::new();
1253        router.register("test", || async { "Hello".to_string() });
1254        let result = router.execute("test").await;
1255        assert_eq!(result, Ok("Hello".to_string()));
1256    }
1257
1258    // New tests for route metadata tracking
1259    #[tokio::test]
1260    async fn test_router_starts_with_no_routes() {
1261        let router = Router::new();
1262        let routes = router.routes();
1263        assert_eq!(routes.len(), 0);
1264    }
1265
1266    #[tokio::test]
1267    async fn test_add_route_metadata() {
1268        let mut router = Router::new();
1269        let metadata = RouteMetadata::new("/users", "GET", "rest");
1270
1271        router.add_route(metadata.clone());
1272
1273        let routes = router.routes();
1274        assert_eq!(routes.len(), 1);
1275        assert_eq!(routes[0].path, "/users");
1276        assert_eq!(routes[0].method, "GET");
1277        assert_eq!(routes[0].protocol, "rest");
1278    }
1279
1280    #[tokio::test]
1281    async fn test_add_multiple_routes() {
1282        let mut router = Router::new();
1283
1284        router.add_route(RouteMetadata::new("/users", "GET", "rest"));
1285        router.add_route(RouteMetadata::new("/users", "POST", "rest"));
1286        router.add_route(RouteMetadata::new("/posts", "GET", "rest"));
1287
1288        let routes = router.routes();
1289        assert_eq!(routes.len(), 3);
1290    }
1291
1292    #[tokio::test]
1293    async fn test_routes_with_different_protocols() {
1294        let mut router = Router::new();
1295
1296        router.add_route(RouteMetadata::new("/users", "GET", "rest"));
1297        router.add_route(RouteMetadata::new("users", "query", "graphql"));
1298        router.add_route(RouteMetadata::new("UserService.GetUser", "unary", "grpc"));
1299
1300        let routes = router.routes();
1301        assert_eq!(routes.len(), 3);
1302
1303        assert_eq!(routes[0].protocol, "rest");
1304        assert_eq!(routes[1].protocol, "graphql");
1305        assert_eq!(routes[2].protocol, "grpc");
1306    }
1307
1308    #[tokio::test]
1309    async fn test_routes_returns_immutable_reference() {
1310        let mut router = Router::new();
1311        router.add_route(RouteMetadata::new("/test", "GET", "rest"));
1312
1313        let routes1 = router.routes();
1314        let routes2 = router.routes();
1315
1316        // Both should have the same data
1317        assert_eq!(routes1.len(), routes2.len());
1318        assert_eq!(routes1[0].path, routes2[0].path);
1319    }
1320
1321    // Tests for type-safe route registration
1322    #[tokio::test]
1323    async fn test_route_get_method() {
1324        let mut router = Router::new();
1325        router.get("/users", || async { "User list".to_string() });
1326
1327        let routes = router.routes();
1328        assert_eq!(routes.len(), 1);
1329        assert_eq!(routes[0].path, "/users");
1330        assert_eq!(routes[0].method, "GET");
1331        assert_eq!(routes[0].protocol, "rest");
1332    }
1333
1334    #[tokio::test]
1335    async fn test_route_post_method() {
1336        let mut router = Router::new();
1337        router.post("/users", || async { "User created".to_string() });
1338
1339        let routes = router.routes();
1340        assert_eq!(routes.len(), 1);
1341        assert_eq!(routes[0].path, "/users");
1342        assert_eq!(routes[0].method, "POST");
1343        assert_eq!(routes[0].protocol, "rest");
1344    }
1345
1346    #[tokio::test]
1347    async fn test_route_put_method() {
1348        let mut router = Router::new();
1349        router.put("/users/1", || async { "User updated".to_string() });
1350
1351        let routes = router.routes();
1352        assert_eq!(routes.len(), 1);
1353        assert_eq!(routes[0].method, "PUT");
1354    }
1355
1356    #[tokio::test]
1357    async fn test_route_delete_method() {
1358        let mut router = Router::new();
1359        router.delete("/users/1", || async { "User deleted".to_string() });
1360
1361        let routes = router.routes();
1362        assert_eq!(routes.len(), 1);
1363        assert_eq!(routes[0].method, "DELETE");
1364    }
1365
1366    #[tokio::test]
1367    async fn test_route_patch_method() {
1368        let mut router = Router::new();
1369        router.patch("/users/1", || async { "User patched".to_string() });
1370
1371        let routes = router.routes();
1372        assert_eq!(routes.len(), 1);
1373        assert_eq!(routes[0].method, "PATCH");
1374    }
1375
1376    #[tokio::test]
1377    async fn test_multiple_routes_different_methods() {
1378        let mut router = Router::new();
1379        router.get("/users", || async { "List".to_string() });
1380        router.post("/users", || async { "Create".to_string() });
1381        router.put("/users/1", || async { "Update".to_string() });
1382        router.delete("/users/1", || async { "Delete".to_string() });
1383
1384        let routes = router.routes();
1385        assert_eq!(routes.len(), 4);
1386
1387        assert_eq!(routes[0].method, "GET");
1388        assert_eq!(routes[1].method, "POST");
1389        assert_eq!(routes[2].method, "PUT");
1390        assert_eq!(routes[3].method, "DELETE");
1391    }
1392
1393    #[tokio::test]
1394    async fn test_route_method_with_path_params() {
1395        let mut router = Router::new();
1396        router.get("/users/{id}", || async { "User details".to_string() });
1397        router.get("/users/{id}/posts/{post_id}", || async {
1398            "Post details".to_string()
1399        });
1400
1401        let routes = router.routes();
1402        assert_eq!(routes.len(), 2);
1403        assert_eq!(routes[0].path, "/users/{id}");
1404        assert_eq!(routes[1].path, "/users/{id}/posts/{post_id}");
1405    }
1406
1407    #[tokio::test]
1408    async fn test_route_registration_and_execution() {
1409        let mut router = Router::new();
1410        router.get("/test", || async { "GET response".to_string() });
1411        router.post("/test", || async { "POST response".to_string() });
1412
1413        // Both route metadata and handler should be registered
1414        assert_eq!(router.routes().len(), 2);
1415        assert_eq!(router.handlers_count(), 2);
1416
1417        // Handlers should be executable
1418        let result1 = router.execute("GET:/test").await;
1419        let result2 = router.execute("POST:/test").await;
1420
1421        assert_eq!(result1, Ok("GET response".to_string()));
1422        assert_eq!(result2, Ok("POST response".to_string()));
1423    }
1424
1425    // Tests for Scalar integration
1426    #[tokio::test]
1427    async fn test_scalar_generates_html() {
1428        let mut router = Router::new();
1429        router.get("/users", || async { "Users".to_string() });
1430
1431        let html = router.scalar("Test API", "1.0.0");
1432
1433        assert!(html.contains("<!DOCTYPE html>"));
1434        assert!(html.contains("<title>Test API - API Documentation</title>"));
1435        assert!(html.contains("@scalar/api-reference"));
1436    }
1437
1438    #[tokio::test]
1439    async fn test_scalar_contains_openapi_spec() {
1440        let mut router = Router::new();
1441        router.get("/users", || async { "Users".to_string() });
1442        router.post("/users", || async { "User created".to_string() });
1443
1444        let html = router.scalar("Test API", "1.0.0");
1445
1446        // Should contain the OpenAPI spec
1447        assert!(html.contains("openapi"));
1448        assert!(html.contains("Test API"));
1449        assert!(html.contains("1.0.0"));
1450    }
1451
1452    #[tokio::test]
1453    async fn test_scalar_docs_with_custom_config() {
1454        let mut router = Router::new();
1455        router.get("/users", || async { "Users".to_string() });
1456
1457        let config = scalar::ScalarConfig::new()
1458            .theme(scalar::ScalarTheme::Light)
1459            .show_sidebar(false);
1460
1461        let html = router.scalar_docs(config, "Custom API", "2.0.0");
1462
1463        assert!(html.contains("Custom API"));
1464        assert!(html.contains(r#""theme":"light""#));
1465        assert!(html.contains(r#""showSidebar":false"#));
1466    }
1467
1468    #[tokio::test]
1469    async fn test_scalar_docs_with_custom_css() {
1470        let mut router = Router::new();
1471        router.get("/test", || async { "Test".to_string() });
1472
1473        let config = scalar::ScalarConfig::new().custom_css("body { font-family: 'Inter'; }");
1474
1475        let html = router.scalar_docs(config, "API", "1.0");
1476
1477        assert!(html.contains("<style>body { font-family: 'Inter'; }</style>"));
1478    }
1479
1480    #[tokio::test]
1481    async fn test_scalar_with_multiple_routes() {
1482        let mut router = Router::new();
1483        router.get("/users", || async { "Users".to_string() });
1484        router.post("/users", || async { "Create".to_string() });
1485        router.get("/users/{id}", || async { "User details".to_string() });
1486        router.delete("/users/{id}", || async { "Delete".to_string() });
1487
1488        let html = router.scalar("API", "1.0.0");
1489
1490        // Should contain all routes in the OpenAPI spec
1491        assert!(html.contains("/users"));
1492    }
1493
1494    // Tests for protocol adapter management
1495    #[tokio::test]
1496    async fn test_get_adapter_returns_adapter() {
1497        let mut router = Router::new();
1498        router.add_adapter(Box::new(RestAdapter::new()));
1499
1500        let adapter = router.get_adapter("rest");
1501        assert!(adapter.is_some());
1502        assert_eq!(adapter.unwrap().name(), "rest");
1503    }
1504
1505    #[tokio::test]
1506    async fn test_get_adapter_returns_none_for_missing() {
1507        let router = Router::new();
1508        let adapter = router.get_adapter("rest");
1509        assert!(adapter.is_none());
1510    }
1511
1512    #[tokio::test]
1513    async fn test_route_request_success() {
1514        let mut router = Router::new();
1515        router.register("test_handler", || async { "Success!".to_string() });
1516
1517        // Register adapter with a route
1518        let mut rest_adapter = RestAdapter::new();
1519        rest_adapter.route("GET", "/test", "test_handler");
1520        router.add_adapter(Box::new(rest_adapter));
1521
1522        let result = router.route_request("rest", "GET /test").await;
1523        assert!(result.is_ok());
1524        let response = result.unwrap();
1525        assert!(response.contains("HTTP 200") || response.contains("test_handler"));
1526    }
1527
1528    #[tokio::test]
1529    async fn test_route_request_unknown_adapter() {
1530        let router = Router::new();
1531        let result = router.route_request("unknown", "test").await;
1532        assert!(result.is_err());
1533        assert!(result.unwrap_err().contains("Adapter not found"));
1534    }
1535
1536    #[tokio::test]
1537    async fn test_enabled_protocols_empty() {
1538        let router = Router::new();
1539        let protocols = router.enabled_protocols();
1540        assert_eq!(protocols.len(), 0);
1541    }
1542
1543    #[tokio::test]
1544    async fn test_enabled_protocols_multiple() {
1545        let mut router = Router::new();
1546        router.add_adapter(Box::new(RestAdapter::new()));
1547        router.add_adapter(Box::new(GraphQLAdapter::new()));
1548        router.add_adapter(Box::new(GrpcAdapter::new()));
1549
1550        let protocols = router.enabled_protocols();
1551        assert_eq!(protocols.len(), 3);
1552        assert!(protocols.contains(&"rest".to_string()));
1553        assert!(protocols.contains(&"graphql".to_string()));
1554        assert!(protocols.contains(&"grpc".to_string()));
1555    }
1556
1557    #[tokio::test]
1558    async fn test_can_handle_rest() {
1559        let mut router = Router::new();
1560        assert!(!router.can_handle_rest("test"));
1561
1562        router.add_adapter(Box::new(RestAdapter::new()));
1563        assert!(router.can_handle_rest("test"));
1564    }
1565
1566    #[tokio::test]
1567    async fn test_can_handle_graphql() {
1568        let mut router = Router::new();
1569        assert!(!router.can_handle_graphql("test"));
1570
1571        router.add_adapter(Box::new(GraphQLAdapter::new()));
1572        assert!(router.can_handle_graphql("test"));
1573    }
1574
1575    #[tokio::test]
1576    async fn test_can_handle_grpc() {
1577        let mut router = Router::new();
1578        assert!(!router.can_handle_grpc("test"));
1579
1580        router.add_adapter(Box::new(GrpcAdapter::new()));
1581        assert!(router.can_handle_grpc("test"));
1582    }
1583
1584    // ===== Integration Tests: Multi-Protocol Routing =====
1585
1586    #[tokio::test]
1587    async fn test_integration_single_handler_rest() {
1588        // Test: Single handler exposed via REST
1589        let mut router = Router::new();
1590        router.register("get_user", || async { "User data".to_string() });
1591
1592        // Configure REST adapter
1593        let mut rest = RestAdapter::new();
1594        rest.route("GET", "/users/:id", "get_user");
1595        router.add_adapter(Box::new(rest));
1596
1597        // Route REST request
1598        let response = router.route_request("rest", "GET /users/42").await;
1599        assert!(response.is_ok());
1600        assert!(response.unwrap().contains("get_user"));
1601    }
1602
1603    #[tokio::test]
1604    async fn test_integration_single_handler_graphql() {
1605        // Test: Single handler exposed via GraphQL
1606        let mut router = Router::new();
1607        router.register("get_user", || async { "User data".to_string() });
1608
1609        // Configure GraphQL adapter
1610        let mut graphql = GraphQLAdapter::new();
1611        graphql.query("user", "get_user");
1612        router.add_adapter(Box::new(graphql));
1613
1614        // Route GraphQL request
1615        let response = router.route_request("graphql", "query { user }").await;
1616        assert!(response.is_ok());
1617        assert!(response.unwrap().contains("get_user"));
1618    }
1619
1620    #[tokio::test]
1621    async fn test_integration_single_handler_grpc() {
1622        // Test: Single handler exposed via gRPC
1623        let mut router = Router::new();
1624        router.register("get_user", || async { "User data".to_string() });
1625
1626        // Configure gRPC adapter
1627        let mut grpc = GrpcAdapter::new();
1628        grpc.unary("UserService", "GetUser", "get_user");
1629        router.add_adapter(Box::new(grpc));
1630
1631        // Route gRPC request
1632        let response = router
1633            .route_request("grpc", "UserService.GetUser:{\"id\":42}")
1634            .await;
1635        assert!(response.is_ok());
1636        assert!(response.unwrap().contains("get_user"));
1637    }
1638
1639    #[tokio::test]
1640    async fn test_integration_single_handler_all_protocols() {
1641        // Test: Single handler exposed via ALL protocols
1642        let mut router = Router::new();
1643        router.register("get_user", || async { "User data".to_string() });
1644
1645        // Configure REST adapter
1646        let mut rest = RestAdapter::new();
1647        rest.route("GET", "/users/:id", "get_user");
1648        router.add_adapter(Box::new(rest));
1649
1650        // Configure GraphQL adapter
1651        let mut graphql = GraphQLAdapter::new();
1652        graphql.query("user", "get_user");
1653        router.add_adapter(Box::new(graphql));
1654
1655        // Configure gRPC adapter
1656        let mut grpc = GrpcAdapter::new();
1657        grpc.unary("UserService", "GetUser", "get_user");
1658        router.add_adapter(Box::new(grpc));
1659
1660        // Test REST
1661        let rest_response = router.route_request("rest", "GET /users/42").await;
1662        assert!(rest_response.is_ok());
1663        assert!(rest_response.unwrap().contains("get_user"));
1664
1665        // Test GraphQL
1666        let graphql_response = router.route_request("graphql", "query { user }").await;
1667        assert!(graphql_response.is_ok());
1668        assert!(graphql_response.unwrap().contains("get_user"));
1669
1670        // Test gRPC
1671        let grpc_response = router
1672            .route_request("grpc", "UserService.GetUser:{\"id\":42}")
1673            .await;
1674        assert!(grpc_response.is_ok());
1675        assert!(grpc_response.unwrap().contains("get_user"));
1676    }
1677
1678    #[tokio::test]
1679    async fn test_integration_multiple_handlers_all_protocols() {
1680        // Test: Multiple handlers, each exposed via all protocols
1681        let mut router = Router::new();
1682        router.register("get_user", || async { "User data".to_string() });
1683        router.register("list_users", || async { "Users list".to_string() });
1684        router.register("create_user", || async { "Created user".to_string() });
1685
1686        // Configure REST adapter
1687        let mut rest = RestAdapter::new();
1688        rest.route("GET", "/users/:id", "get_user");
1689        rest.route("GET", "/users", "list_users");
1690        rest.route("POST", "/users", "create_user");
1691        router.add_adapter(Box::new(rest));
1692
1693        // Configure GraphQL adapter
1694        let mut graphql = GraphQLAdapter::new();
1695        graphql.query("user", "get_user");
1696        graphql.query("users", "list_users");
1697        graphql.mutation("createUser", "create_user");
1698        router.add_adapter(Box::new(graphql));
1699
1700        // Configure gRPC adapter
1701        let mut grpc = GrpcAdapter::new();
1702        grpc.unary("UserService", "GetUser", "get_user");
1703        grpc.unary("UserService", "ListUsers", "list_users");
1704        grpc.unary("UserService", "CreateUser", "create_user");
1705        router.add_adapter(Box::new(grpc));
1706
1707        // Test each handler via each protocol
1708        assert!(router
1709            .route_request("rest", "GET /users/42")
1710            .await
1711            .unwrap()
1712            .contains("get_user"));
1713        assert!(router
1714            .route_request("graphql", "query { user }")
1715            .await
1716            .unwrap()
1717            .contains("get_user"));
1718        assert!(router
1719            .route_request("grpc", "UserService.GetUser:{}")
1720            .await
1721            .unwrap()
1722            .contains("get_user"));
1723    }
1724
1725    #[tokio::test]
1726    async fn test_integration_error_handling_rest_404() {
1727        // Test: REST 404 error
1728        let mut router = Router::new();
1729
1730        let mut rest = RestAdapter::new();
1731        rest.route("GET", "/users/:id", "get_user");
1732        router.add_adapter(Box::new(rest));
1733
1734        let response = router.route_request("rest", "GET /posts/42").await;
1735        assert!(response.is_ok());
1736        assert!(response.unwrap().contains("HTTP 404"));
1737    }
1738
1739    #[tokio::test]
1740    async fn test_integration_error_handling_graphql_not_found() {
1741        // Test: GraphQL operation not found
1742        let mut router = Router::new();
1743
1744        let mut graphql = GraphQLAdapter::new();
1745        graphql.query("user", "get_user");
1746        router.add_adapter(Box::new(graphql));
1747
1748        let response = router.route_request("graphql", "query { post }").await;
1749        assert!(response.is_ok());
1750        assert!(response.unwrap().contains("errors"));
1751    }
1752
1753    #[tokio::test]
1754    async fn test_integration_error_handling_grpc_unimplemented() {
1755        // Test: gRPC method not implemented
1756        let mut router = Router::new();
1757
1758        let mut grpc = GrpcAdapter::new();
1759        grpc.unary("UserService", "GetUser", "get_user");
1760        router.add_adapter(Box::new(grpc));
1761
1762        let response = router.route_request("grpc", "UserService.GetPost:{}").await;
1763        assert!(response.is_ok());
1764        assert!(response.unwrap().contains("grpc-status: 12")); // UNIMPLEMENTED
1765    }
1766
1767    #[tokio::test]
1768    async fn test_integration_unknown_protocol() {
1769        // Test: Unknown protocol error
1770        let router = Router::new();
1771
1772        let response = router.route_request("unknown", "request").await;
1773        assert!(response.is_err());
1774        assert!(response.unwrap_err().contains("Adapter not found"));
1775    }
1776
1777    #[tokio::test]
1778    async fn test_integration_protocol_specific_features_rest_methods() {
1779        // Test: REST-specific HTTP methods
1780        let mut router = Router::new();
1781        router.register("get_users", || async { "Users".to_string() });
1782        router.register("create_user", || async { "Created".to_string() });
1783        router.register("update_user", || async { "Updated".to_string() });
1784        router.register("delete_user", || async { "Deleted".to_string() });
1785
1786        let mut rest = RestAdapter::new();
1787        rest.route("GET", "/users", "get_users");
1788        rest.route("POST", "/users", "create_user");
1789        rest.route("PUT", "/users/:id", "update_user");
1790        rest.route("DELETE", "/users/:id", "delete_user");
1791        router.add_adapter(Box::new(rest));
1792
1793        // Test different HTTP methods
1794        assert!(router
1795            .route_request("rest", "GET /users")
1796            .await
1797            .unwrap()
1798            .contains("get_users"));
1799        assert!(router
1800            .route_request("rest", "POST /users")
1801            .await
1802            .unwrap()
1803            .contains("create_user"));
1804        assert!(router
1805            .route_request("rest", "PUT /users/42")
1806            .await
1807            .unwrap()
1808            .contains("update_user"));
1809        assert!(router
1810            .route_request("rest", "DELETE /users/42")
1811            .await
1812            .unwrap()
1813            .contains("delete_user"));
1814    }
1815
1816    #[tokio::test]
1817    async fn test_integration_protocol_specific_features_graphql_types() {
1818        // Test: GraphQL-specific query vs mutation
1819        let mut router = Router::new();
1820        router.register("get_user", || async { "User".to_string() });
1821        router.register("create_user", || async { "Created".to_string() });
1822
1823        let mut graphql = GraphQLAdapter::new();
1824        graphql.query("user", "get_user");
1825        graphql.mutation("createUser", "create_user");
1826        router.add_adapter(Box::new(graphql));
1827
1828        // Test query
1829        assert!(router
1830            .route_request("graphql", "query { user }")
1831            .await
1832            .unwrap()
1833            .contains("get_user"));
1834
1835        // Test mutation
1836        assert!(router
1837            .route_request("graphql", "mutation { createUser }")
1838            .await
1839            .unwrap()
1840            .contains("create_user"));
1841    }
1842
1843    #[tokio::test]
1844    async fn test_integration_protocol_specific_features_grpc_streaming() {
1845        // Test: gRPC-specific streaming modes
1846        let mut router = Router::new();
1847        router.register("get_user", || async { "User".to_string() });
1848        router.register("list_users", || async { "Users".to_string() });
1849
1850        let mut grpc = GrpcAdapter::new();
1851        grpc.unary("UserService", "GetUser", "get_user");
1852        grpc.server_streaming("UserService", "ListUsers", "list_users");
1853        router.add_adapter(Box::new(grpc));
1854
1855        // Test unary
1856        let unary_response = router
1857            .route_request("grpc", "UserService.GetUser:{}")
1858            .await
1859            .unwrap();
1860        assert!(unary_response.contains("unary"));
1861
1862        // Test server streaming
1863        let streaming_response = router
1864            .route_request("grpc", "UserService.ListUsers:{}")
1865            .await
1866            .unwrap();
1867        assert!(streaming_response.contains("server_streaming"));
1868    }
1869
1870    // ===== Streaming handler registration tests =====
1871
1872    #[tokio::test]
1873    async fn test_register_streaming_handler() {
1874        let mut router = Router::new();
1875        router.register_streaming("stream_data", |tx: StreamSender| async move {
1876            tx.send("item".to_string()).await.ok();
1877            "done".to_string()
1878        });
1879        assert!(router.is_streaming("stream_data"));
1880        assert!(!router.is_streaming("nonexistent"));
1881    }
1882
1883    #[tokio::test]
1884    async fn test_register_streaming_with_args() {
1885        #[derive(serde::Deserialize)]
1886        struct Input {
1887            count: usize,
1888        }
1889
1890        let mut router = Router::new();
1891        router.register_streaming_with_args("stream_items", |args: Input, tx: StreamSender| async move {
1892            for i in 0..args.count {
1893                tx.send(format!("item-{i}")).await.ok();
1894            }
1895            "done".to_string()
1896        });
1897        assert!(router.is_streaming("stream_items"));
1898    }
1899
1900    #[tokio::test]
1901    async fn test_streaming_handler_not_in_regular_handlers() {
1902        let mut router = Router::new();
1903        router.register_streaming("stream", |_tx: StreamSender| async move {
1904            "done".to_string()
1905        });
1906        // Streaming handlers are NOT in the regular handlers map
1907        assert_eq!(router.handlers_count(), 0);
1908    }
1909
1910    #[tokio::test]
1911    async fn test_list_handlers_includes_streaming() {
1912        let mut router = Router::new();
1913        router.register("regular", || async { "ok".to_string() });
1914        router.register_streaming("stream", |_tx: StreamSender| async move {
1915            "ok".to_string()
1916        });
1917
1918        let handlers = router.list_handlers();
1919        assert_eq!(handlers.len(), 2);
1920        assert!(handlers.contains(&"regular".to_string()));
1921        assert!(handlers.contains(&"stream".to_string()));
1922    }
1923
1924    #[tokio::test]
1925    async fn test_call_streaming_handler() {
1926        let mut router = Router::new();
1927        router.register_streaming("stream", |tx: StreamSender| async move {
1928            tx.send("a".to_string()).await.ok();
1929            tx.send("b".to_string()).await.ok();
1930            "final".to_string()
1931        });
1932
1933        let (mut rx, fut) = router.call_streaming_handler("stream", "{}").unwrap();
1934        let result = fut.await;
1935
1936        assert_eq!(result, Ok("final".to_string()));
1937        assert_eq!(rx.recv().await, Some("a".to_string()));
1938        assert_eq!(rx.recv().await, Some("b".to_string()));
1939    }
1940
1941    #[tokio::test]
1942    async fn test_call_streaming_handler_with_args() {
1943        #[derive(serde::Deserialize)]
1944        struct Input {
1945            n: usize,
1946        }
1947
1948        let mut router = Router::new();
1949        router.register_streaming_with_args("count", |args: Input, tx: StreamSender| async move {
1950            for i in 0..args.n {
1951                tx.send(format!("{i}")).await.ok();
1952            }
1953            format!("counted to {}", args.n)
1954        });
1955
1956        let (mut rx, fut) = router.call_streaming_handler("count", r#"{"n":3}"#).unwrap();
1957        let result = fut.await;
1958
1959        assert_eq!(result, Ok("counted to 3".to_string()));
1960        assert_eq!(rx.recv().await, Some("0".to_string()));
1961        assert_eq!(rx.recv().await, Some("1".to_string()));
1962        assert_eq!(rx.recv().await, Some("2".to_string()));
1963    }
1964
1965    #[tokio::test]
1966    async fn test_call_streaming_handler_not_found() {
1967        let router = Router::new();
1968        let result = router.call_streaming_handler("missing", "{}");
1969        assert!(result.is_err());
1970        match result {
1971            Err(e) => assert!(e.contains("not found")),
1972            Ok(_) => panic!("expected error"),
1973        }
1974    }
1975
1976    #[tokio::test]
1977    async fn test_is_streaming_false_for_regular() {
1978        let mut router = Router::new();
1979        router.register("regular", || async { "ok".to_string() });
1980        assert!(!router.is_streaming("regular"));
1981    }
1982
1983    #[tokio::test]
1984    async fn test_mixed_router() {
1985        let mut router = Router::new();
1986        router.register("get_user", || async { "user".to_string() });
1987        router.register_streaming("stream_updates", |tx: StreamSender| async move {
1988            tx.send("update".to_string()).await.ok();
1989            "done".to_string()
1990        });
1991
1992        // Regular handler works
1993        let result = router.execute("get_user").await;
1994        assert_eq!(result, Ok("user".to_string()));
1995
1996        // Streaming handler works
1997        let (mut rx, fut) = router.call_streaming_handler("stream_updates", "{}").unwrap();
1998        let result = fut.await;
1999        assert_eq!(result, Ok("done".to_string()));
2000        assert_eq!(rx.recv().await, Some("update".to_string()));
2001
2002        // Regular handler not found in streaming
2003        assert!(!router.is_streaming("get_user"));
2004        assert!(router.call_streaming_handler("get_user", "{}").is_err());
2005    }
2006
2007    #[tokio::test]
2008    async fn test_register_streaming_with_state() {
2009        struct AppState {
2010            prefix: String,
2011        }
2012
2013        #[derive(serde::Deserialize)]
2014        struct Input {
2015            name: String,
2016        }
2017
2018        let mut router = Router::new().with_state(AppState {
2019            prefix: "Hello".to_string(),
2020        });
2021        router.register_streaming_with_state::<AppState, Input, _, _, _>(
2022            "greet_stream",
2023            |state: State<Arc<AppState>>, args: Input, tx: StreamSender| async move {
2024                tx.send(format!("{} {}", state.prefix, args.name))
2025                    .await
2026                    .ok();
2027                "done".to_string()
2028            },
2029        );
2030
2031        let (mut rx, fut) = router
2032            .call_streaming_handler("greet_stream", r#"{"name":"Alice"}"#)
2033            .unwrap();
2034        let result = fut.await;
2035
2036        assert_eq!(result, Ok("done".to_string()));
2037        assert_eq!(rx.recv().await, Some("Hello Alice".to_string()));
2038    }
2039
2040    #[tokio::test]
2041    async fn test_register_streaming_with_state_only() {
2042        struct AppState {
2043            items: Vec<String>,
2044        }
2045
2046        let mut router = Router::new().with_state(AppState {
2047            items: vec!["x".to_string(), "y".to_string()],
2048        });
2049        router.register_streaming_with_state_only::<AppState, _, _, _>(
2050            "list_stream",
2051            |state: State<Arc<AppState>>, tx: StreamSender| async move {
2052                for item in &state.items {
2053                    tx.send(item.clone()).await.ok();
2054                }
2055                format!("listed {}", state.items.len())
2056            },
2057        );
2058
2059        let (mut rx, fut) = router
2060            .call_streaming_handler("list_stream", "{}")
2061            .unwrap();
2062        let result = fut.await;
2063
2064        assert_eq!(result, Ok("listed 2".to_string()));
2065        assert_eq!(rx.recv().await, Some("x".to_string()));
2066        assert_eq!(rx.recv().await, Some("y".to_string()));
2067    }
2068
2069    // ===== Stream return adapter tests =====
2070
2071    #[tokio::test]
2072    async fn test_register_stream_no_args() {
2073        let mut router = Router::new();
2074        router.register_stream("items", || async {
2075            tokio_stream::iter(vec!["a".to_string(), "b".to_string(), "c".to_string()])
2076        });
2077
2078        assert!(router.is_streaming("items"));
2079
2080        let (mut rx, fut) = router.call_streaming_handler("items", "{}").unwrap();
2081        let _result = fut.await;
2082
2083        assert_eq!(rx.recv().await, Some("a".to_string()));
2084        assert_eq!(rx.recv().await, Some("b".to_string()));
2085        assert_eq!(rx.recv().await, Some("c".to_string()));
2086    }
2087
2088    #[tokio::test]
2089    async fn test_register_stream_with_args() {
2090        #[derive(serde::Deserialize)]
2091        struct Input {
2092            count: usize,
2093        }
2094
2095        let mut router = Router::new();
2096        router.register_stream_with_args("counting", |args: Input| async move {
2097            tokio_stream::iter((0..args.count).map(|i| format!("{i}")))
2098        });
2099
2100        assert!(router.is_streaming("counting"));
2101
2102        let (mut rx, fut) = router
2103            .call_streaming_handler("counting", r#"{"count":3}"#)
2104            .unwrap();
2105        let _result = fut.await;
2106
2107        assert_eq!(rx.recv().await, Some("0".to_string()));
2108        assert_eq!(rx.recv().await, Some("1".to_string()));
2109        assert_eq!(rx.recv().await, Some("2".to_string()));
2110    }
2111
2112    #[tokio::test]
2113    async fn test_register_stream_with_state() {
2114        struct AppState {
2115            items: Vec<String>,
2116        }
2117
2118        let mut router = Router::new().with_state(AppState {
2119            items: vec!["x".to_string(), "y".to_string()],
2120        });
2121        router.register_stream_with_state::<AppState, serde_json::Value, _, _, _, _>(
2122            "state_stream",
2123            |state: State<Arc<AppState>>, _args: serde_json::Value| {
2124                let items = state.items.clone();
2125                async move { tokio_stream::iter(items) }
2126            },
2127        );
2128
2129        assert!(router.is_streaming("state_stream"));
2130    }
2131
2132    #[tokio::test]
2133    async fn test_stream_adapter_shows_in_is_streaming() {
2134        let mut router = Router::new();
2135        router.register_stream("my_stream", || async {
2136            tokio_stream::iter(vec!["done".to_string()])
2137        });
2138
2139        assert!(router.is_streaming("my_stream"));
2140        assert!(!router.is_streaming("nonexistent"));
2141    }
2142
2143    #[tokio::test]
2144    async fn test_multiple_state_types() {
2145        struct DbPool {
2146            url: String,
2147        }
2148        struct AppConfig {
2149            name: String,
2150        }
2151
2152        #[derive(serde::Deserialize)]
2153        struct Input {
2154            key: String,
2155        }
2156
2157        let mut router = Router::new()
2158            .with_state(DbPool {
2159                url: "postgres://localhost".to_string(),
2160            })
2161            .with_state(AppConfig {
2162                name: "MyApp".to_string(),
2163            });
2164
2165        // Handler using DbPool
2166        router.register_with_state::<DbPool, Input, _, _>(
2167            "db_query",
2168            |state: State<Arc<DbPool>>, args: Input| async move {
2169                format!("{}:{}", state.url, args.key)
2170            },
2171        );
2172
2173        // Handler using AppConfig
2174        router.register_with_state_only::<AppConfig, _, _>(
2175            "app_name",
2176            |state: State<Arc<AppConfig>>| async move { state.name.clone() },
2177        );
2178
2179        let result = router.call_handler("db_query", r#"{"key":"users"}"#).await;
2180        assert_eq!(result, Ok("postgres://localhost:users".to_string()));
2181
2182        let result = router.call_handler("app_name", "{}").await;
2183        assert_eq!(result, Ok("MyApp".to_string()));
2184    }
2185
2186    #[tokio::test]
2187    async fn test_inject_state_after_construction() {
2188        struct LateState {
2189            value: String,
2190        }
2191
2192        let mut router = Router::new();
2193        router.inject_state(LateState {
2194            value: "injected".to_string(),
2195        });
2196        router.register_with_state_only::<LateState, _, _>(
2197            "get_value",
2198            |state: State<Arc<LateState>>| async move { state.value.clone() },
2199        );
2200
2201        let result = router.call_handler("get_value", "{}").await;
2202        assert_eq!(result, Ok("injected".to_string()));
2203    }
2204
2205    #[tokio::test]
2206    async fn test_multiple_state_streaming() {
2207        struct StreamConfig {
2208            prefix: String,
2209        }
2210
2211        let mut router = Router::new().with_state(StreamConfig {
2212            prefix: "stream".to_string(),
2213        });
2214
2215        router.register_streaming_with_state_only::<StreamConfig, _, _, _>(
2216            "prefixed_stream",
2217            |state: State<Arc<StreamConfig>>, tx: StreamSender| async move {
2218                tx.send(format!("{}:item", state.prefix)).await.ok();
2219                "done".to_string()
2220            },
2221        );
2222
2223        let (mut rx, fut) = router
2224            .call_streaming_handler("prefixed_stream", "{}")
2225            .unwrap();
2226        let result = fut.await;
2227        assert_eq!(result, Ok("done".to_string()));
2228        assert_eq!(rx.recv().await, Some("stream:item".to_string()));
2229    }
2230
2231    #[tokio::test]
2232    async fn test_with_state_duplicate_type_last_wins() {
2233        // Calling with_state twice with the same type replaces the previous value.
2234        let mut router = Router::new()
2235            .with_state("first".to_string())
2236            .with_state("second".to_string());
2237
2238        router.register_with_state_only::<String, _, _>(
2239            "get",
2240            |state: State<Arc<String>>| async move { (**state).clone() },
2241        );
2242
2243        let result = router.call_handler("get", "{}").await;
2244        assert_eq!(result, Ok("second".to_string()));
2245    }
2246
2247    // ─── register_handlers! macro tests ─────────────────────────────────
2248
2249    // Define handler functions at module scope so they're visible as paths
2250    mod macro_test_handlers {
2251        use super::{State, StreamSender};
2252        use std::sync::Arc;
2253
2254        pub async fn health() -> String {
2255            "ok".to_string()
2256        }
2257
2258        pub async fn echo(args: EchoArgs) -> String {
2259            args.message
2260        }
2261
2262        #[derive(serde::Deserialize)]
2263        pub struct EchoArgs {
2264            pub message: String,
2265        }
2266
2267        pub async fn ticker(tx: StreamSender) -> String {
2268            tx.send("tick".to_string()).await.ok();
2269            "done".to_string()
2270        }
2271
2272        pub async fn search(args: SearchArgs, tx: StreamSender) -> String {
2273            tx.send(format!("found:{}", args.query)).await.ok();
2274            "complete".to_string()
2275        }
2276
2277        #[derive(serde::Deserialize)]
2278        pub struct SearchArgs {
2279            pub query: String,
2280        }
2281
2282        // State handler functions for register_handlers! state variants
2283        pub async fn get_status(state: State<Arc<String>>) -> String {
2284            format!("status:{}", *state)
2285        }
2286
2287        pub async fn save_key(state: State<Arc<String>>, args: SaveArgs) -> String {
2288            format!("{}:{}", *state, args.key)
2289        }
2290
2291        #[derive(serde::Deserialize)]
2292        pub struct SaveArgs {
2293            pub key: String,
2294        }
2295
2296        pub async fn state_stream(state: State<Arc<String>>, tx: StreamSender) -> String {
2297            tx.send(format!("{}:chunk", *state)).await.ok();
2298            "done".to_string()
2299        }
2300
2301        pub async fn state_search(
2302            state: State<Arc<String>>,
2303            args: SearchArgs,
2304            tx: StreamSender,
2305        ) -> String {
2306            tx.send(format!("{}:{}", *state, args.query)).await.ok();
2307            "complete".to_string()
2308        }
2309    }
2310
2311    #[tokio::test]
2312    async fn test_register_handlers_basic() {
2313        let mut router = Router::new();
2314        register_handlers!(router, [
2315            "health" => macro_test_handlers::health,
2316        ]);
2317        assert_eq!(router.handlers_count(), 1);
2318        let result = router.call_handler("health", "{}").await;
2319        assert_eq!(result, Ok("ok".to_string()));
2320    }
2321
2322    #[tokio::test]
2323    async fn test_register_handlers_with_args() {
2324        let mut router = Router::new();
2325        register_handlers!(router, [
2326            args "echo" => macro_test_handlers::echo,
2327        ]);
2328        assert_eq!(router.handlers_count(), 1);
2329        let result = router
2330            .call_handler("echo", r#"{"message":"hello"}"#)
2331            .await;
2332        assert_eq!(result, Ok("hello".to_string()));
2333    }
2334
2335    #[tokio::test]
2336    async fn test_register_handlers_streaming() {
2337        let mut router = Router::new();
2338        register_handlers!(router, [
2339            streaming "ticker" => macro_test_handlers::ticker,
2340        ]);
2341        assert!(router.is_streaming("ticker"));
2342        let (mut rx, fut) = router.call_streaming_handler("ticker", "{}").unwrap();
2343        let result = fut.await;
2344        assert_eq!(result, Ok("done".to_string()));
2345        assert_eq!(rx.recv().await, Some("tick".to_string()));
2346    }
2347
2348    #[tokio::test]
2349    async fn test_register_handlers_streaming_with_args() {
2350        let mut router = Router::new();
2351        register_handlers!(router, [
2352            streaming args "search" => macro_test_handlers::search,
2353        ]);
2354        assert!(router.is_streaming("search"));
2355        let (mut rx, fut) = router
2356            .call_streaming_handler("search", r#"{"query":"rust"}"#)
2357            .unwrap();
2358        let result = fut.await;
2359        assert_eq!(result, Ok("complete".to_string()));
2360        assert_eq!(rx.recv().await, Some("found:rust".to_string()));
2361    }
2362
2363    #[tokio::test]
2364    async fn test_register_handlers_mixed() {
2365        let mut router = Router::new();
2366        register_handlers!(router, [
2367            "health" => macro_test_handlers::health,
2368            args "echo" => macro_test_handlers::echo,
2369            streaming "ticker" => macro_test_handlers::ticker,
2370            streaming args "search" => macro_test_handlers::search,
2371        ]);
2372
2373        // All 4 registered (2 regular + 2 streaming)
2374        assert_eq!(router.handlers_count(), 2);
2375        assert_eq!(router.list_handlers().len(), 4);
2376
2377        // Regular handlers work
2378        assert_eq!(
2379            router.call_handler("health", "{}").await,
2380            Ok("ok".to_string())
2381        );
2382        assert_eq!(
2383            router
2384                .call_handler("echo", r#"{"message":"hi"}"#)
2385                .await,
2386            Ok("hi".to_string())
2387        );
2388
2389        // Streaming handlers work
2390        assert!(router.is_streaming("ticker"));
2391        assert!(router.is_streaming("search"));
2392    }
2393
2394    #[tokio::test]
2395    async fn test_register_handlers_empty() {
2396        let router = Router::new();
2397        register_handlers!(router, []);
2398        assert_eq!(router.handlers_count(), 0);
2399    }
2400
2401    #[tokio::test]
2402    async fn test_register_handlers_state_only() {
2403        let mut router = Router::new().with_state("active".to_string());
2404        register_handlers!(router, [
2405            state "get_status" => macro_test_handlers::get_status,
2406        ]);
2407        let result = router.call_handler("get_status", "{}").await;
2408        assert_eq!(result, Ok("status:active".to_string()));
2409    }
2410
2411    #[tokio::test]
2412    async fn test_register_handlers_state_args() {
2413        let mut router = Router::new().with_state("ns".to_string());
2414        register_handlers!(router, [
2415            state args "save_key" => macro_test_handlers::save_key,
2416        ]);
2417        let result = router
2418            .call_handler("save_key", r#"{"key":"api_token"}"#)
2419            .await;
2420        assert_eq!(result, Ok("ns:api_token".to_string()));
2421    }
2422
2423    #[tokio::test]
2424    async fn test_register_handlers_state_streaming() {
2425        let mut router = Router::new().with_state("ctx".to_string());
2426        register_handlers!(router, [
2427            state streaming "state_stream" => macro_test_handlers::state_stream,
2428        ]);
2429        assert!(router.is_streaming("state_stream"));
2430        let (mut rx, fut) = router
2431            .call_streaming_handler("state_stream", "{}")
2432            .unwrap();
2433        let result = fut.await;
2434        assert_eq!(result, Ok("done".to_string()));
2435        assert_eq!(rx.recv().await, Some("ctx:chunk".to_string()));
2436    }
2437
2438    #[tokio::test]
2439    async fn test_register_handlers_state_streaming_args() {
2440        let mut router = Router::new().with_state("db".to_string());
2441        register_handlers!(router, [
2442            state streaming args "state_search" => macro_test_handlers::state_search,
2443        ]);
2444        assert!(router.is_streaming("state_search"));
2445        let (mut rx, fut) = router
2446            .call_streaming_handler("state_search", r#"{"query":"rust"}"#)
2447            .unwrap();
2448        let result = fut.await;
2449        assert_eq!(result, Ok("complete".to_string()));
2450        assert_eq!(rx.recv().await, Some("db:rust".to_string()));
2451    }
2452
2453    #[tokio::test]
2454    async fn test_register_handlers_mixed_with_state() {
2455        let mut router = Router::new().with_state("app".to_string());
2456        register_handlers!(router, [
2457            "health" => macro_test_handlers::health,
2458            args "echo" => macro_test_handlers::echo,
2459            state "get_status" => macro_test_handlers::get_status,
2460            state args "save_key" => macro_test_handlers::save_key,
2461            streaming "ticker" => macro_test_handlers::ticker,
2462            state streaming "state_stream" => macro_test_handlers::state_stream,
2463            state streaming args "state_search" => macro_test_handlers::state_search,
2464        ]);
2465
2466        // Regular handlers
2467        assert_eq!(
2468            router.call_handler("health", "{}").await,
2469            Ok("ok".to_string())
2470        );
2471        assert_eq!(
2472            router.call_handler("get_status", "{}").await,
2473            Ok("status:app".to_string())
2474        );
2475        assert_eq!(
2476            router
2477                .call_handler("save_key", r#"{"key":"secret"}"#)
2478                .await,
2479            Ok("app:secret".to_string())
2480        );
2481
2482        // Streaming handlers
2483        assert!(router.is_streaming("ticker"));
2484        assert!(router.is_streaming("state_stream"));
2485        assert!(router.is_streaming("state_search"));
2486    }
2487
2488    // ─── KeyTransform tests ─────────────────────────────────────────────
2489
2490    #[test]
2491    fn test_camel_to_snake_basic() {
2492        assert_eq!(camel_to_snake("workflowId"), "workflow_id");
2493        assert_eq!(camel_to_snake("actionLabel"), "action_label");
2494        assert_eq!(camel_to_snake("simple"), "simple");
2495        assert_eq!(camel_to_snake("alreadySnake"), "already_snake");
2496        assert_eq!(camel_to_snake("ABC"), "a_b_c");
2497    }
2498
2499    #[test]
2500    fn test_camel_to_snake_single_char() {
2501        assert_eq!(camel_to_snake("a"), "a");
2502        assert_eq!(camel_to_snake("A"), "a");
2503    }
2504
2505    #[test]
2506    fn test_camel_to_snake_empty() {
2507        assert_eq!(camel_to_snake(""), "");
2508    }
2509
2510    #[test]
2511    fn test_camel_to_snake_already_snake() {
2512        assert_eq!(camel_to_snake("already_snake_case"), "already_snake_case");
2513    }
2514
2515    #[test]
2516    fn test_transform_keys_flat_object() {
2517        let input: Value = serde_json::json!({
2518            "workflowId": "abc",
2519            "actionLabel": "run"
2520        });
2521        let result = transform_keys(input, KeyTransform::CamelToSnake);
2522        assert_eq!(result, serde_json::json!({
2523            "workflow_id": "abc",
2524            "action_label": "run"
2525        }));
2526    }
2527
2528    #[test]
2529    fn test_transform_keys_nested_object() {
2530        let input: Value = serde_json::json!({
2531            "outerKey": {
2532                "innerKey": "value"
2533            }
2534        });
2535        let result = transform_keys(input, KeyTransform::CamelToSnake);
2536        assert_eq!(result, serde_json::json!({
2537            "outer_key": {
2538                "inner_key": "value"
2539            }
2540        }));
2541    }
2542
2543    #[test]
2544    fn test_transform_keys_array_of_objects() {
2545        let input: Value = serde_json::json!([
2546            {"firstName": "Alice"},
2547            {"firstName": "Bob"}
2548        ]);
2549        let result = transform_keys(input, KeyTransform::CamelToSnake);
2550        assert_eq!(result, serde_json::json!([
2551            {"first_name": "Alice"},
2552            {"first_name": "Bob"}
2553        ]));
2554    }
2555
2556    #[test]
2557    fn test_transform_keys_primitive_passthrough() {
2558        assert_eq!(transform_keys(Value::Null, KeyTransform::CamelToSnake), Value::Null);
2559        assert_eq!(transform_keys(serde_json::json!(42), KeyTransform::CamelToSnake), serde_json::json!(42));
2560        assert_eq!(transform_keys(serde_json::json!("hello"), KeyTransform::CamelToSnake), serde_json::json!("hello"));
2561    }
2562
2563    #[tokio::test]
2564    async fn test_router_with_key_transform_camel_to_snake() {
2565        #[derive(serde::Deserialize)]
2566        struct Input {
2567            workflow_name: String,
2568            is_active: bool,
2569        }
2570
2571        let mut router = Router::new()
2572            .with_key_transform(KeyTransform::CamelToSnake);
2573        router.register_with_args("test", |args: Input| async move {
2574            format!("{}:{}", args.workflow_name, args.is_active)
2575        });
2576
2577        // camelCase keys should be transformed to snake_case
2578        let result = router
2579            .call_handler("test", r#"{"workflowName":"deploy","isActive":true}"#)
2580            .await;
2581        assert_eq!(result, Ok("deploy:true".to_string()));
2582    }
2583
2584    #[tokio::test]
2585    async fn test_router_with_key_transform_already_snake() {
2586        #[derive(serde::Deserialize)]
2587        struct Input {
2588            workflow_name: String,
2589        }
2590
2591        let mut router = Router::new()
2592            .with_key_transform(KeyTransform::CamelToSnake);
2593        router.register_with_args("test", |args: Input| async move {
2594            args.workflow_name
2595        });
2596
2597        // snake_case keys should pass through unchanged
2598        let result = router
2599            .call_handler("test", r#"{"workflow_name":"deploy"}"#)
2600            .await;
2601        assert_eq!(result, Ok("deploy".to_string()));
2602    }
2603
2604    #[tokio::test]
2605    async fn test_router_without_key_transform() {
2606        #[derive(serde::Deserialize)]
2607        struct Input {
2608            workflow_name: String,
2609        }
2610
2611        let mut router = Router::new(); // no key transform
2612        router.register_with_args("test", |args: Input| async move {
2613            args.workflow_name
2614        });
2615
2616        // camelCase keys should fail without transform
2617        let result = router
2618            .call_handler("test", r#"{"workflowName":"deploy"}"#)
2619            .await;
2620        assert!(result.is_err());
2621        assert!(result.unwrap_err().contains("Failed to deserialize"));
2622    }
2623
2624    #[tokio::test]
2625    async fn test_router_key_transform_streaming_handler() {
2626        #[derive(serde::Deserialize)]
2627        struct Input {
2628            item_count: usize,
2629        }
2630
2631        let mut router = Router::new()
2632            .with_key_transform(KeyTransform::CamelToSnake);
2633        router.register_streaming_with_args("stream", |args: Input, tx: StreamSender| async move {
2634            for i in 0..args.item_count {
2635                tx.send(format!("{i}")).await.ok();
2636            }
2637            "done".to_string()
2638        });
2639
2640        let (mut rx, fut) = router
2641            .call_streaming_handler("stream", r#"{"itemCount":2}"#)
2642            .unwrap();
2643        let result = fut.await;
2644
2645        assert_eq!(result, Ok("done".to_string()));
2646        assert_eq!(rx.recv().await, Some("0".to_string()));
2647        assert_eq!(rx.recv().await, Some("1".to_string()));
2648    }
2649
2650    #[tokio::test]
2651    async fn test_router_key_transform_with_state() {
2652        struct AppState {
2653            prefix: String,
2654        }
2655
2656        #[derive(serde::Deserialize)]
2657        struct Input {
2658            user_name: String,
2659        }
2660
2661        let mut router = Router::new()
2662            .with_key_transform(KeyTransform::CamelToSnake)
2663            .with_state(AppState { prefix: "Hello".to_string() });
2664
2665        router.register_with_state::<AppState, Input, _, _>(
2666            "greet",
2667            |state: State<Arc<AppState>>, args: Input| async move {
2668                format!("{} {}", state.prefix, args.user_name)
2669            },
2670        );
2671
2672        let result = router
2673            .call_handler("greet", r#"{"userName":"Alice"}"#)
2674            .await;
2675        assert_eq!(result, Ok("Hello Alice".to_string()));
2676    }
2677
2678    #[tokio::test]
2679    async fn test_router_key_transform_zero_arg_handler_unaffected() {
2680        let mut router = Router::new()
2681            .with_key_transform(KeyTransform::CamelToSnake);
2682        router.register("health", || async { "ok".to_string() });
2683
2684        let result = router.call_handler("health", "{}").await;
2685        assert_eq!(result, Ok("ok".to_string()));
2686    }
2687}