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}