wasmbus_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::{Span, TokenStream as TokenStream2};
3use proc_macro_error::{abort, proc_macro_error};
4use quote::{format_ident, quote, ToTokens};
5use syn::{
6    parse::Result as ParseResult, parse_macro_input, spanned::Spanned, Attribute, Fields, Ident,
7    Meta, NestedMeta,
8};
9
10/// extract traits from attribute
11///  `#[services(Piano,Tuba)]` returns vec![ Piano, Tuba ]
12///  items in the vec are syn::Path, and may have more than one path segment,
13///    as in instruments::Piano
14///
15fn attr_traits(attr: &Attribute, key: &str) -> Vec<syn::Path> {
16    let mut traits = Vec::new();
17    if attr.path.is_ident(key) {
18        if let Ok(Meta::List(ref ml)) = attr.parse_meta() {
19            for n in ml.nested.iter() {
20                if let NestedMeta::Meta(Meta::Path(p)) = n {
21                    traits.push(p.clone())
22                }
23            }
24        }
25    }
26    traits
27}
28
29#[allow(dead_code)]
30struct ReceiverDef {
31    attrs: Vec<Attribute>,
32    attrs_span: Span,
33    ident: Ident,
34    ident_span: Span,
35    fields: Fields,
36}
37
38impl syn::parse::Parse for ReceiverDef {
39    fn parse(input: syn::parse::ParseStream) -> ParseResult<Self> {
40        let derive_input: syn::DeriveInput = input.parse()?;
41        let attrs_span = derive_input.span();
42        let syn::DeriveInput { attrs, ident, data, .. } = derive_input;
43        let ident_span = ident.span();
44        let fields = match data {
45            syn::Data::Struct(data) => data.fields,
46            _ => {
47                return Err(syn::Error::new(
48                    ident_span,
49                    "derive macro only works for structs",
50                ))
51            }
52        };
53        Ok(ReceiverDef {
54            attrs,
55            attrs_span,
56            ident,
57            ident_span,
58            fields,
59        })
60    }
61}
62
63#[proc_macro_error]
64#[proc_macro_derive(Actor, attributes(services))]
65pub fn derive_actor(input: TokenStream) -> TokenStream {
66    let actor_receiver = parse_macro_input!(input as ReceiverDef);
67
68    let mut traits = Vec::new();
69    for attr in actor_receiver.attrs.iter() {
70        traits.extend(attr_traits(attr, "services"));
71    }
72    if traits.is_empty() {
73        abort!(
74            actor_receiver.attrs_span,
75            "Missing list of traits. try `#[services(Trait1,Trait2)]`"
76        );
77    }
78    let actor_ident = actor_receiver.ident;
79    let dispatch_impl = gen_dispatch(&traits, &actor_ident);
80
81    let output = quote!(
82
83    // version of the host-actor api
84    pub const HOST_API_VERSION : u32 = 1;
85
86    #[link(wasm_import_module = "wasmbus")]
87    #[cfg(target_arch = "wasm32")]
88    extern "C" {
89        pub fn __guest_response(ptr: *const u8, len: usize);
90        pub fn __guest_error(ptr: *const u8, len: usize);
91        pub fn __guest_request(op_ptr: *const u8, ptr: *const u8);
92    }
93
94    #[no_mangle]
95    pub extern "C" fn __wasmbus_rpc_version() -> u32 {
96        HOST_API_VERSION
97    }
98
99    #[no_mangle]
100    #[cfg(target_arch = "wasm32")]
101    pub extern "C" fn __guest_call(op_len: i32, req_len: i32) -> i32 {
102        use std::slice;
103
104        let buf: Vec<u8> = Vec::with_capacity(req_len as _);
105        let req_ptr = buf.as_ptr();
106
107        let opbuf: Vec<u8> = Vec::with_capacity(op_len as _);
108        let op_ptr = opbuf.as_ptr();
109
110        let (slice, op) = unsafe {
111            __guest_request(op_ptr, req_ptr);
112            (
113                slice::from_raw_parts(req_ptr, req_len as _),
114                slice::from_raw_parts(op_ptr, op_len as _),
115            )
116        };
117        let method = String::from_utf8_lossy(op);
118        let context = wasmbus_rpc::common::Context::default();
119        let actor = #actor_ident ::default();
120        let resp = futures::executor::block_on({
121            wasmbus_rpc::common::MessageDispatch::dispatch(
122                &actor,
123                &context,
124                wasmbus_rpc::common::Message {
125                    method: &method,
126                    arg: std::borrow::Cow::Borrowed(slice),
127                },
128            )
129        });
130        match resp {
131            Ok(data) => {
132                unsafe {
133                    __guest_response(data.as_ptr(), data.len() as _);
134                }
135                1
136            }
137            Err(e) => {
138                let errmsg = format!("Guest call failed for method {}: {}",
139                        &method, e);
140                unsafe {
141                    __guest_error(errmsg.as_ptr(), errmsg.len() as _);
142                }
143                0
144            }
145        }
146    }
147
148       #dispatch_impl
149    ); // end quote
150
151    // struct #actor_ident { #fields }
152    output.into()
153}
154
155#[proc_macro_derive(ActorHealthResponder)]
156pub fn derive_health_responder(input: TokenStream) -> TokenStream {
157    let actor_receiver = parse_macro_input!(input as ReceiverDef);
158    let actor_ident = actor_receiver.ident;
159    let output = quote!(
160
161        #[async_trait]
162        impl Actor for #actor_ident {
163            async fn health_request(
164                &self,
165                ctx: &wasmbus_rpc::common::Context,
166                arg: &wasmbus_rpc::core::HealthCheckRequest,
167            ) -> wasmbus_rpc::error::RpcResult<wasmbus_rpc::core::HealthCheckResponse> {
168                Ok(wasmbus_rpc::core::HealthCheckResponse {
169                    healthy: true,
170                    message: None,
171                })
172            }
173        }
174    ); // end quote
175    output.into()
176}
177
178fn gen_dispatch(traits: &[syn::Path], ident: &Ident) -> TokenStream2 {
179    let mut methods = Vec::new();
180    let mut trait_receiver_impl = Vec::new();
181
182    for path in traits.iter() {
183        let path_str = path.segments.to_token_stream().to_string();
184        let id = format_ident!("{}Receiver", &path_str);
185        methods.push(quote!(
186            #path_str => #id::dispatch(self, ctx, message).await
187        ));
188        trait_receiver_impl.push(quote!(
189            impl #id for #ident { }
190        ));
191    }
192
193    quote!(
194        #[async_trait]
195        impl wasmbus_rpc::common::MessageDispatch for #ident {
196            async fn dispatch(
197                &self,
198                ctx: &wasmbus_rpc::common::Context,
199                message: wasmbus_rpc::common::Message<'_>,
200            ) -> std::result::Result<Vec<u8>, wasmbus_rpc::error::RpcError> {
201                let (trait_name, trait_method) = message
202                    .method
203                    .rsplit_once('.')
204                    .unwrap_or(("_", message.method));
205                let message = wasmbus_rpc::common::Message {
206                    method: trait_method,
207                    arg: message.arg,
208                };
209                match trait_name {
210                   #( #methods, )*
211                    _ => Err(wasmbus_rpc::error::RpcError::MethodNotHandled(
212                            format!("{}.{} - unknown method", trait_name,message.method)))
213                }
214            }
215        }
216
217      #( #trait_receiver_impl )*
218    )
219}
220
221// for providers that do not implement any Service Receivers
222// (for example, HttpServer that sends only)
223// implement MessageDispatch that always return error if we receive rpc
224fn gen_empty_dispatch(ident: &Ident) -> TokenStream2 {
225    quote!(
226        #[async_trait]
227        impl wasmbus_rpc::common::MessageDispatch for #ident {
228            async fn dispatch(&self, _ctx: &wasmbus_rpc::common::Context, message: wasmbus_rpc::common::Message<'_>) -> std::result::Result<Vec<u8>, wasmbus_rpc::error::RpcError> {
229                Err(wasmbus_rpc::error::RpcError::MethodNotHandled(message.method.to_string()))
230            }
231        }
232    )
233}
234
235#[proc_macro_error]
236#[proc_macro_derive(Provider, attributes(services))]
237pub fn derive_provider(input: TokenStream) -> TokenStream {
238    let provider_receiver = parse_macro_input!(input as ReceiverDef);
239
240    let mut traits = Vec::new();
241    for attr in provider_receiver.attrs.iter() {
242        traits.extend(attr_traits(attr, "services"));
243    }
244    let ident = provider_receiver.ident;
245    let output = if traits.is_empty() {
246        gen_empty_dispatch(&ident)
247    } else {
248        gen_dispatch(&traits, &ident)
249    };
250    output.into()
251}