bdk_macros/
lib.rs

1// Bitcoin Dev Kit
2// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3//
4// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5//
6// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9// You may not use this file except in accordance with one or both of these
10// licenses.
11
12#[macro_use]
13extern crate quote;
14
15use proc_macro::TokenStream;
16
17use syn::spanned::Spanned;
18use syn::{parse, ImplItemMethod, ItemImpl, ItemTrait, Token};
19
20fn add_async_trait(mut parsed: ItemTrait) -> TokenStream {
21    let output = quote! {
22        #[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
23        #parsed
24    };
25
26    for mut item in &mut parsed.items {
27        if let syn::TraitItem::Method(m) = &mut item {
28            m.sig.asyncness = Some(Token![async](m.span()));
29        }
30    }
31
32    let output = quote! {
33        #output
34
35        #[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
36        #[async_trait(?Send)]
37        #parsed
38    };
39
40    output.into()
41}
42
43fn add_async_method(mut parsed: ImplItemMethod) -> TokenStream {
44    let output = quote! {
45        #[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
46        #parsed
47    };
48
49    parsed.sig.asyncness = Some(Token![async](parsed.span()));
50
51    let output = quote! {
52        #output
53
54        #[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
55        #parsed
56    };
57
58    output.into()
59}
60
61fn add_async_impl_trait(mut parsed: ItemImpl) -> TokenStream {
62    let output = quote! {
63        #[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
64        #parsed
65    };
66
67    for mut item in &mut parsed.items {
68        if let syn::ImplItem::Method(m) = &mut item {
69            m.sig.asyncness = Some(Token![async](m.span()));
70        }
71    }
72
73    let output = quote! {
74        #output
75
76        #[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
77        #[async_trait(?Send)]
78        #parsed
79    };
80
81    output.into()
82}
83
84/// Makes a method or every method of a trait "async" only if the target_arch is "wasm32"
85///
86/// Requires the `async-trait` crate as a dependency whenever this attribute is used on a trait
87/// definition or trait implementation.
88#[proc_macro_attribute]
89pub fn maybe_async(_attr: TokenStream, item: TokenStream) -> TokenStream {
90    if let Ok(parsed) = parse(item.clone()) {
91        add_async_trait(parsed)
92    } else if let Ok(parsed) = parse(item.clone()) {
93        add_async_method(parsed)
94    } else if let Ok(parsed) = parse(item) {
95        add_async_impl_trait(parsed)
96    } else {
97        (quote! {
98            compile_error!("#[maybe_async] can only be used on methods, trait or trait impl blocks")
99        })
100        .into()
101    }
102}
103
104/// Awaits if target_arch is "wasm32", does nothing otherwise
105#[proc_macro]
106pub fn maybe_await(expr: TokenStream) -> TokenStream {
107    let expr: proc_macro2::TokenStream = expr.into();
108    let quoted = quote! {
109        {
110            #[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
111            {
112                #expr
113            }
114
115            #[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
116            {
117                #expr.await
118            }
119        }
120    };
121
122    quoted.into()
123}
124
125/// Awaits if target_arch is "wasm32", uses `tokio::Runtime::block_on()` otherwise
126///
127/// Requires the `tokio` crate as a dependecy with `rt-core` or `rt-threaded` to build on non-wasm32 platforms.
128#[proc_macro]
129pub fn await_or_block(expr: TokenStream) -> TokenStream {
130    let expr: proc_macro2::TokenStream = expr.into();
131    let quoted = quote! {
132        {
133            #[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
134            {
135                tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(#expr)
136            }
137
138            #[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
139            {
140                #expr.await
141            }
142        }
143    };
144
145    quoted.into()
146}