conquer_struct/
lib.rs

1//!
2//! # Conquer Struct
3//!
4//!
5//! Introducing `conquer_struct`, as the name suggest it will
6//! conquer concurreny for you and you can avoid writing any boilerplate
7//! code. Let's take the same example as above, look at how to make it concurrent
8//!
9//! ```rust
10//!
11//! async fn get_contact_info(contact_id: usize) -> Contact {
12//!     conquer_future!(Contact {
13//!         name: async { get_contact_name(contact_id).await },
14//!         number: async { get_contact_number(contact_id).await },
15//!         is_verified: true
16//!     }).await
17//! }
18//!
19//! ```
20//!
21//! Done! Wasn't is simple, with minimal change our code is transformed into
22//! concurrent code.
23//!
24//! ## Usage
25//!
26//! `conquer_struct` provides use with 2 macros.
27//!
28//! [`conquer_future!`] this macro resolves the futures provided inside the struct.
29//! the macro has a rough signature:
30//!
31//! ```rust
32//!
33//! conquer_future!(StructName {
34//!     value1: T,
35//!     value2: async { .. } // ~ impl Future<Output = V>, considering the field
36//!                         // accepts type V
37//! }) -> impl Future<Output = StructName>
38//!
39//! ```
40//!
41//! [`try_conquer_future!`] this macro resolves the futures provided inside the struct,
42//! as well as consider the result of the future that are present inside.
43//!
44//! ```rust
45//!
46//! try_conquer_future!(StructName {
47//!     value1: T,
48//!     value2: async { .. } // ~ impl Future<Output = Result<V, E>>, consider the field
49//!                         // accepts type V
50//! }) -> impl Future<Output = Result<StructName, E>>
51//!
52//! ```
53//!
54//!
55//! ### Support
56//!
57//! Supported runtime for concurrent execution
58//! - `tokio`
59//!
60
61mod executor;
62mod types;
63
64use proc_macro::TokenStream;
65use syn::parse_macro_input;
66
67///
68/// Macro function to use for resolving the futures concurrently, for fields of struct and
69/// returning the struct itself.
70///
71/// ## Example
72///  Following is a usecase where we are trying to get the some details from some async functions,
73///  but don't wish to write all the boilerplate code for it. `conquer_future` internally uses
74///  `tokio::join` to achieve concurrent execution.
75///
76/// ```
77/// #[derive(PartialEq, Debug)]
78/// struct Contact {
79///     name: String,
80///     phone_no: String,
81///     address: String,
82///
83/// }
84///
85/// #[tokio::main]
86/// async fn main() {
87///     let contact = conquer_struct::conquer_future!(Contact {
88///         name: "John Doe".to_string(),
89///         phone_no: async { get_contact_no().await },
90///         address: async { get_address().await }
91///     }).await;
92///
93///     assert_eq!(contact, Contact { name: "John Doe".to_string(), phone_no:
94///     "1234567890".to_string(), address: "221B Baker Street".to_string() })
95/// }
96/// async fn get_contact_no() -> String { "1234567890".to_string() }
97/// async fn get_address() -> String { "221B Baker Street".to_string() }
98/// ```
99/// ---
100///
101/// In case you haven't provided any async field to the struct within the macro, the macro will
102/// throw a compile time error, not to use the macro.
103/// As the macro internally creates a new `async {  }` block to store the code that is generated,
104/// in case of no async execution this creates a redundant future.
105///  
106/// ```compile_fail
107/// #[derive(PartialEq, Debug)]
108/// struct Contact {
109///     name: String,
110///     phone_no: String,
111///     address: String,
112///
113/// }
114///
115/// #[tokio::main]
116/// async fn main() {
117///     let contact = conquer_struct::conquer_future!(Contact {
118///         name: "John Doe".to_string(),
119///         phone_no: "1234567890".to_string(),
120///         address: "221B Baker Street".to_string(),
121///     }).await;
122/// }
123/// ```
124///
125///
126#[proc_macro]
127pub fn conquer_future(input: TokenStream) -> TokenStream {
128    let container = parse_macro_input!(input as types::OutputStructAbstraction);
129
130    executor::concurrent_struct_constructor(container, false).into()
131}
132
133///
134/// Macro function to use for resolving the futures concurrently, for fields of struct and
135/// returning the struct itself. Additionally, `try_conquer_future` resolve the `Result<T, E>`
136/// which is expected to be yielded by the future.
137///
138/// note: the `E` of the result type should match the `E` on the current scope. Internally, `?` is
139/// used to get the fields.
140///
141///
142/// # Example
143/// similar to `conquer_future`, only difference being it uses `tokio::try_join` internally to try
144/// and resolve the futures and get the `Ok(...)` of the `Result<T, E>` returned by the futures
145/// ```
146/// #[derive(PartialEq, Debug)]
147/// struct Contact {
148///     name: String,
149///     phone_no: String,
150///     address: String,
151///
152/// }
153///
154/// #[tokio::main]
155/// async fn main() -> Result<(), ()> {
156///     let contact = conquer_struct::try_conquer_future!(Contact {
157///         name: "John Doe".to_string(),
158///         phone_no: async { get_contact_no().await },
159///         address: async { get_address().await }
160///     }).await?;
161///
162///     assert_eq!(contact, Contact { name: "John Doe".to_string(), phone_no:
163///     "1234567890".to_string(), address: "221B Baker Street".to_string() });
164///     Ok(())
165/// }
166/// async fn get_contact_no() -> Result<String, ()> { Ok("1234567890".to_string()) }
167/// async fn get_address() -> Result<String, ()> { Ok("221B Baker Street".to_string()) }
168/// ```
169///
170/// ---
171///
172///
173#[proc_macro]
174pub fn try_conquer_future(input: TokenStream) -> TokenStream {
175    let container = parse_macro_input!(input as types::OutputStructAbstraction);
176
177    executor::concurrent_struct_constructor(container, true).into()
178}