awpak_rs_macros/
lib.rs

1use impls::{awpak_main::awpak_main_impl, from_value::from_value_impl, methods::{connect::connect_impl, delete::delete_impl, get::get_impl, head::head_impl, options::options_impl, patch::patch_impl, post::post_impl, put::put_impl, trace::trace_impl}, middleware::middleware_impl, redirect_to::redirect_to_impl, set_status_code::set_status_code_impl};
2use proc_macro::TokenStream;
3use quote::quote;
4
5mod impls;
6mod util;
7
8/// The `awpak_main` macro defines the entry point for an `awpak-rs` web application.
9///
10/// This macro should be applied to the `main` function and allows optional configuration
11/// of the server's IP address and port.
12///
13/// # Parameters
14///
15/// - `ip` *(optional, default: `"127.0.0.1"`)*  
16///   Specifies the IP address on which the server will listen.
17///
18/// - `port` *(optional, default: `"3000"`)*  
19///   Specifies the port on which the server will listen.
20///
21/// # Example
22///
23/// ```ignore
24/// #[awpak_main(ip = "127.0.0.1", port = "3001")]
25/// fn main() {}
26/// ```
27///
28/// In this example, the web server will start on `127.0.0.1:3001`.  
29/// If no parameters are provided, the server will default to `127.0.0.1:3000`.
30#[proc_macro_attribute]
31pub fn awpak_main( args: TokenStream, item: TokenStream ) -> TokenStream
32{
33    awpak_main_impl( args, item )
34}
35
36/// The `post` macro defines an HTTP POST endpoint for an `awpak-rs` web application.
37///
38/// This macro should be applied to a function that handles HTTP requests for the specified URL.
39/// 
40/// The return type of the function can be any Rust primitive or any type that implements `Serialize` from `serde`.
41///
42/// # Asynchronous Execution
43/// 
44/// Functions annotated with `post` are executed asynchronously.  
45/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
46/// This means you can freely use `.await` inside these functions.
47/// 
48/// # Parameters
49///
50/// - `url` *(required)*  
51///   The URL pattern for this endpoint.
52///
53/// # Example
54///
55/// ```ignore
56/// #[post(url = "/post_body_echo")]
57/// fn post_body_echo(#[request_body] point: Point) -> Point {
58///     point
59/// }
60/// ```
61///
62/// In this example, a request to `POST /post_body_echo` with a JSON body `{ "x": 3, "y": 2 }`
63/// will return the same object.
64#[proc_macro_attribute]
65pub fn post( args: TokenStream, item: TokenStream ) -> TokenStream
66{
67    post_impl( args, item )
68}
69
70/// The `get` macro defines an HTTP GET endpoint for an `awpak-rs` web application.
71///
72/// This macro should be applied to a function that handles HTTP requests for the specified URL.
73/// 
74/// The return type of the function can be any Rust primitive or any type that implements `Serialize` from `serde`.
75///
76/// # Asynchronous Execution
77///
78/// Functions annotated with `get` are executed asynchronously.  
79/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
80/// This means you can freely use `.await` inside these functions.
81/// 
82/// # Parameters
83///
84/// - `url` *(required)*  
85///   The URL pattern for this endpoint.
86///
87/// # Example
88///
89/// ```ignore
90/// #[get(url = "/get_echo")]
91/// fn get_echo() -> String {
92///     "Hello, world!".to_string()
93/// }
94/// ```
95///
96/// In this example, a request to `GET /get_echo` will return `"Hello, world!"`.
97#[proc_macro_attribute]
98pub fn get( args: TokenStream, item: TokenStream ) -> TokenStream
99{
100    get_impl( args, item )
101}
102
103/// Defines an HTTP `CONNECT` route.
104///
105/// This macro registers a function as a handler for `CONNECT` requests.
106/// It takes a required `url` parameter to specify the endpoint path.
107///
108/// # Asynchronous Execution
109/// 
110/// Functions annotated with `connect` are executed asynchronously.  
111/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
112/// This means you can freely use `.await` inside these functions.
113/// 
114/// # Parameters
115/// - `url`: *(required)* The endpoint URL pattern.
116///
117/// # Return Type
118/// The function's return type can be any Rust primitive or a type implementing `Serialize` from `serde`.
119///
120/// # Example
121/// ```ignore
122/// #[connect(url = "/connect_example")]
123/// fn handle_connect() -> String {
124///     "Connected".to_string()
125/// }
126/// ```
127#[proc_macro_attribute]
128pub fn connect( args: TokenStream, item: TokenStream ) -> TokenStream
129{
130    connect_impl( args, item )
131}
132
133/// Defines an HTTP `DELETE` route.
134///
135/// This macro registers a function as a handler for `DELETE` requests.
136/// It takes a required `url` parameter to specify the endpoint path.
137///
138/// # Asynchronous Execution
139/// 
140/// Functions annotated with `delete` are executed asynchronously.  
141/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
142/// This means you can freely use `.await` inside these functions.
143/// 
144/// # Parameters
145/// - `url`: *(required)* The endpoint URL pattern.
146///
147/// # Return Type
148/// The function's return type can be any Rust primitive or a type implementing `Serialize` from `serde`.
149///
150/// # Example
151/// ```ignore
152/// #[delete(url = "/delete_example")]
153/// fn handle_delete() -> String {
154///     "Deleted".to_string()
155/// }
156/// ```
157#[proc_macro_attribute]
158pub fn delete( args: TokenStream, item: TokenStream ) -> TokenStream
159{
160    delete_impl( args, item )
161}
162
163/// Defines an HTTP `HEAD` route.
164///
165/// This macro registers a function as a handler for `HEAD` requests.
166/// It takes a required `url` parameter to specify the endpoint path.
167///
168/// # Asynchronous Execution
169/// 
170/// Functions annotated with `head` are executed asynchronously.  
171/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
172/// This means you can freely use `.await` inside these functions.
173/// 
174/// # Parameters
175/// - `url`: *(required)* The endpoint URL pattern.
176///
177/// # Return Type
178/// The function's return type can be any Rust primitive or a type implementing `Serialize` from `serde`.
179///
180/// # Example
181/// ```ignore
182/// #[head(url = "/head_example")]
183/// fn handle_head() {}
184/// ```
185#[proc_macro_attribute]
186pub fn head( args: TokenStream, item: TokenStream ) -> TokenStream
187{
188    head_impl( args, item )
189}
190
191/// Defines an HTTP `OPTIONS` route.
192///
193/// This macro registers a function as a handler for `OPTIONS` requests.
194/// It takes a required `url` parameter to specify the endpoint path.
195///
196/// # Asynchronous Execution
197/// 
198/// Functions annotated with `options` are executed asynchronously.  
199/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
200/// This means you can freely use `.await` inside these functions.
201/// 
202/// # Parameters
203/// - `url`: *(required)* The endpoint URL pattern.
204///
205/// # Return Type
206/// The function's return type can be any Rust primitive or a type implementing `Serialize` from `serde`.
207///
208/// # Example
209/// ```ignore
210/// #[options(url = "/options_example")]
211/// fn handle_options() -> String {
212///     "Allowed methods: GET, POST".to_string()
213/// }
214/// ```
215#[proc_macro_attribute]
216pub fn options( args: TokenStream, item: TokenStream ) -> TokenStream
217{
218    options_impl( args, item )
219}
220
221/// Defines an HTTP `PATCH` route.
222///
223/// This macro registers a function as a handler for `PATCH` requests.
224/// It takes a required `url` parameter to specify the endpoint path.
225///
226/// # Asynchronous Execution
227/// 
228/// Functions annotated with `patch` are executed asynchronously.  
229/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
230/// This means you can freely use `.await` inside these functions.
231/// 
232/// # Parameters
233/// - `url`: *(required)* The endpoint URL pattern.
234///
235/// # Return Type
236/// The function's return type can be any Rust primitive or a type implementing `Serialize` from `serde`.
237///
238/// # Example
239/// ```ignore
240/// #[patch(url = "/patch_example")]
241/// fn handle_patch() -> String {
242///     "Patched".to_string()
243/// }
244/// ```
245#[proc_macro_attribute]
246pub fn patch( args: TokenStream, item: TokenStream ) -> TokenStream
247{
248    patch_impl( args, item )
249}
250
251/// Defines an HTTP `PUT` route.
252///
253/// This macro registers a function as a handler for `PUT` requests.
254/// It takes a required `url` parameter to specify the endpoint path.
255/// 
256/// # Asynchronous Execution
257/// 
258/// Functions annotated with `put` are executed asynchronously.  
259/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
260/// This means you can freely use `.await` inside these functions.
261///
262/// # Parameters
263/// - `url`: *(required)* The endpoint URL pattern.
264///
265/// # Return Type
266/// The function's return type can be any Rust primitive or a type implementing `Serialize` from `serde`.
267///
268/// # Example
269/// ```ignore
270/// #[put(url = "/put_example")]
271/// fn handle_put() -> String {
272///     "Updated".to_string()
273/// }
274/// ```
275#[proc_macro_attribute]
276pub fn put( args: TokenStream, item: TokenStream ) -> TokenStream
277{
278    put_impl( args, item )
279}
280
281/// Defines an HTTP `TRACE` route.
282///
283/// This macro registers a function as a handler for `TRACE` requests.
284/// It takes a required `url` parameter to specify the endpoint path.
285///
286/// # Asynchronous Execution
287/// 
288/// Functions annotated with `trace` are executed asynchronously.  
289/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
290/// This means you can freely use `.await` inside these functions.
291/// 
292/// # Parameters
293/// - `url`: *(required)* The endpoint URL pattern.
294///
295/// # Return Type
296/// The function's return type can be any Rust primitive or a type implementing `Serialize` from `serde`.
297///
298/// # Example
299/// ```ignore
300/// #[trace(url = "/trace_example")]
301/// fn handle_trace() -> String {
302///     "Trace response".to_string()
303/// }
304/// ```
305#[proc_macro_attribute]
306pub fn trace( args: TokenStream, item: TokenStream ) -> TokenStream
307{
308    trace_impl( args, item )
309}
310
311/// The `request_body` macro deserializes the entire request body into a Rust type.
312///
313/// This macro should be applied to function parameters that represent the body of the request.
314///
315/// The type must implement `Deserialize` and `FromValue`.
316///
317/// # Example
318///
319/// ```ignore
320/// #[post(url = "/post_point")]
321/// fn post_point(#[request_body] point: Point) -> Point {
322///     point
323/// }
324/// ```
325///
326/// A request like `POST /post_point` with a JSON body `{ "x": 3, "y": 2 }`
327/// will return the same object.
328#[proc_macro]
329pub fn request_body( _args : TokenStream ) -> TokenStream
330{
331    quote! {}.into()
332}
333
334/// The `query_params` macro deserializes multiple query parameters into a struct.
335///
336/// This macro should be applied to a function parameter that implements `Deserialize` and `FromValue`.
337///
338/// # Example
339///
340/// ```ignore
341/// #[derive(Serialize, Deserialize, FromValue)]
342/// struct Point {
343///     x: Option<f32>,
344///     y: f32,
345/// }
346///
347/// #[get(url = "/point")]
348/// fn get_point(#[query_params] point: Point) -> Point {
349///     point
350/// }
351/// ```
352///
353/// A request like `GET /point?x=1.5&y=2.0` will return a JSON object `{"x":1.5,"y":2.0}`.
354#[proc_macro]
355pub fn query_params( _args : TokenStream ) -> TokenStream
356{
357    quote! {}.into()
358}
359
360/// Macro for extracting a single file from a multipart request in `awpak-rs`.
361///
362/// The `part_file` macro allows an endpoint to retrieve a specific file from a multipart request.
363/// The extracted `FileData` struct contains information about the uploaded file, including its name, filename, bytes, and content type.
364///
365/// # Example
366/// ```ignore
367/// #[post( url = "/post_multipart_file_len" )]
368/// fn post_multipart_file_len(
369///     #[part_file] img: FileData
370/// ) -> usize {
371///     img.bytes.len()
372/// }
373/// ```
374///
375/// If the file is optional, use `Option<FileData>`:
376/// ```ignore
377/// #[part_file] img: Option<FileData>
378/// ```
379#[proc_macro]
380pub fn part_file( _args : TokenStream ) -> TokenStream
381{
382    quote! {}.into()
383}
384
385/// Macro for extracting multiple files from a multipart request in `awpak-rs`.
386///
387/// The `part_files` macro allows an endpoint to retrieve all files uploaded in a multipart request.
388/// The extracted `Vec<FileData>` contains information about each file received.
389///
390/// # Example
391/// ```ignore
392/// #[post( url = "/post_multipart_files_len" )]
393/// fn post_multipart_files_len(
394///     #[part_files] img: Vec<FileData>
395/// ) -> usize {
396///     img.iter().map(|i| i.bytes.len()).sum()
397/// }
398/// ```
399#[proc_macro]
400pub fn part_files( _args : TokenStream ) -> TokenStream
401{
402    quote! {}.into()
403}
404
405/// The `body_param` macro extracts a specific parameter from the request body.
406///
407/// This macro should be applied to function parameters to deserialize a single value
408/// from the body of a JSON request.
409///
410/// The extracted type must be a Rust primitive or implement `Deserialize` and `FromValue`.
411///
412/// # Example
413///
414/// ```ignore
415/// #[post(url = "/post_x")]
416/// fn post_x(#[body_param] x: f32) -> f32 {
417///     x
418/// }
419/// ```
420///
421/// If a request sends `{ "x": 3.5, "y": 9.7 }` in the body, the function will receive `3.5` as `x`.
422#[proc_macro]
423pub fn body_param( _args : TokenStream ) -> TokenStream
424{
425    quote! {}.into()
426}
427
428/// The `path_variable` macro extracts a path parameter from the URL.
429///
430/// This macro should be applied to function parameters to deserialize path variables.
431/// The extracted value can be converted into any Rust primitive type or any type
432/// that implements the `FromAsyncStr` trait.
433///
434/// # Example
435///
436/// ```ignore
437/// #[get(url = "/user/{name}")]
438/// fn get_user(#[path_variable] name: String) -> String {
439///     name
440/// }
441/// ```
442///
443/// A request like `GET /user/john` will return `"john"`.
444///
445/// You can also deserialize the path variable into a more complex type that
446/// implements `FromAsyncStr`:
447///
448/// ```ignore
449/// #[get(url = "/user/{id}")]
450/// async fn get_user(#[path_variable] user: User) -> User {
451///     user
452/// }
453/// ```
454///
455/// If `User` implements `FromAsyncStr`, the framework will automatically fetch
456/// the user from a database or other source based on the `id` provided in the URL.
457#[proc_macro]
458pub fn path_variable( _args : TokenStream ) -> TokenStream
459{
460    quote! {}.into()
461}
462
463/// Macro for accessing and modifying the request context in `awpak-rs`.
464///
465/// The `context` macro allows an endpoint to retrieve or modify the shared context set by a middleware.
466/// This context can store application-specific data that needs to persist through the request lifecycle.
467///
468/// # Example
469/// ```ignore
470/// struct ContextTest {
471///     x: usize,
472/// }
473///
474/// #[middleware]
475/// fn middleware_set_context(mut io: IO) -> MiddlewareResponse {
476///     io.set_context(ContextTest { x: 77 });
477///     MiddlewareResponse::Next(io)
478/// }
479///
480/// #[post( url = "/post_echo_context" )]
481/// fn post_echo_context(
482///     #[context] ctx: Option<&ContextTest>
483/// ) -> String {
484///     match ctx {
485///         Some(v) => format!("x:{}", v.x),
486///         None => "None".to_string(),
487///     }
488/// }
489/// ```
490///
491/// To modify the context, use `Option<&mut T>` instead:
492/// ```ignore
493/// #[post( url = "/post_modify_context" )]
494/// fn post_modify_context(
495///     #[context] ctx: Option<&mut ContextTest>
496/// ) -> String {
497///     if let Some(c) = ctx {
498///         c.x += 1;
499///         format!("Updated x: {}", c.x)
500///     } else {
501///         "No context available".to_string()
502///     }
503/// }
504/// ```
505#[proc_macro]
506pub fn context( _args : TokenStream ) -> TokenStream
507{
508    quote! {}.into()
509}
510
511/// Extracts the request headers in an endpoint function.
512///
513/// The `request_headers` macro allows an endpoint function to access the HTTP request headers.
514/// It must be used as an attribute on a function parameter of type `Headers`.
515///
516/// # Usage
517/// - If headers need to be modified, the parameter should be mutable (`mut`).
518/// - The headers can be queried using methods from the `Headers` struct.
519///
520/// # Example
521/// ```ignore
522/// #[post( url = "/post_request_header_example" )]
523/// fn post_request_header_example(
524///     #[request_headers]
525///     headers : Headers
526/// ) -> String
527/// {
528///     match headers.get_value("content-type") {
529///         Some(c) => c,
530///         _ => "Content-Type not found".to_string()
531///     }
532/// }
533/// ```
534#[proc_macro]
535pub fn request_headers( _args : TokenStream ) -> TokenStream
536{
537    quote! {}.into()
538}
539
540/// Provides access to response headers in an endpoint function.
541///
542/// The `response_headers` macro allows modifying HTTP response headers.
543/// It must be used as an attribute on a function parameter of type `Headers`.
544///
545/// # Usage
546/// - If headers need to be modified, the parameter should be mutable (`mut`).
547/// - Headers can be set or replaced using methods from the `Headers` struct.
548///
549/// # Example
550/// ```ignore
551/// #[post( url = "/post_response_header_example" )]
552/// fn post_response_header_example(
553///     #[response_headers]
554///     mut headers : Headers
555/// ) -> String
556/// {
557///     headers.replace_header("content-type".to_string(), "application/json".to_string());
558///
559///     "Content-Type changed".to_string()
560/// }
561/// ```
562#[proc_macro]
563pub fn response_headers( _args : TokenStream ) -> TokenStream
564{
565    quote! {}.into()
566}
567
568/// Macro for accessing cookies in `awpak-rs` request handlers.
569///
570/// The `request_cookies` macro allows an endpoint to access the cookies sent by the client in the request.
571/// The extracted `Cookies` struct provides methods for retrieving specific cookies by name.
572///
573/// # Example
574/// ```ignore
575/// #[post( url = "/post_request_cookies_example" )]
576/// fn post_request_cookies_example(
577///     #[request_cookies] cookies: Cookies
578/// ) -> String {
579///     match cookies.find_first_by_name("session_id") {
580///         Some(cookie) => format!("Session ID: {}", cookie.value()),
581///         None => "No session cookie found".to_string(),
582///     }
583/// }
584/// ```
585#[proc_macro]
586pub fn request_cookies( _args : TokenStream ) -> TokenStream
587{
588    quote! {}.into()
589}
590
591/// Macro for modifying response cookies in `awpak-rs` request handlers.
592///
593/// The `response_cookies` macro allows an endpoint to modify the cookies that will be sent in the response.
594/// The extracted `Cookies` struct can be used to add or replace cookies.
595///
596/// # Example
597/// ```ignore
598/// #[post( url = "/post_response_cookies_example" )]
599/// fn post_response_cookies_example(
600///     #[response_cookies] mut res_cookies: Cookies
601/// ) -> String {
602///     res_cookies.add_cookie("user_id=12345; Path=/;").unwrap();
603///     "Cookie set successfully".to_string()
604/// }
605/// ```
606///
607/// If cookies do not need to be modified, the `mut` keyword can be omitted.
608#[proc_macro]
609pub fn response_cookies( _args : TokenStream ) -> TokenStream
610{
611    quote! {}.into()
612}
613
614/// The `query_param` macro extracts a single query parameter from the URL.
615///
616/// This macro should be applied to function parameters to deserialize query parameters.
617/// 
618/// The extracted type must be a Rust primitive or implement `Deserialize` and `FromValue`.
619///
620/// # Example
621///
622/// ```ignore
623/// #[get(url = "/sum")]
624/// fn sum(
625///     #[query_param] a: u16,
626///     #[query_param] b: u16
627/// ) -> u16 {
628///     a + b
629/// }
630/// ```
631///
632/// A request like `GET /sum?a=5&b=10` will return `15`.
633#[proc_macro]
634pub fn query_param( _args : TokenStream ) -> TokenStream
635{
636    quote! {}.into()
637}
638
639/// Derive macro for implementing `FromValue`.
640///
641/// This macro automatically generates an implementation of the `FromValue` trait,
642/// allowing a struct to be deserialized from query parameters, request bodies, or
643/// other extracted values.
644///
645/// # Usage
646///
647/// This derive macro is used for structs that need to be deserialized using
648/// `query_param`, `query_params`, `request_body`, or `body_param`. It works alongside
649/// `serde`'s `Deserialize` trait.
650///
651/// # Example
652///
653/// ```ignore
654/// use awpak_rs::FromValue;
655/// use serde::Deserialize;
656///
657/// #[derive(Deserialize, FromValue)]
658/// struct User {
659///     id: u32,
660///     name: String,
661/// }
662/// ```
663///
664/// Now, `User` can be used with `#[query_param]`, `#[query_params]`, `#[request_body]`, or `#[body_param]`
665/// to automatically deserialize values from the request.
666#[proc_macro_derive(FromValue)]
667pub fn derive_from_value( item : TokenStream ) -> TokenStream
668{
669    from_value_impl( item )
670}
671
672/// Defines a middleware function in `awpak-rs`.
673///
674/// Middleware functions allow modifying incoming requests, responses, or setting a shared context
675/// before or after executing an endpoint. Middleware can be applied globally or conditionally
676/// based on URL patterns, HTTP methods, or execution order.
677///
678/// # Asynchronous Execution
679/// 
680/// Functions annotated with `middleware` are executed asynchronously.  
681/// You do not need to explicitly mark them as `async`, as the macro automatically ensures async execution.  
682/// This means you can freely use `.await` inside these functions.
683/// 
684/// # Parameters
685///
686/// - `urls`: *(optional, default: all URLs)*  
687///   A list of regular expressions defining which URLs this middleware should apply to.
688///   Example: `urls = ["/api/.*"]` applies the middleware to all endpoints under `/api/`.
689///
690/// - `order`: *(optional, default: 10000)*  
691///   Determines the execution order of middlewares. Lower values run first. If two middlewares
692///   have the same order, their execution order is undefined.
693///
694/// - `execute_after`: *(optional, default: `false`)*  
695///   If `true`, the middleware will execute after the endpoint instead of before it.
696///
697/// - `method`: *(optional, default: all methods)*  
698///   Restricts the middleware to a specific HTTP method (e.g., `method = "get"`).
699///
700/// # Middleware Response
701///
702/// Middleware functions must return a `MiddlewareResponse`, which can be:
703/// - `MiddlewareResponse::Next(io)`: Continue to the next middleware or endpoint.
704/// - `MiddlewareResponse::Cancel(io)`: Stop execution and return a response immediately.
705///
706/// # Example: Logging Middleware
707///
708/// ```ignore
709/// use awpak_rs::*;
710///
711/// #[middleware(urls=["/secure/.*"], order=1)]
712/// fn logging_middleware(mut io: IO) -> MiddlewareResponse {
713///     println!("Request to: {}", io.request.uri.path);
714///     MiddlewareResponse::Next(io)
715/// }
716/// ```
717///
718/// # Example: Authentication Middleware
719///
720/// ```ignore
721/// #[middleware(order=5, execute_after=false)]
722/// fn auth_middleware(mut io: IO) -> MiddlewareResponse {
723///     if let Some(token) = io.request.headers.get("Authorization") {
724///         if validate_token(token) {
725///             return MiddlewareResponse::Next(io);
726///         }
727///     }
728///     io.response.status = 401;
729///     MiddlewareResponse::Cancel(io)
730/// }
731/// ```
732#[proc_macro_attribute]
733pub fn middleware( args: TokenStream, item: TokenStream ) -> TokenStream
734{
735    middleware_impl( args, item )
736}
737
738#[proc_macro]
739pub fn set_status_code( item : TokenStream ) -> TokenStream
740{
741    set_status_code_impl( item )
742}
743
744#[proc_macro]
745pub fn redirect_to( item : TokenStream ) -> TokenStream
746{
747    redirect_to_impl( item )
748}