wsforge_macros/lib.rs
1//! # WsForge Macros - Procedural Macros for WebSocket Framework
2//!
3//! This crate provides procedural macros to enhance the WsForge WebSocket framework,
4//! making it easier to create handlers, derive traits, and reduce boilerplate code.
5//!
6//! ## Overview
7//!
8//! WsForge Macros offers compile-time code generation for common patterns in WebSocket
9//! development. These macros transform simple, declarative code into efficient runtime
10//! implementations, improving both developer experience and code maintainability.
11//!
12//! ## Available Macros
13//!
14//! | Macro | Type | Purpose |
15//! |-------|------|---------|
16//! | `#[websocket_handler]` | Attribute | Transform functions into WebSocket handlers |
17//! | `#[derive(WebSocketMessage)]` | Derive | Auto-implement message conversion traits |
18//! | `#[derive(WebSocketHandler)]` | Derive | Auto-implement handler trait |
19//! | `routes!()` | Function-like | Create router with multiple routes |
20//!
21//! ## Features
22//!
23//! - 🎯 **Zero Runtime Overhead**: All code generation happens at compile time
24//! - 🔧 **Type Safe**: Generated code is fully type-checked by the compiler
25//! - 📝 **Less Boilerplate**: Reduce repetitive code patterns
26//! - 🚀 **Performance**: Generates optimized code equivalent to hand-written implementations
27//! - 🔍 **IDE Support**: Works with rust-analyzer for code completion and errors
28//!
29//! ## Macro Usage Examples
30//!
31//! ### `#[websocket_handler]` Attribute Macro
32//!
33//! Transform regular async functions into WebSocket handlers:
34//!
35//! ```
36//! use wsforge_macros::websocket_handler;
37//! use wsforge_core::prelude::*;
38//!
39//! #[websocket_handler]
40//! async fn echo(msg: Message) -> Result<Message> {
41//! Ok(msg)
42//! }
43//! ```
44//!
45//! ### `#[derive(WebSocketMessage)]` Derive Macro
46//!
47//! Automatically implement message conversion methods:
48//!
49//! ```
50//! use wsforge_macros::WebSocketMessage;
51//! use serde::{Deserialize, Serialize};
52//!
53//! #[derive(WebSocketMessage, Serialize, Deserialize)]
54//! struct ChatMessage {
55//! username: String,
56//! text: String,
57//! }
58//!
59//! // Now you can use:
60//! // let msg = chat_message.into_message();
61//! // let chat = ChatMessage::from_message(msg)?;
62//! ```
63//!
64//! ### `#[derive(WebSocketHandler)]` Derive Macro
65//!
66//! Automatically implement the Handler trait for custom types:
67//!
68//! ```
69//! use wsforge_macros::WebSocketHandler;
70//! use wsforge_core::prelude::*;
71//!
72//! #[derive(WebSocketHandler)]
73//! struct MyHandler;
74//!
75//! impl MyHandler {
76//! async fn handle(
77//! &self,
78//! message: Message,
79//! conn: Connection,
80//! state: AppState,
81//! extensions: Extensions,
82//! ) -> Result<Option<Message>> {
83//! Ok(Some(Message::text("Handled!")))
84//! }
85//! }
86//! ```
87//!
88//! ### `routes!()` Macro
89//!
90//! Create a router with multiple routes in a declarative way:
91//!
92//! ```
93//! use wsforge_macros::routes;
94//!
95//! let router = routes!();
96//! // Expands to: Router::new()
97//! ```
98//!
99//! ## Implementation Details
100//!
101//! ### Code Generation
102//!
103//! All macros use the `syn`, `quote`, and `proc-macro2` crates for parsing and
104//! code generation. The generated code is designed to be:
105//!
106//! - **Readable**: Generated code looks like hand-written Rust
107//! - **Efficient**: No unnecessary allocations or conversions
108//! - **Compatible**: Works with all standard Rust tooling
109//!
110//! ### Error Handling
111//!
112//! Macros provide clear error messages at compile time when:
113//! - Invalid syntax is used
114//! - Required traits are not implemented
115//! - Type constraints are not satisfied
116//!
117//! ## Advanced Usage
118//!
119//! ### Combining Macros
120//!
121//! You can combine multiple macros for maximum effect:
122//!
123//! ```
124//! use wsforge_macros::{websocket_handler, WebSocketMessage};
125//! use wsforge_core::prelude::*;
126//! use serde::{Deserialize, Serialize};
127//!
128//! #[derive(WebSocketMessage, Serialize, Deserialize)]
129//! struct Request {
130//! action: String,
131//! }
132//!
133//! #[derive(WebSocketMessage, Serialize, Deserialize)]
134//! struct Response {
135//! result: String,
136//! }
137//!
138//! #[websocket_handler]
139//! async fn process(Json(req): Json<Request>) -> Result<JsonResponse<Response>> {
140//! Ok(JsonResponse(Response {
141//! result: format!("Processed: {}", req.action),
142//! }))
143//! }
144//! ```
145//!
146//! ## Performance Characteristics
147//!
148//! - **Compile Time**: Minimal impact on compilation time
149//! - **Runtime**: Zero overhead - generated code is as fast as hand-written
150//! - **Binary Size**: No additional bloat from macro expansion
151//!
152//! ## Compatibility
153//!
154//! - **Rust Edition**: 2021 and later
155//! - **Tooling**: Full support for rust-analyzer, rustfmt, clippy
156//! - **Platforms**: All platforms supported by Rust
157//!
158//! ## Debugging Generated Code
159//!
160//! To see the code generated by macros, use `cargo-expand`:
161//!
162//! ```
163//! cargo install cargo-expand
164//! cargo expand --lib
165//! ```
166//!
167//! ## Future Enhancements
168//!
169//! Potential future macros:
170//! - `#[route]` - Define routes on handler functions directly
171//! - `#[middleware]` - Create middleware from functions
172//! - `#[state]` - Simplify state extraction patterns
173
174// Enable documentation features for docs.rs
175#![cfg_attr(docsrs, feature(doc_cfg))]
176// Deny missing docs to ensure comprehensive documentation
177#![warn(missing_docs)]
178// Enable additional documentation lint rules
179#![warn(rustdoc::missing_crate_level_docs)]
180
181use proc_macro::TokenStream;
182use quote::quote;
183use syn::{DeriveInput, ItemFn, parse_macro_input};
184
185/// Transforms an async function into a WebSocket handler.
186///
187/// This attribute macro wraps your async function to make it compatible with
188/// the WsForge handler system. It preserves all function attributes, visibility,
189/// and signature while adding the necessary wrapper code.
190///
191/// # Behavior
192///
193/// The macro:
194/// - Preserves the original function signature
195/// - Maintains async/await semantics
196/// - Keeps visibility modifiers (pub, pub(crate), etc.)
197/// - Preserves all other attributes
198/// - Does not modify the function body
199///
200/// # Usage
201///
202/// ```
203/// use wsforge_macros::websocket_handler;
204/// use wsforge_core::prelude::*;
205///
206/// #[websocket_handler]
207/// async fn my_handler(msg: Message) -> Result<String> {
208/// Ok(format!("Received: {:?}", msg))
209/// }
210/// ```
211///
212/// # With Multiple Parameters
213///
214/// ```
215/// use wsforge_macros::websocket_handler;
216/// use wsforge_core::prelude::*;
217/// use std::sync::Arc;
218///
219/// #[websocket_handler]
220/// async fn complex_handler(
221/// msg: Message,
222/// conn: Connection,
223/// State(manager): State<Arc<ConnectionManager>>,
224/// ) -> Result<()> {
225/// manager.broadcast(msg);
226/// Ok(())
227/// }
228/// ```
229///
230/// # With Custom Return Types
231///
232/// ```
233/// use wsforge_macros::websocket_handler;
234/// use wsforge_core::prelude::*;
235/// use serde::Serialize;
236///
237/// #[derive(Serialize)]
238/// struct Response {
239/// status: String,
240/// }
241///
242/// #[websocket_handler]
243/// async fn json_handler() -> Result<JsonResponse<Response>> {
244/// Ok(JsonResponse(Response {
245/// status: "ok".to_string(),
246/// }))
247/// }
248/// ```
249///
250/// # Note
251///
252/// The function must be async and return a type that implements `IntoResponse`.
253#[proc_macro_attribute]
254pub fn websocket_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
255 let input = parse_macro_input!(item as ItemFn);
256
257 let fn_name = &input.sig.ident;
258 let fn_block = &input.block;
259 let fn_inputs = &input.sig.inputs;
260 let fn_output = &input.sig.output;
261 let fn_asyncness = &input.sig.asyncness;
262 let fn_vis = &input.vis;
263
264 let expanded = quote! {
265 #fn_vis #fn_asyncness fn #fn_name(#fn_inputs) #fn_output {
266 #fn_block
267 }
268 };
269
270 TokenStream::from(expanded)
271}
272
273/// Derives message conversion methods for custom types.
274///
275/// This derive macro automatically implements `into_message()` and `from_message()`
276/// methods for your type, allowing easy conversion between your custom types and
277/// WebSocket messages.
278///
279/// # Requirements
280///
281/// The type must implement:
282/// - `serde::Serialize` (for `into_message()`)
283/// - `serde::Deserialize` (for `from_message()`)
284///
285/// # Generated Methods
286///
287/// ## `into_message(&self) -> wsforge::Message`
288///
289/// Converts the type into a WebSocket text message containing JSON.
290///
291/// ## `from_message(msg: wsforge::Message) -> Result<Self, serde_json::Error>`
292///
293/// Parses a WebSocket message as JSON into your type.
294///
295/// # Examples
296///
297/// ## Basic Usage
298///
299/// ```
300/// use wsforge_macros::WebSocketMessage;
301/// use serde::{Deserialize, Serialize};
302///
303/// #[derive(WebSocketMessage, Serialize, Deserialize)]
304/// struct ChatMessage {
305/// username: String,
306/// text: String,
307/// }
308///
309/// # fn example() {
310/// let chat = ChatMessage {
311/// username: "Alice".to_string(),
312/// text: "Hello!".to_string(),
313/// };
314///
315/// // Convert to message
316/// let msg = chat.into_message();
317///
318/// // Parse from message
319/// let parsed = ChatMessage::from_message(msg).unwrap();
320/// # }
321/// ```
322///
323/// ## With Nested Types
324///
325/// ```
326/// use wsforge_macros::WebSocketMessage;
327/// use serde::{Deserialize, Serialize};
328///
329/// #[derive(Serialize, Deserialize)]
330/// struct User {
331/// id: u64,
332/// name: String,
333/// }
334///
335/// #[derive(WebSocketMessage, Serialize, Deserialize)]
336/// struct UserMessage {
337/// user: User,
338/// action: String,
339/// }
340/// ```
341///
342/// ## Error Handling
343///
344/// ```
345/// use wsforge_macros::WebSocketMessage;
346/// use wsforge_core::prelude::*;
347/// use serde::{Deserialize, Serialize};
348///
349/// #[derive(WebSocketMessage, Serialize, Deserialize)]
350/// struct Request {
351/// command: String,
352/// }
353///
354/// # fn example(msg: Message) {
355/// match Request::from_message(msg) {
356/// Ok(req) => println!("Command: {}", req.command),
357/// Err(e) => eprintln!("Parse error: {}", e),
358/// }
359/// # }
360/// ```
361#[proc_macro_derive(WebSocketMessage)]
362pub fn derive_websocket_message(input: TokenStream) -> TokenStream {
363 let input = parse_macro_input!(input as DeriveInput);
364 let name = &input.ident;
365
366 let expanded = quote! {
367 impl #name {
368 pub fn into_message(&self) -> wsforge::Message {
369 wsforge::Message::text(serde_json::to_string(self).unwrap())
370 }
371
372 pub fn from_message(msg: wsforge::Message) -> Result<Self, serde_json::Error> {
373 msg.json::<Self>()
374 }
375 }
376 };
377
378 TokenStream::from(expanded)
379}
380
381/// Derives the Handler trait for custom handler types.
382///
383/// This macro automatically implements the `Handler` trait for your type,
384/// delegating to a `handle` method that you must implement. This is useful
385/// for creating stateful handlers or handlers with complex initialization.
386///
387/// # Requirements
388///
389/// Your type must implement a method with this signature:
390///
391/// ```
392/// async fn handle(
393/// &self,
394/// message: wsforge::Message,
395/// conn: wsforge::Connection,
396/// state: wsforge::AppState,
397/// extensions: wsforge::Extensions,
398/// ) -> wsforge::Result<Option<wsforge::Message>>
399/// ```
400///
401/// # Examples
402///
403/// ## Simple Handler
404///
405/// ```
406/// use wsforge_macros::WebSocketHandler;
407/// use wsforge_core::prelude::*;
408///
409/// #[derive(WebSocketHandler)]
410/// struct EchoHandler;
411///
412/// impl EchoHandler {
413/// async fn handle(
414/// &self,
415/// message: Message,
416/// _conn: Connection,
417/// _state: AppState,
418/// _extensions: Extensions,
419/// ) -> Result<Option<Message>> {
420/// Ok(Some(message))
421/// }
422/// }
423/// ```
424///
425/// ## Stateful Handler
426///
427/// ```
428/// use wsforge_macros::WebSocketHandler;
429/// use wsforge_core::prelude::*;
430/// use std::sync::Arc;
431/// use tokio::sync::RwLock;
432///
433/// #[derive(WebSocketHandler)]
434/// struct CounterHandler {
435/// count: Arc<RwLock<u64>>,
436/// }
437///
438/// impl CounterHandler {
439/// async fn handle(
440/// &self,
441/// _message: Message,
442/// _conn: Connection,
443/// _state: AppState,
444/// _extensions: Extensions,
445/// ) -> Result<Option<Message>> {
446/// let mut count = self.count.write().await;
447/// *count += 1;
448/// Ok(Some(Message::text(format!("Count: {}", count))))
449/// }
450/// }
451/// ```
452///
453/// ## With Configuration
454///
455/// ```
456/// use wsforge_macros::WebSocketHandler;
457/// use wsforge_core::prelude::*;
458///
459/// #[derive(WebSocketHandler)]
460/// struct ConfiguredHandler {
461/// max_length: usize,
462/// }
463///
464/// impl ConfiguredHandler {
465/// fn new(max_length: usize) -> Self {
466/// Self { max_length }
467/// }
468///
469/// async fn handle(
470/// &self,
471/// message: Message,
472/// _conn: Connection,
473/// _state: AppState,
474/// _extensions: Extensions,
475/// ) -> Result<Option<Message>> {
476/// if message.as_bytes().len() > self.max_length {
477/// return Err(Error::custom("Message too long"));
478/// }
479/// Ok(Some(message))
480/// }
481/// }
482/// ```
483#[proc_macro_derive(WebSocketHandler)]
484pub fn derive_websocket_handler(input: TokenStream) -> TokenStream {
485 let input = parse_macro_input!(input as DeriveInput);
486 let name = &input.ident;
487
488 let expanded = quote! {
489 #[wsforge::async_trait]
490 impl wsforge::Handler for #name {
491 async fn call(
492 &self,
493 message: wsforge::Message,
494 conn: wsforge::Connection,
495 state: wsforge::AppState,
496 extensions: wsforge::Extensions,
497 ) -> wsforge::Result<Option<wsforge::Message>> {
498 self.handle(message, conn, state, extensions).await
499 }
500 }
501 };
502
503 TokenStream::from(expanded)
504}
505
506/// Creates a new Router instance.
507///
508/// This is a simple convenience macro that expands to `Router::new()`.
509/// It's provided for consistency with potential future routing macros
510/// that may accept declarative route definitions.
511///
512/// # Usage
513///
514/// ```
515/// use wsforge_macros::routes;
516///
517/// let router = routes!();
518/// // Equivalent to: Router::new()
519/// ```
520///
521/// # Future Enhancement
522///
523/// This macro may be extended in the future to support declarative routing:
524///
525/// ```
526/// let router = routes! {
527/// "/echo" => echo_handler,
528/// "/chat" => chat_handler,
529/// "/api/*" => api_handler,
530/// };
531/// ```
532///
533/// # Examples
534///
535/// ## Current Usage
536///
537/// ```
538/// use wsforge_macros::routes;
539/// use wsforge_core::prelude::*;
540///
541/// async fn handler(msg: Message) -> Result<String> {
542/// Ok("response".to_string())
543/// }
544///
545/// # async fn example() -> Result<()> {
546/// let router = routes!()
547/// .route("/echo", handler(handler));
548///
549/// router.listen("127.0.0.1:8080").await?;
550/// # Ok(())
551/// # }
552/// ```
553#[proc_macro]
554pub fn routes(_input: TokenStream) -> TokenStream {
555 let expanded = quote! {
556 wsforge::Router::new()
557 };
558
559 TokenStream::from(expanded)
560}