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}