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() }