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 ServerDef {
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 ServerDef {
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 {
43 attrs, ident, data, ..
44 } = derive_input;
45 let ident_span = ident.span();
46 let fields = match data {
47 syn::Data::Struct(data) => data.fields,
48 _ => {
49 return Err(syn::Error::new(
50 ident_span,
51 "derive macro only works for structs",
52 ))
53 }
54 };
55 Ok(ServerDef {
56 attrs,
57 attrs_span,
58 ident,
59 ident_span,
60 fields,
61 })
62 }
63}
64
65#[proc_macro_error]
66#[proc_macro_derive(FrodobufActor, attributes(services))]
67pub fn derive_actor(input: TokenStream) -> TokenStream {
68 let actor_server = parse_macro_input!(input as ServerDef);
69
70 let mut traits = Vec::new();
71 for attr in actor_server.attrs.iter() {
72 traits.extend(attr_traits(attr, "services"));
73 }
74 if traits.is_empty() {
75 abort!(
76 actor_server.attrs_span,
77 "Missing list of traits. try `#[services(Trait1,Trait2)]`"
78 );
79 }
80 let actor_ident = actor_server.ident;
81 let dispatch_impl = gen_dispatch(&traits, &actor_ident);
82 let output = quote!(
83
84 #[link(wasm_import_module = "wapc")]
85 extern "C" {
86 pub fn __guest_response(ptr: *const u8, len: usize);
87 pub fn __guest_error(ptr: *const u8, len: usize);
88 pub fn __guest_request(op_ptr: *const u8, ptr: *const u8);
89 }
90
91 #[no_mangle]
92 pub extern "C" fn __actor_api_version() -> u32 {
93 frodobuf::FRODOBUF_API_VERSION
94 }
95
96 #[no_mangle]
97 pub extern "C" fn __guest_call(op_len: i32, req_len: i32) -> i32 {
98 use std::slice;
99
100 let buf: Vec<u8> = Vec::with_capacity(req_len as _);
101 let req_ptr = buf.as_ptr();
102
103 let opbuf: Vec<u8> = Vec::with_capacity(op_len as _);
104 let op_ptr = opbuf.as_ptr();
105
106 let (slice, op) = unsafe {
107 __guest_request(op_ptr, req_ptr);
108 (
109 slice::from_raw_parts(req_ptr, req_len as _),
110 slice::from_raw_parts(op_ptr, op_len as _),
111 )
112 };
113 let method = String::from_utf8_lossy(op);
114 let context = context::Context::default();
115 let actor = #actor_ident ::default();
116 let resp = futures::executor::block_on({
117 MessageDispatch::dispatch(
118 &actor,
119 &context,
120 Message {
121 method: &method,
122 arg: std::borrow::Cow::Borrowed(slice),
123 },
124 )
125 });
126 match resp {
127 Ok(Message { arg, .. }) => {
128 unsafe {
129 __guest_response(arg.as_ptr(), arg.len() as _);
130 }
131 1
132 }
133 Err(e) => {
134 let errmsg = format!("Guest call failed for method {}: {}",
137 &method, e);
138 unsafe {
139 __guest_error(errmsg.as_ptr(), errmsg.len() as _);
140 }
141 0
142 }
143 }
144 }
145
146 #dispatch_impl
147 ); output.into()
151}
152fn gen_dispatch(traits: &[syn::Path], ident: &Ident) -> TokenStream2 {
159 let mut methods = Vec::new();
160 let mut methods_legacy = Vec::new();
161 let mut trait_server_impl = Vec::new();
162 for path in traits.iter() {
165 let path_str = path.segments.to_token_stream().to_string();
166 let id = format_ident!("{}Server", &path_str);
167 methods.push(quote!(
169 #path_str => #id::dispatch(self, ctx, &message).await
170 ));
171 methods_legacy.push(quote!(
172 match #id::dispatch(self, ctx, &message).await {
173 Err(RpcError::MethodNotHandled(_)) => {}, res => return res, };
176 ));
177 trait_server_impl.push(quote!(
178 impl #id for #ident { }
179 ));
180 }
181
182 quote!(
183 #[async_trait]
184 impl MessageDispatch for #ident {
185 async fn dispatch(
186 &self,
187 ctx: &context::Context<'_>,
188 message: Message<'_>,
189 ) -> Result<Message<'static>, RpcError> {
190 let (trait_name, trait_method) = message
191 .method
192 .rsplit_once('.')
193 .unwrap_or(("_", message.method));
194
195 let message = Message {
196 method: trait_method,
197 arg: message.arg,
198 };
199 match trait_name {
200 #( #methods, )*
201
202 "_" => {
203 #( #methods_legacy )*
205 Err(RpcError::MethodNotHandled(message.method.to_string()))
206 },
207 _ => Err(RpcError::MethodNotHandled(
208 format!("{} - unknown trait", message.method)))
209 }
210 }
211 }
212
213 #( #trait_server_impl )*
214 )
215}
216#[proc_macro_error]
221#[proc_macro_derive(FrodobufProvider, attributes(services))]
222pub fn derive_provider(input: TokenStream) -> TokenStream {
223 let provider_server = parse_macro_input!(input as ServerDef);
224
225 let mut traits = Vec::new();
226 for attr in provider_server.attrs.iter() {
227 traits.extend(attr_traits(attr, "services"));
228 }
229 if traits.is_empty() {
230 abort!(
231 provider_server.attrs_span,
232 "Missing list of traits. try `#[services(Trait1,Trait2)]`"
233 );
234 }
235 let ident = provider_server.ident;
236 let dispatch_impl = gen_dispatch(&traits, &ident);
238 let output = quote!(
239
240 impl wasmcloud_provider_core::CapabilityProvider for #ident {
241 fn configure_dispatch( &self, dispatcher: Box<dyn provider::Dispatcher>,
246 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
247 let mut lock = self.dispatcher.write().unwrap();
248 *lock = Some(dispatcher);
249 Ok(())
250 }
251
252 fn handle_call(
254 &self,
255 actor: &str,
256 op: &str,
257 arg: &[u8],
258 ) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
259 let ctx = &context::Context {
260 actor: Some(actor),
261 ..Default::default()
262 };
263 let response = futures::executor::block_on(MessageDispatch::dispatch(
264 self,
265 &ctx,
266 Message {
267 method: op,
268 arg: std::borrow::Cow::Borrowed(arg),
269 },
270 ))?;
271 Ok(response.arg.to_vec())
272 }
273
274 fn stop(&self) {
280 #ident :: stop(&self);
281 }
282 }
283
284 #dispatch_impl
285
286 );
287 output.into()
288}