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
10fn 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 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 ); 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 ); 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
221fn 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}