Skip to main content

deboa_macros/
lib.rs

1//! # Deboa Macros
2//!
3//! This crate provides procedural macros for the `deboa` HTTP client to simplify
4//! common HTTP request patterns with a more concise syntax.
5//!
6//! ## Features
7//!
8//! - **Request Macros**: Shortcut macros for common HTTP methods (GET, POST, PUT, PATCH, DELETE)
9//! - **Type Safety**: Compile-time type checking for request/response bodies
10//! - **Async Support**: Seamless integration with async/await syntax
11//! - **Multiple Serialization Formats**: Support for JSON, XML, and MessagePack out of the box
12//!
13//! ## Available Macros
14//!
15//! - `get!`: Make a GET request
16//! - `post!`: Make a POST request with a body
17//! - `put!`: Make a PUT request with a body
18//! - `patch!`: Make a PATCH request with a body
19//! - `delete!`: Make a DELETE request
20//! - `fetch!`: Generic request macro that takes a method parameter
21//!
22//! ## Examples
23//!
24//! ### Basic GET Request
25//! ```compile_fail
26//! use deboa::{Client, Result};
27//! use deboa_extras::http::serde::json::JsonBody;
28//!
29//! #[derive(serde::Deserialize)]
30//! struct Post {
31//!     id: u32,
32//!     title: String,
33//!     body: String,
34//!     userId: u32,
35//! }
36//!
37//! #[tokio::main]
38//! async fn main() -> Result<()> {
39//!     let client = Client::new();
40//!     let post: Post = get!("https://jsonplaceholder.typicode.com/posts/1", &client, JsonBody, Post);
41//!     println!("Post title: {}", post.title);
42//!     Ok(())
43//! }
44//! ```
45//!
46//! ### POST with JSON Body
47//! ```compile_fail
48//! use deboa::{Client, Result};
49//! use deboa_extras::http::serde::json::JsonBody;
50//! use serde::Serialize;
51//!
52//! #[derive(Serialize)]
53//! struct NewPost {
54//!     title: String,
55//!     body: String,
56//!     userId: u32,
57//! }
58//!
59//! #[tokio::main]
60//! async fn main() -> Result<()> {
61//!     let client = Client::new();
62//!     let new_post = NewPost {
63//!         title: "Hello World".into(),
64//!         body: "This is a test post".into(),
65//!         userId: 1,
66//!     };
67//!     let response = post!(
68//!         new_post,
69//!         JsonBody,
70//!         "https://jsonplaceholder.typicode.com/posts",
71//!         &client
72//!     );
73//!     println!(200, response.status());
74//!     Ok(())
75//! }
76//! ```
77
78#[macro_export]
79/// Make a GET request to the specified URL.
80///
81/// The `get!` macro is used to make a GET request to the specified URL.
82/// Its first argument is a string literal or a variable. Arrows are
83/// used to specify the body serialization type and the output type.
84///
85/// You can use the `JsonBody`, `XmlBody`, `MsgPack` type for JSON, XML
86/// and MessagePack serialization.
87///
88/// To help understand the macro arguments, here is an example:
89///
90/// get!(url, headers, &mut client)
91///
92/// or
93///
94/// get!(url, &mut client, JsonBody, ty)
95///
96/// or
97///
98/// get!(url, vec![("User-Agent", "deboa")], &mut client, JsonBody, ty)
99///
100/// # Arguments
101///
102/// * `url`         - The URL to make the GET request to.
103/// * `client`      - The client variable to use for the request.
104/// * `res_body_ty` - The body type of the response.
105/// * `res_ty`      - The type of the response.
106///
107/// Please note url can be a string literal or a variable.
108///
109/// # Example
110///
111/// ```compile_fail
112/// let mut client = Client::new();
113/// let response = get!("https://jsonplaceholder.typicode.com/posts", &client, JsonBody, Vec<Post>);
114/// assert_eq!(response.len(), 100);
115/// ```
116macro_rules! get {
117    ($url:expr, &$client:ident) => {
118        $client
119            .execute($url)
120            .await?
121            .text()
122            .await?
123    };
124
125    ($url:expr, $headers:expr, &$client:ident) => {
126        deboa::request::DeboaRequest::get($url)?
127            .headers($headers)
128            .send_with($client)
129            .await?
130            .text()
131            .await?
132    };
133
134    ($url:literal, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
135        $client
136            .execute($url)
137            .await?
138            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
139            .await?
140    };
141
142    ($url:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
143        $client
144            .execute($url)
145            .await?
146            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
147            .await?
148    };
149
150    ($url:expr, $headers:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
151        deboa::request::DeboaRequest::get($url)?
152            .headers($headers)
153            .send_with($client)
154            .await?
155            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
156            .await?
157    };
158}
159
160#[macro_export]
161/// Make a POST request to the specified URL.
162///
163/// The `post!` macro is used to make a POST request to the specified URL.
164///
165/// It can be either:
166///
167/// post!(input, req_body_ty, url, &client)
168///
169/// or
170///   
171/// post!(input, req_body_ty, url, headers, &client)
172///
173/// or
174///
175/// post!(input, req_body_ty, url, &client, res_body_ty, res_ty)
176///
177/// or
178///
179/// post!(input, req_body_ty, url, headers, &client, res_body_ty, res_ty)
180///
181/// # Arguments
182///
183/// * `input`       - The input to send with the request.
184/// * `req_body_ty` - The body serialization format of the request.
185/// * `url`         - The URL to make the POST request to.
186/// * `headers`     - The headers to send with the request.
187/// * `client`      - The client variable to use for the request.
188/// * `res_body_ty` - The body serialization format of the response.
189/// * `res_ty`      - The type of the response.
190///
191/// Please note url can be a string literal or a variable.
192///
193/// # Example
194///
195/// ## Without response body deserialization
196///
197/// ```compile_fail
198/// let client = Client::new();
199/// let response = post!(data,
200///     JsonBody,
201///     "https://jsonplaceholder.typicode.com/posts",
202///     vec!(("Content-Type", "application/json")),
203///     &client
204/// );
205/// assert_eq!(response.id, 1);
206/// ```
207///
208/// ## With response body deserialization
209///
210/// ```compile_fail
211/// let client = Client::new();
212/// let response = post!(data, JsonBody, "https://jsonplaceholder.typicode.com/posts", &client, JsonBody, Post);
213/// assert_eq!(response.id, 1);
214/// ```
215macro_rules! post {
216    ($input:ident, $url:literal, &$client:ident) => {
217        $client
218            .execute(
219                deboa::request::DeboaRequest::post($url)?
220                    .body_as(deboa_extras::http::serde::json::JsonBody, $input)?
221                    .build()?,
222            )
223            .await?
224    };
225
226    ($input:ident, $url:expr, &$client:ident) => {
227        $client
228            .execute(
229                deboa::request::DeboaRequest::post($url)?
230                    .body_as(deboa_extras::http::serde::json::JsonBody, $input)?
231                    .build()?,
232            )
233            .await?
234    };
235
236    ($input:ident, $url:literal, $headers:expr, &$client:ident) => {
237        $client
238            .execute(
239                deboa::request::DeboaRequest::post($url)?
240                    .headers($headers)
241                    .body_as(deboa_extras::http::serde::json::JsonBody, $input)?
242                    .build()?,
243            )
244            .await?
245    };
246
247    ($input:ident, $req_body_ty:ident, $url:literal, &$client:ident) => {
248        $client
249            .execute(
250                deboa::request::DeboaRequest::post($url)?
251                    .body_as($req_body_ty, $input)?
252                    .build()?,
253            )
254            .await?
255    };
256
257    ($input:ident, $req_body_ty:ident, $url:expr, &$client:ident) => {
258        $client
259            .execute(
260                deboa::request::DeboaRequest::post($url)?
261                    .body_as($req_body_ty, $input)?
262                    .build()?,
263            )
264            .await?
265    };
266
267    ($input:ident, $req_body_ty:ident, $url:expr, $headers:expr, &$client:ident) => {
268        $client
269            .execute(
270                deboa::request::DeboaRequest::post($url)?
271                    .headers($headers)
272                    .body_as($req_body_ty, $input)?
273                    .build()?,
274            )
275            .await?
276    };
277
278    ($input:ident, $req_body_ty:ident, $url:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
279        $client
280            .execute(
281                deboa::request::DeboaRequest::post($url)?
282                    .body_as($req_body_ty, $input)?
283                    .build()?,
284            )
285            .await?
286            .body_as::<$res_body_ty, $res_ty>($res_body_ty)?
287    };
288
289    ($input:ident, $req_body_ty:ident, $url:expr, $headers:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
290        $client
291            .execute(
292                deboa::request::DeboaRequest::post($url)?
293                    .headers($headers)
294                    .body_as($req_body_ty, $input)?
295                    .build()?,
296            )
297            .await?
298            .body_as::<$res_body_ty, $res_ty>($res_body_ty)?
299    };
300}
301
302#[macro_export]
303/// Make a PUT request to the specified URL.
304///
305/// The `put!` macro is used to make a PUT request to the specified URL
306/// Its first argument is a string literal or a variable.
307///
308/// To help understand the macro arguments, here is an example:
309///
310/// put!(input, req_body_ty, url, &mut client)
311///
312/// # Arguments
313///
314/// * `input`       - The input to send with request.
315/// * `req_body_ty` - The body serialization format of request.
316/// * `url`         - The URL to make the PUT request to.
317/// * `headers`     - The headers to send with request.
318/// * `client`      - The client variable to use for request.
319///
320/// Please note url can be a string literal or a variable.
321///
322/// # Example
323///
324/// ```compile_fail
325/// let client = Client::new();
326/// let response = put!(data, JsonBody, "https://jsonplaceholder.typicode.com/posts/1", &client);
327/// assert_eq!(response.id, 1);
328/// ```
329macro_rules! put {
330    ($input:ident, $url:literal, &$client:ident) => {
331        $client
332            .execute(
333                deboa::request::DeboaRequest::put($url)?
334                    .body_as(deboa_extras::http::serde::json::JsonBody, $input)?
335                    .build()?,
336            )
337            .await?
338    };
339
340    ($input:ident, $url:expr, &$client:ident) => {
341        $client
342            .execute(
343                deboa::request::DeboaRequest::put($url)?
344                    .body_as(deboa_extras::http::serde::json::JsonBody, $input)?
345                    .build()?,
346            )
347            .await?
348    };
349
350    ($input:ident, $url:literal, $headers:expr, &$client:ident) => {
351        $client
352            .execute(
353                deboa::request::DeboaRequest::put($url)?
354                    .headers($headers)
355                    .body_as(deboa_extras::http::serde::json::JsonBody, $input)?
356                    .build()?,
357            )
358            .await?
359    };
360
361    ($input:ident, $req_body_ty:ident, $url:literal, &$client:ident) => {
362        $client
363            .execute(
364                deboa::request::DeboaRequest::put($url)?
365                    .body_as($req_body_ty, $input)?
366                    .build()?,
367            )
368            .await?
369    };
370
371    ($input:ident, $req_body_ty:ident, $url:expr, &$client:ident) => {
372        $client
373            .execute(
374                deboa::request::DeboaRequest::put($url)?
375                    .body_as($req_body_ty, $input)?
376                    .build()?,
377            )
378            .await?
379    };
380
381    ($input:ident, $req_body_ty:ident, $url:expr, $headers:expr, &$client:ident) => {
382        $client
383            .execute(
384                deboa::request::DeboaRequest::put($url)?
385                    .headers($headers)
386                    .body_as($req_body_ty, $input)?
387                    .build()?,
388            )
389            .await?
390    };
391
392    ($input:ident, $req_body_ty:ident, $url:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
393        $client
394            .execute(
395                deboa::request::DeboaRequest::put($url)?
396                    .body_as($req_body_ty, $input)?
397                    .build()?,
398            )
399            .await?
400            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
401            .await?
402    };
403
404    ($input:ident, $req_body_ty:ident, $url:expr, $headers:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
405        $client
406            .execute(
407                deboa::request::DeboaRequest::put($url)?
408                    .headers($headers)
409                    .body_as($req_body_ty, $input)?
410                    .build()?,
411            )
412            .await?
413            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
414            .await?
415    };
416}
417
418#[macro_export]
419/// Make a PATCH request to the specified URL.
420///
421/// The `patch!` macro is used to make a PATCH request to the specified URL
422/// Its first argument is a string literal or a variable.
423///
424/// To help understand the macro arguments, here is an example:
425///
426/// patch!(input, req_body_ty, url, &mut client)
427///
428/// # Arguments
429///
430/// * `input`       - The input to send with request.
431/// * `req_body_ty` - The body serialization format of request.
432/// * `url`         - The URL to make the PATCH request to.
433/// * `headers`     - The headers to send with request.
434/// * `client`      - The client variable to use for request.
435///
436/// Please note url can be a string literal or a variable.
437///
438/// # Example
439///
440/// ```compile_fail
441/// let client = Client::new();
442/// let response = patch!(data, JsonBody, "https://jsonplaceholder.typicode.com/posts/1", &client);
443/// assert_eq!(response.id, 1);
444/// ```
445macro_rules! patch {
446    ($input:ident, $url:literal, &$client:ident) => {
447        $client
448            .execute(
449                deboa::request::DeboaRequest::patch($url)?
450                    .body_as(deboa_extras::http::serde::json::JsonBody, $input)?
451                    .build()?,
452            )
453            .await?
454    };
455
456    ($input:ident, $url:expr, $headers:expr, &$client:ident) => {
457        $client
458            .execute(
459                deboa::request::DeboaRequest::patch($url)?
460                    .headers($headers)
461                    .body_as(deboa_extras::http::serde::json::JsonBody, $input)?
462                    .build()?,
463            )
464            .await?
465    };
466
467    ($input:ident, $req_body_ty:ident, $url:literal, &$client:ident) => {
468        $client
469            .execute(
470                deboa::request::DeboaRequest::patch($url)?
471                    .body_as($req_body_ty, $input)?
472                    .build()?,
473            )
474            .await?
475    };
476
477    ($input:ident, $req_body_ty:ident, $url:expr, &$client:ident) => {
478        $client
479            .execute(
480                deboa::request::DeboaRequest::patch($url)?
481                    .body_as($req_body_ty, $input)?
482                    .build()?,
483            )
484            .await?
485    };
486
487    ($input:ident, $req_body_ty:ident, $url:literal, $headers:expr, &$client:ident) => {
488        $client
489            .execute(
490                deboa::request::DeboaRequest::patch($url)?
491                    .headers($headers)
492                    .body_as($req_body_ty, $input)?
493                    .build()?,
494            )
495            .await?
496    };
497
498    ($input:ident, $req_body_ty:ident, $url:expr, $headers:expr, &$client:ident) => {
499        $client
500            .execute(
501                deboa::request::DeboaRequest::patch($url)?
502                    .headers($headers)
503                    .body_as($req_body_ty, $input)?
504                    .build()?,
505            )
506            .await?
507    };
508
509    ($input:ident, $req_body_ty:ident, $url:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
510        $client
511            .execute(
512                deboa::request::DeboaRequest::patch($url)?
513                    .body_as($req_body_ty, $input)?
514                    .build()?,
515            )
516            .await?
517            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
518            .await?
519    };
520
521    ($input:ident, $req_body_ty:ident, $url:expr, $headers:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
522        $client
523            .execute(
524                deboa::request::DeboaRequest::patch($url)?
525                    .headers($headers)
526                    .body_as($req_body_ty, $input)?
527                    .build()?,
528            )
529            .await?
530            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
531            .await?
532    };
533}
534
535#[macro_export]
536/// Make a DELETE request to the specified URL.
537///
538/// The `delete!` macro is used to make a DELETE request to the specified URL
539/// Its first argument is a string literal or a variable.
540///
541/// To help understand the macro arguments, here is an example:
542///
543/// delete!(url, &mut client)
544///
545/// # Arguments
546///
547/// * `url`    - The URL to make the DELETE request to.
548/// * `headers` - The headers to send with request.
549/// * `client` - The client variable to use for request.
550///
551/// Please note url can be a string literal or a variable.
552///
553/// # Example
554///
555/// ```compile_fail
556/// let client = Client::new();
557/// let response = delete!("https://jsonplaceholder.typicode.com/posts/1", &client);
558/// assert_eq!(response.id, 1);
559/// ```
560macro_rules! delete {
561    ($url:literal, &$client:ident) => {
562        $client
563            .execute(deboa::request::DeboaRequest::delete($url)?.build()?)
564            .await?
565    };
566
567    ($url:expr, &$client:ident) => {
568        $client
569            .execute(deboa::request::DeboaRequest::delete($url)?.build()?)
570            .await?
571    };
572
573    ($url:expr, $headers:expr, &$client:ident) => {
574        $client
575            .execute(
576                deboa::request::DeboaRequest::delete($url)?
577                    .headers($headers)
578                    .build()?,
579            )
580            .await?
581    };
582}
583
584#[macro_export]
585/// Make a GET request to the specified URL.
586///
587/// The `fetch!` macro is a more generic version of the `get!` macro.
588/// Its first argument is a string literal or a variable. Arrows are
589/// used to specify the body serialization type and the output type.
590///
591/// You can use the `JsonBody`, `XmlBody`, `MsgPack` type for JSON, XML
592/// and MessagePack serialization.
593///
594/// To help understand the macro arguments, here is an example:
595///
596/// fetch!(url, &mut client, body, ty)
597///
598/// # Arguments
599///
600/// * `url`         - The URL to make the GET request to.
601/// * `headers`     - The headers to send with request.
602/// * `client`      - The client variable to use request.
603/// * `res_body_ty` - The body serialization format of response.
604/// * `res_ty`      - The type of response.
605///
606/// Please note url can be a string literal or a variable.
607///
608/// # Example
609///
610/// ```compile_fail
611/// let client = Client::new();
612/// let response = fetch!("https://jsonplaceholder.typicode.com/posts", &client, JsonBody, Post);
613/// assert_eq!(response.id, 1);
614/// ```
615macro_rules! fetch {
616    ($url:expr, &$client:ident) => {
617        $client
618            .execute($url)
619            .await?
620    };
621
622    ($url:expr, $headers:expr, &$client:ident) => {
623        $client
624            .execute(
625                deboa::request::DeboaRequest::get($url)?
626                    .headers($headers)
627                    .build()?,
628            )
629            .await?
630    };
631
632    ($url:literal, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
633        $client
634            .execute($url)
635            .await?
636            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
637            .await?
638    };
639
640    ($url:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
641        $client
642            .execute($url)
643            .await?
644            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
645            .await?
646    };
647
648    ($url:expr, $headers:expr, &$client:ident, $res_body_ty:ident, $res_ty:ty) => {
649        $client
650            .execute(
651                deboa::request::DeboaRequest::get($url)?
652                    .headers($headers)
653                    .build()?,
654            )
655            .await?
656            .body_as::<$res_body_ty, $res_ty>($res_body_ty)
657            .await?
658    };
659}
660#[macro_export]
661/// Submit a request to the specified URL.
662///
663/// The `submit!` macro is a more generic version of the `get!` macro.
664/// Its first argument is a string literal or a variable. Arrows are
665/// used to specify the body serialization type and the output type.
666///
667/// You can use the `JsonBody`, `XmlBody`, `MsgPack` type for JSON, XML
668/// and MessagePack serialization.
669///
670/// To help understand the macro arguments, here is an example:
671///
672/// fetch!(url, &mut client, body, ty)
673///
674/// # Arguments
675///
676/// * `method`      - The HTTP method to use.
677/// * `input`       - The input to send with request.
678/// * `url`         - The URL to make the GET request to.
679/// * `headers`     - The headers to send with request.
680/// * `client`      - The client variable to use for request.
681/// * `res_body_ty` - The body serialization format of response.
682/// * `res_ty`      - The type of response.
683///
684/// Please note url can be a string literal or a variable.
685///
686/// # Example
687///
688/// ```compile_fail
689/// let client = Client::new();
690/// let response = submit!("POST", "user=deboa", "https://jsonplaceholder.typicode.com/posts", &client);
691/// assert_eq!(response.id, 1);
692/// ```
693macro_rules! submit {
694    ($method:expr, $input:expr, $url:expr, &$client:ident) => {
695        $client
696            .execute(
697                deboa::request::DeboaRequest::at($url, $method)?
698                    .text($input)
699                    .build()?,
700            )
701            .await?
702    };
703
704    ($method:expr, $input:expr, $url:expr, $headers:expr, &$client:ident) => {
705        $client
706            .execute(
707                deboa::request::DeboaRequest::at($url, $method)?
708                    .headers($headers)
709                    .text($input)
710                    .build()?,
711            )
712            .await?
713    };
714}
715
716#[macro_export]
717/// Make a GET request to the specified URL, returning a stream.
718///
719/// The `stream!` macro is used to make a GET request to the specified URL
720/// Its first argument is a string literal or a variable.
721///
722/// To help understand the macro arguments, here is an example:
723///
724/// stream!(url, &mut client)
725///
726/// # Arguments
727///
728/// * `url`    - The URL to make the GET request to.
729/// * `headers` - The headers to send with request.
730/// * `client` - The client variable to use for request.
731///
732/// Please note url can be a string literal or a variable.
733///
734/// # Example
735///
736/// ```compile_fail
737/// let client = Client::new();
738/// let response = stream!("https://jsonplaceholder.typicode.com/posts", &client);
739/// assert_eq!(response.id, 1);
740/// ```
741macro_rules! stream {
742    ($url:expr, &$client:ident) => {
743        $client
744            .execute($url)
745            .await?
746            .stream()
747    };
748
749    ($url:expr, $headers:expr, &$client:ident) => {
750        $client
751            .execute(
752                deboa::request::DeboaRequest::get($url)?
753                    .headers($headers)?
754                    .build()?,
755            )
756            .await?
757            .stream()
758    };
759}