1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
//! # Ut
//!
//! Wraps asynchronous functions in order to make them synchronous based on if a
//! "sync" feature is enabled. This is useful when writting http clients as you
//! can write async methods and wrap them for use in synchronous code bases
//! automatically.
//!
//! # Usage
//! ```toml
//! [dependencies]
//! ut = "0.2.0"
//! ```
//!
//! Then in the crate that you want to have synchronous functions created for you
//! you create a sync feature. When this feature is enabled ut will create
//! synchronous functions on what you have wrapped.
//!
//! you can either: 
//! - Replace your asynchronous function with a synchronous one
//! - Clone your asynchronous function with a synchronous one ending in _blocking
//! - Clone all methods in an impl with synchronous ones
//!
//! # Replacing async functions
//!
//! You can replace your asynchronous function with a synchronous one by doing
//! something like the following:
//!
//!
//! ```rust
//! #[ut::wrap]
//! async fn foo(input: &str) -> String {
//!   format!("I am {} now", input)
//! }
//!
//! fn main() {
//!   let out = foo("sync");
//!   assert_eq!(out, "I am sync now".to_owned())
//! }
//! ```
//!
//! # Cloning async functions 
//!
//! You can clone your asynchronous function with a synchronous one ending in _blocking
//! by doing something like the following:
//!
//!
//! ```
//! #[ut::clone]
//! async fn foo(input: &str) -> String {
//!  format!("I am {} now", input)
//! }
//!
//! let out = foo_blocking("sync");
//! assert_eq!(out, "I am sync now".to_owned())
//! ```
//!
//! # Cloning async methods in implementations
//!
//! You can clone all methods in an impl with synchronous ones by using
//! ut::clone_impl. This is useful when you want to support both
//! async and sync functions in a struct implementation.
//!
//!
//! ```
//! // The original struct
//! #[derive(Default)]
//! pub struct Example {
//!   pub fooers: Fooers,
//! }
//!
//! // You also need to create the struct to place the cloned impls in
//! // This is done so you can choose what structs/impls to clone/wrap
//! // The cloned structs/impls should end in Blocking
//! #[derive(Default)]
//! pub struct ExampleBlocking {
//!   pub fooers: FooersBlocking,
//! }
//!
//! // The original struct with async functions
//! #[derive(Default)]
//! pub struct Fooers;
//!
//! // The blocking struct that we are cloning impls into
//! // You have to create this so you can add custom derives
//! #[derive(Default)]
//! pub struct FooersBlocking;
//!
//! // The async impls that you want to wrap
//! // All methods within this impl must be async
//! #[ut::clone_impl]
//! impl Fooers {
//!   pub async fn foo(&self, input: &str) -> String {
//!     format!("I am {} now", input)
//!   }
//!
//!   pub async fn bar(&self, input: &str) -> String {
//!     format!("I am also {} now", input)
//!   }
//! }
//! let example = ExampleBlocking::default();
//! let out = example.fooers.foo("sync");
//! assert_eq!(out, "I am sync now".to_owned());
//! let out = example.fooers.bar("sync");
//! assert_eq!(out, "I am also sync now".to_owned())
//! ```
//!
//! Currently the wrapping is very naive and simply wraps the function in
//! tokio::main. This is likely more expensive then it needs to be and I hope
//! to make it more efficient later.

use syn;
use quote::quote;
use proc_macro::TokenStream;

/// Wraps an async function in order to make it synchronous
///
/// # Examples
///
/// ```
/// #[ut::wrap]
/// async fn foo(input: &str) -> String {
///  format!("I am {} now", input)
/// }
///
/// let out = foo("sync");
/// assert_eq!(out, "I am sync now".to_owned())
/// ```
#[proc_macro_attribute]
pub fn wrap(_meta: TokenStream, input: TokenStream) -> TokenStream {
  // parse the input stream into our async function
  let func = syn::parse_macro_input!(input as syn::ItemFn);
  // get attributes (docstrings/examples) for our function
  let attrs = &func.attrs;
  // get visibility of function
  let vis = &func.vis;
  // get the name of our function
  let name = &func.sig.ident;
  // get information on the generics to pass
  let generics = &func.sig.generics;
  // get the arguments for our function
  let args = &func.sig.inputs;
  // get our output
  let output = &func.sig.output;
  // get the block of instrutions that are going to be called
  let block = &func.block;
  // cast back to a token stream
  let output = quote!{
    // iterate and add all of our attributes
    #(#attrs)*
    // conditionally add tokio::main if the sync feature is enabled
    #[cfg_attr(feature = "sync", tokio::main)]
    #vis async fn #name #generics(#args) #output { #block }
  };
  output.into()
}

/// Clones an async function in order to make it also synchronous
///
/// This will add _blocking to the name of the function to clone.
///
/// # Examples
///
/// ```
/// #[ut::clone]
/// async fn foo(input: &str) -> String {
///  format!("I am {} now", input)
/// }
///
/// let out = foo_blocking("sync");
/// assert_eq!(out, "I am sync now".to_owned())
/// ```
#[proc_macro_attribute]
pub fn clone(_meta: TokenStream, input: TokenStream) -> TokenStream {
  // parse the input stream into our async function
  let func = syn::parse_macro_input!(input as syn::ItemFn);
  // get attributes (docstrings/examples) for our function
  let attrs = &func.attrs;
  // get visibility of function
  let vis = &func.vis;
  // get the name of our function
  let name = &func.sig.ident;
  // get the name of our cloned function
  let sync_name = syn::Ident::new(&format!("{}_blocking", name), name.span());
  // get information on the generics to pass
  let generics = &func.sig.generics;
  // get the arguments for our function
  let args = &func.sig.inputs;
  // get our output
  let output = &func.sig.output;
  // get the block of instrutions that are going to be called
  let block = &func.block;
  // cast back to a token stream
  let output = quote!{
    // iterate and add all of our attributes
    #(#attrs)*
    // conditionally add tokio::main if the sync feature is enabled
    #vis async fn #name #generics(#args) #output { #block }
    
    // iterate and add all of our attributes
    #(#attrs)*
    // conditionally add tokio::main if the sync feature is enabled
    #[cfg_attr(feature = "sync", tokio::main)]
    #vis async fn #sync_name #generics(#args) #output { #block }
  };
  output.into()
}


/// Clones an group of async functions in an impl to a new sub structure
///
/// This is useful when you want to support both async and sync functions
/// in a struct implementation.
///
/// # Examples
///
/// ```
/// #[derive(Default)]
/// pub struct Example {
///   pub fooers: Fooers,
/// }
///
/// #[derive(Default)]
/// pub struct ExampleBlocking {
///   pub fooers: FooersBlocking,
/// }
///
/// #[derive(Default)]
/// pub struct Fooers;
///
/// #[derive(Default)]
/// pub struct FooersBlocking;
///
/// #[ut::clone_impl]
/// impl Fooers {
///   pub async fn foo(&self, input: &str) -> String {
///     format!("I am {} now", input)
///   }
/// }
///
/// let out = ExampleBlocking::default().fooers.foo("sync");
/// assert_eq!(out, "I am sync now".to_owned())
/// ```
#[proc_macro_attribute]
pub fn clone_impl(_meta: TokenStream, input: TokenStream) -> TokenStream {
  // parse the input stream into our async function
  let imp = syn::parse_macro_input!(input as syn::ItemImpl);
  // get attributes (docstrings/examples) for our function
  let attrs = &imp.attrs;
  // get the methods implemented in this impl
  let items = &imp.items;
  // get the self type for this impl
  let self_ty = match *imp.self_ty {
    syn::Type::Path(path)  =>  path,
    _ => panic!("Only type paths are supported"),
  };
  // build sync name
  let ident = self_ty.path.get_ident().unwrap();
  let sync_name = syn::Ident::new(&format!("{}Blocking", ident), ident.span());
  // get information on the generics to pass
  let generics = &imp.generics;
  // cast back to a token stream
  let output = quote!{
    // iterate and add all of the original async methods
    #(#attrs)*
    #generics 
    impl #self_ty {
      #(#items)*
    }

    // Clone our async methods but wrap them
    impl #sync_name {
      // wrap them to make the synchronous
      #(
        #[ut::wrap]
        #items
      )*
    }  
  };
  output.into()
}