wsforge_core/
handler.rs

1//! Handler traits and implementations for WebSocket message processing.
2//!
3//! This module provides the foundation for handling WebSocket messages with a flexible,
4//! type-safe interface. Handlers can accept multiple extractors and return various response types,
5//! making it easy to build complex message processing logic.
6//!
7//! # Overview
8//!
9//! The handler system consists of three main components:
10//! - [`Handler`] trait: Core trait that all handlers implement
11//! - [`IntoResponse`] trait: Converts handler return values into messages
12//! - [`HandlerService`]: Wrapper that bridges async functions to the Handler trait
13//!
14//! # Handler Signatures
15//!
16//! Handlers can have various signatures with different combinations of extractors:
17//!
18//! ```
19//! use wsforge::prelude::*;
20//! use std::sync::Arc;
21//!
22//! // No extractors
23//! async fn simple() -> Result<String> {
24//!     Ok("Hello!".to_string())
25//! }
26//!
27//! // Single extractor
28//! async fn with_message(msg: Message) -> Result<String> {
29//!     Ok("Received".to_string())
30//! }
31//!
32//! // Multiple extractors
33//! async fn complex(
34//!     Json(data): Json<serde_json::Value>,
35//!     conn: Connection,
36//!     State(manager): State<Arc<ConnectionManager>>,
37//! ) -> Result<()> {
38//!     Ok(())
39//! }
40//! ```
41//!
42//! # Return Types
43//!
44//! Handlers can return various types that implement [`IntoResponse`]:
45//!
46//! | Return Type | Description | Example |
47//! |-------------|-------------|---------|
48//! | `()` | No response sent | `async fn handler() -> Result<()>` |
49//! | `String` | Text message | `async fn handler() -> Result<String>` |
50//! | `&str` | Text message | `async fn handler() -> Result<&str>` |
51//! | `Message` | Raw message | `async fn handler() -> Result<Message>` |
52//! | `Vec<u8>` | Binary message | `async fn handler() -> Result<Vec<u8>>` |
53//! | `JsonResponse<T>` | JSON response | `async fn handler() -> Result<JsonResponse<T>>` |
54//! | `Result<T>` | Automatic error handling | Any of above wrapped in `Result` |
55//!
56//! # Examples
57//!
58//! ## Echo Handler
59//!
60//! ```
61//! use wsforge::prelude::*;
62//!
63//! async fn echo_handler(msg: Message) -> Result<Message> {
64//!     Ok(msg)
65//! }
66//! ```
67//!
68//! ## JSON Processing
69//!
70//! ```
71//! use wsforge::prelude::*;
72//! use serde::{Deserialize, Serialize};
73//!
74//! #[derive(Deserialize)]
75//! struct Request {
76//!     action: String,
77//! }
78//!
79//! #[derive(Serialize)]
80//! struct Response {
81//!     status: String,
82//!     result: String,
83//! }
84//!
85//! async fn json_handler(Json(req): Json<Request>) -> Result<JsonResponse<Response>> {
86//!     Ok(JsonResponse(Response {
87//!         status: "success".to_string(),
88//!         result: format!("Executed: {}", req.action),
89//!     }))
90//! }
91//! ```
92//!
93//! ## Broadcasting
94//!
95//! ```
96//! use wsforge::prelude::*;
97//! use std::sync::Arc;
98//!
99//! async fn broadcast_handler(
100//!     msg: Message,
101//!     conn: Connection,
102//!     State(manager): State<Arc<ConnectionManager>>,
103//! ) -> Result<()> {
104//!     // Broadcast to everyone except sender
105//!     manager.broadcast_except(conn.id(), msg);
106//!     Ok(())
107//! }
108//! ```
109
110use crate::connection::Connection;
111use crate::error::Result;
112use crate::extractor::{Extensions, FromMessage};
113use crate::message::Message;
114use crate::state::AppState;
115use async_trait::async_trait;
116use serde::Serialize;
117use std::marker::PhantomData;
118use std::sync::Arc;
119
120/// Trait for converting handler return values into WebSocket messages.
121///
122/// This trait allows handlers to return various types that are automatically
123/// converted to messages or no response. The framework handles the conversion
124/// transparently.
125///
126/// # Automatic Implementations
127///
128/// The trait is implemented for common return types:
129/// - `()` - No response is sent
130/// - `String` - Sent as text message
131/// - `&str` - Sent as text message
132/// - `Message` - Sent as-is
133/// - `Vec<u8>` - Sent as binary message
134/// - `Result<T>` - Automatically handles errors
135///
136/// # Examples
137///
138/// ## Implementing for Custom Types
139///
140/// ```
141/// use wsforge::prelude::*;
142/// use async_trait::async_trait;
143///
144/// struct CustomResponse {
145///     code: u32,
146///     data: String,
147/// }
148///
149/// #[async_trait]
150/// impl IntoResponse for CustomResponse {
151///     async fn into_response(self) -> Result<Option<Message>> {
152///         let text = format!("{}:{}", self.code, self.data);
153///         Ok(Some(Message::text(text)))
154///     }
155/// }
156///
157/// async fn handler() -> Result<CustomResponse> {
158///     Ok(CustomResponse {
159///         code: 200,
160///         data: "Success".to_string(),
161///     })
162/// }
163/// ```
164#[async_trait]
165pub trait IntoResponse: Send {
166    /// Converts this value into an optional message.
167    ///
168    /// # Returns
169    ///
170    /// - `Ok(Some(message))` - A message to send back to the client
171    /// - `Ok(None)` - No response should be sent
172    /// - `Err(error)` - An error occurred during conversion
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use wsforge::prelude::*;
178    ///
179    /// # async fn example() -> Result<()> {
180    /// let response = "Hello".to_string();
181    /// let message = response.into_response().await?;
182    /// assert!(message.is_some());
183    /// # Ok(())
184    /// # }
185    /// ```
186    async fn into_response(self) -> Result<Option<Message>>;
187}
188
189/// Response that sends nothing back to the client.
190///
191/// Use this when the handler only performs side effects and doesn't need
192/// to send a response.
193///
194/// # Examples
195///
196/// ```
197/// use wsforge::prelude::*;
198/// use std::sync::Arc;
199///
200/// async fn log_handler(
201///     msg: Message,
202///     State(manager): State<Arc<ConnectionManager>>,
203/// ) -> Result<()> {
204///     println!("Received message, {} connections active", manager.count());
205///     Ok(())
206/// }
207/// ```
208#[async_trait]
209impl IntoResponse for () {
210    async fn into_response(self) -> Result<Option<Message>> {
211        Ok(None)
212    }
213}
214
215/// Response that sends the message as-is.
216///
217/// Use this when you have full control over the message construction.
218///
219/// # Examples
220///
221/// ```
222/// use wsforge::prelude::*;
223///
224/// async fn custom_handler() -> Result<Message> {
225///     Ok(Message::text("Custom response"))
226/// }
227/// ```
228#[async_trait]
229impl IntoResponse for Message {
230    async fn into_response(self) -> Result<Option<Message>> {
231        Ok(Some(self))
232    }
233}
234
235/// Response that sends a string as a text message.
236///
237/// # Examples
238///
239/// ```
240/// use wsforge::prelude::*;
241///
242/// async fn greeting_handler() -> Result<String> {
243///     Ok(format!("Hello at {}", chrono::Utc::now()))
244/// }
245/// ```
246#[async_trait]
247impl IntoResponse for String {
248    async fn into_response(self) -> Result<Option<Message>> {
249        Ok(Some(Message::text(self)))
250    }
251}
252
253/// Response that sends a string slice as a text message.
254///
255/// # Examples
256///
257/// ```
258/// use wsforge::prelude::*;
259///
260/// async fn static_handler() -> Result<&'static str> {
261///     Ok("Static response")
262/// }
263/// ```
264#[async_trait]
265impl IntoResponse for &str {
266    async fn into_response(self) -> Result<Option<Message>> {
267        Ok(Some(Message::text(self.to_string())))
268    }
269}
270
271/// Response that sends binary data.
272///
273/// # Examples
274///
275/// ```
276/// use wsforge::prelude::*;
277///
278/// async fn binary_handler() -> Result<Vec<u8>> {
279///     Ok(vec![0x01, 0x02, 0x03, 0x04])
280/// }
281/// ```
282#[async_trait]
283impl IntoResponse for Vec<u8> {
284    async fn into_response(self) -> Result<Option<Message>> {
285        Ok(Some(Message::binary(self)))
286    }
287}
288
289/// Automatic error handling for handler results.
290///
291/// When a handler returns `Result<T>`, errors are automatically converted
292/// to error messages sent back to the client.
293///
294/// # Examples
295///
296/// ```
297/// use wsforge::prelude::*;
298///
299/// async fn validated_handler(msg: Message) -> Result<String> {
300///     let text = msg.as_text()
301///         .ok_or_else(|| Error::custom("Message must be text"))?;
302///
303///     if text.is_empty() {
304///         return Err(Error::custom("Message cannot be empty"));
305///     }
306///
307///     Ok(format!("Processed: {}", text))
308/// }
309/// ```
310#[async_trait]
311impl<T: IntoResponse> IntoResponse for Result<T> {
312    async fn into_response(self) -> Result<Option<Message>> {
313        match self {
314            Ok(resp) => resp.into_response().await,
315            Err(e) => Ok(Some(Message::text(format!("Error: {}", e)))),
316        }
317    }
318}
319
320/// JSON response wrapper.
321///
322/// Automatically serializes data to JSON and sends it as a text message.
323/// The type must implement `serde::Serialize`.
324///
325/// # Examples
326///
327/// ## Simple Response
328///
329/// ```
330/// use wsforge::prelude::*;
331/// use serde::Serialize;
332///
333/// #[derive(Serialize)]
334/// struct UserInfo {
335///     id: u64,
336///     name: String,
337/// }
338///
339/// async fn user_handler() -> Result<JsonResponse<UserInfo>> {
340///     Ok(JsonResponse(UserInfo {
341///         id: 123,
342///         name: "Alice".to_string(),
343///     }))
344/// }
345/// ```
346///
347/// ## Dynamic JSON
348///
349/// ```
350/// use wsforge::prelude::*;
351///
352/// async fn stats_handler() -> Result<JsonResponse<serde_json::Value>> {
353///     let stats = serde_json::json!({
354///         "users": 42,
355///         "messages": 1337,
356///         "uptime": 86400,
357///     });
358///     Ok(JsonResponse(stats))
359/// }
360/// ```
361pub struct JsonResponse<T: Serialize>(pub T);
362
363#[async_trait]
364impl<T: Serialize + Send> IntoResponse for JsonResponse<T> {
365    async fn into_response(self) -> Result<Option<Message>> {
366        let json = serde_json::to_string(&self.0)?;
367        Ok(Some(Message::text(json)))
368    }
369}
370
371/// Core trait for message handlers.
372///
373/// This trait is automatically implemented for async functions that match
374/// the required signature. You typically don't implement this trait directly;
375/// instead, use the [`handler()`] function to wrap your async functions.
376///
377/// # Automatic Implementation
378///
379/// Any async function with up to 8 extractor parameters that returns
380/// `impl IntoResponse` automatically implements this trait.
381///
382/// # Examples
383///
384/// ```
385/// use wsforge::prelude::*;
386///
387/// // This function automatically implements Handler
388/// async fn my_handler(msg: Message, conn: Connection) -> Result<String> {
389///     Ok(format!("Received from {}", conn.id()))
390/// }
391///
392/// # fn example() {
393/// // Wrap it in a HandlerService
394/// let handler = handler(my_handler);
395/// # }
396/// ```
397#[async_trait]
398pub trait Handler: Send + Sync + 'static {
399    /// Processes a message and returns an optional response.
400    ///
401    /// This method is called by the framework when a message is received.
402    /// It extracts the required data and executes the handler logic.
403    ///
404    /// # Arguments
405    ///
406    /// * `message` - The received WebSocket message
407    /// * `conn` - The connection that sent the message
408    /// * `state` - The application state
409    /// * `extensions` - Request-scoped extension data
410    ///
411    /// # Returns
412    ///
413    /// - `Ok(Some(message))` - Send this message back to the client
414    /// - `Ok(None)` - Don't send any response
415    /// - `Err(error)` - An error occurred during processing
416    async fn call(
417        &self,
418        message: Message,
419        conn: Connection,
420        state: AppState,
421        extensions: Extensions,
422    ) -> Result<Option<Message>>;
423}
424
425/// Service wrapper for handler functions.
426///
427/// This struct wraps async functions and implements the [`Handler`] trait.
428/// It uses a phantom type parameter to distinguish between different handler signatures.
429///
430/// You typically don't construct this directly; use the [`handler()`] function instead.
431///
432/// # Type Parameters
433///
434/// * `F` - The function type
435/// * `T` - Phantom type representing the extractor tuple
436pub struct HandlerService<F, T> {
437    handler: F,
438    _marker: PhantomData<fn() -> T>,
439}
440
441impl<F, T> HandlerService<F, T> {
442    /// Creates a new `HandlerService` wrapping the given function.
443    ///
444    /// # Examples
445    ///
446    /// ```
447    /// use wsforge::prelude::*;
448    ///
449    /// async fn my_handler() -> Result<String> {
450    ///     Ok("Hello".to_string())
451    /// }
452    ///
453    /// let service = HandlerService::new(my_handler);
454    /// ```
455    pub fn new(handler: F) -> Self {
456        Self {
457            handler,
458            _marker: PhantomData,
459        }
460    }
461}
462
463// Macro to implement Handler trait for various argument counts
464macro_rules! impl_handler {
465    (
466        $($ty:ident),*
467    ) => {
468        #[allow(non_snake_case)]
469        #[async_trait]
470        impl<F, Fut, Res, $($ty,)*> Handler for HandlerService<F, ($($ty,)*)>
471        where
472            F: Fn($($ty,)*) -> Fut + Send + Sync + 'static,
473            Fut: std::future::Future<Output = Res> + Send + 'static,
474            Res: IntoResponse,
475            $( $ty: FromMessage + Send + 'static, )*
476        {
477            async fn call(
478                &self,
479                _message: Message,
480                _conn: Connection,
481                _state: AppState,
482                _extensions: Extensions,
483            ) -> Result<Option<Message>> {
484                $(
485                    let $ty = $ty::from_message(&_message, &_conn, &_state, &_extensions).await?;
486                )*
487
488                let response = (self.handler)($($ty,)*).await;
489                response.into_response().await
490            }
491        }
492
493        impl<F, Fut, Res, $($ty,)*> IntoHandler<($($ty,)*)> for F
494        where
495            F: Fn($($ty,)*) -> Fut + Send + Sync + 'static,
496            Fut: std::future::Future<Output = Res> + Send + 'static,
497            Res: IntoResponse,
498            $( $ty: FromMessage + Send + 'static, )*
499        {
500            type Handler = HandlerService<F, ($($ty,)*)>;
501
502            fn into_handler(self) -> Self::Handler {
503                HandlerService::new(self)
504            }
505        }
506    };
507}
508
509/// Helper trait for converting functions into handlers.
510///
511/// This trait is automatically implemented for async functions and is used
512/// internally by the [`handler()`] function.
513///
514/// # Type Parameters
515///
516/// * `T` - Tuple representing the extractor types
517pub trait IntoHandler<T> {
518    /// The resulting handler type.
519    type Handler: Handler;
520
521    /// Converts this function into a handler.
522    fn into_handler(self) -> Self::Handler;
523}
524
525// Implement for 0 to 8 arguments
526impl_handler!();
527impl_handler!(T1);
528impl_handler!(T1, T2);
529impl_handler!(T1, T2, T3);
530impl_handler!(T1, T2, T3, T4);
531impl_handler!(T1, T2, T3, T4, T5);
532impl_handler!(T1, T2, T3, T4, T5, T6);
533impl_handler!(T1, T2, T3, T4, T5, T6, T7);
534impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8);
535
536/// Converts an async function into a handler.
537///
538/// This is the main function you use to create handlers from async functions.
539/// It automatically detects the function signature and creates the appropriate
540/// handler implementation.
541///
542/// # Type Inference
543///
544/// The function uses type inference to determine the extractor types. You don't
545/// need to specify any type parameters explicitly.
546///
547/// # Examples
548///
549/// ## Simple Handler
550///
551/// ```
552/// use wsforge::prelude::*;
553///
554/// async fn echo(msg: Message) -> Result<Message> {
555///     Ok(msg)
556/// }
557///
558/// # fn example() {
559/// let router = Router::new()
560///     .default_handler(handler(echo));
561/// # }
562/// ```
563///
564/// ## Handler with State
565///
566/// ```
567/// use wsforge::prelude::*;
568/// use std::sync::Arc;
569///
570/// async fn broadcast(
571///     msg: Message,
572///     State(manager): State<Arc<ConnectionManager>>,
573/// ) -> Result<()> {
574///     manager.broadcast(msg);
575///     Ok(())
576/// }
577///
578/// # fn example() {
579/// let router = Router::new()
580///     .default_handler(handler(broadcast));
581/// # }
582/// ```
583///
584/// ## Handler with Multiple Extractors
585///
586/// ```
587/// use wsforge::prelude::*;
588/// use serde::Deserialize;
589/// use std::sync::Arc;
590///
591/// #[derive(Deserialize)]
592/// struct GameMove {
593///     player_id: u64,
594///     action: String,
595/// }
596///
597/// async fn process_move(
598///     Json(game_move): Json<GameMove>,
599///     conn: Connection,
600///     State(manager): State<Arc<ConnectionManager>>,
601/// ) -> Result<String> {
602///     println!("Player {} from {} made move: {}",
603///         game_move.player_id, conn.id(), game_move.action);
604///
605///     manager.broadcast_except(conn.id(),
606///         Message::text(format!("Player {} moved", game_move.player_id)));
607///
608///     Ok("Move processed".to_string())
609/// }
610///
611/// # fn example() {
612/// let router = Router::new()
613///     .route("/game/move", handler(process_move));
614/// # }
615/// ```
616///
617/// ## Handler Returning JSON
618///
619/// ```
620/// use wsforge::prelude::*;
621/// use serde::Serialize;
622///
623/// #[derive(Serialize)]
624/// struct Stats {
625///     users: u32,
626///     messages: u64,
627/// }
628///
629/// async fn get_stats() -> Result<JsonResponse<Stats>> {
630///     Ok(JsonResponse(Stats {
631///         users: 42,
632///         messages: 1337,
633///     }))
634/// }
635///
636/// # fn example() {
637/// let router = Router::new()
638///     .route("/stats", handler(get_stats));
639/// # }
640/// ```
641pub fn handler<F, T>(f: F) -> Arc<dyn Handler>
642where
643    F: IntoHandler<T>,
644{
645    Arc::new(f.into_handler())
646}
647
648#[cfg(test)]
649mod tests {
650    use super::*;
651
652    #[tokio::test]
653    async fn test_string_into_response() {
654        let response = "test".to_string();
655        let result = response.into_response().await.unwrap();
656        assert!(result.is_some());
657    }
658
659    #[tokio::test]
660    async fn test_unit_into_response() {
661        let response = ();
662        let result = response.into_response().await.unwrap();
663        assert!(result.is_none());
664    }
665
666    #[tokio::test]
667    async fn test_json_response() {
668        use serde_json::json;
669
670        let data = json!({"key": "value"});
671        let response = JsonResponse(data);
672        let result = response.into_response().await.unwrap();
673        assert!(result.is_some());
674    }
675
676    #[tokio::test]
677    async fn test_handler_creation() {
678        async fn test_handler() -> Result<String> {
679            Ok("test".to_string())
680        }
681
682        let _handler = handler(test_handler);
683    }
684}