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}