1extern crate proc_macro;
28use darling::FromAttributes;
29use proc_macro::TokenStream;
30use proc_macro2::Span;
31use quote::quote;
32use syn::{parse_macro_input, Data, DeriveInput, Fields, Type};
33
34#[proc_macro_derive(Interface, attributes(zestors))]
53pub fn derive_interface_polybox(input: TokenStream) -> TokenStream {
54 derive_interface(input)
55}
56
57#[derive(darling::FromAttributes)]
60#[darling(attributes(zestors, msg))]
61struct Attrs {
62 interface_path: Option<syn::Path>,
63 distr_path: Option<syn::Path>,
64 actor_path: Option<syn::Path>,
65 reply: Option<syn::Type>,
66 id: Option<syn::LitStr>,
67 no_auto_register: darling::util::Flag,
68}
69
70impl Attrs {
71 fn interface_path(&self) -> syn::Path {
72 self.interface_path
73 .clone()
74 .unwrap_or_else(|| syn::parse_str("::zestors::interface").unwrap())
75 }
76
77 fn distr_path(&self) -> syn::Path {
78 self.distr_path
79 .clone()
80 .unwrap_or_else(|| syn::parse_str("::zestors::distr").unwrap())
81 }
82
83 fn actor_path(&self) -> syn::Path {
84 self.actor_path
85 .clone()
86 .unwrap_or_else(|| syn::parse_str("::zestors::actor").unwrap())
87 }
88}
89
90fn derive_interface(input: TokenStream) -> TokenStream {
91 let input = parse_macro_input!(input as DeriveInput);
92 let attrs = match Attrs::from_attributes(&input.attrs) {
93 Ok(attrs) => attrs,
94 Err(err) => return err.write_errors().into(),
95 };
96 let enum_name = &input.ident;
97
98 let base_path: syn::Path = attrs.interface_path();
99 let msg_path: syn::Path = base_path.clone();
100
101 let variants = match &input.data {
103 Data::Enum(data_enum) => &data_enum.variants,
104 _ => panic!("Interface derive can only be used on enums"),
105 };
106
107 let mut inner_types = Vec::new();
108 let mut try_from_matches = Vec::new();
109 let mut try_into_matches = Vec::new();
110 let mut into_matches = Vec::new();
111 let mut from_impls = Vec::new();
112
113 for variant in variants {
114 let variant_name = &variant.ident;
115
116 match &variant.fields {
117 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
118 let field_type = &fields.unnamed[0].ty;
119
120 let inner_type = extract_inner_envelope_type(field_type)
121 .expect("Interface variants must be of type Envelope<T>");
122
123 inner_types.push(inner_type);
124
125 try_from_matches.push(quote! {
126 let envelope = match envelope.downcast::<#inner_type>() {
127 Ok(envelope) => return Ok(Self::#variant_name(envelope)),
128 Err(envelope) => envelope,
129 };
130 });
131
132 try_into_matches.push(quote! {
133 if id == std::any::TypeId::of::<#inner_type>() {
134 if let Self::#variant_name(envelope) = self {
135 let converted = unsafe {
137 std::mem::transmute_copy::<#base_path::Envelope<#inner_type>, #base_path::Envelope<I>>(&envelope)
138 };
139 std::mem::forget(envelope);
140 return Ok(converted);
141 }
142 }
143 });
144
145 into_matches.push(quote! {
146 Self::#variant_name(envelope) => #msg_path::AnyEnvelope::new::<#inner_type>(envelope),
147 });
148
149 from_impls.push(quote! {
150 impl From<#msg_path::Envelope<#inner_type>> for #enum_name {
151 fn from(envelope: #msg_path::Envelope<#inner_type>) -> Self {
152 Self::#variant_name(envelope)
153 }
154 }
155
156 impl TryInto<#msg_path::Envelope<#inner_type>> for #enum_name {
157 type Error = Self;
158
159 fn try_into(self) -> Result<#msg_path::Envelope<#inner_type>, Self> {
160 if let #enum_name::#variant_name(envelope) = self {
161 Ok(envelope)
162 } else {
163 Err(self)
164 }
165 }
166 }
167 });
168 }
169 _ => panic!("Interface derive only supports variants with a single unnamed field, e.g., A(Envelope<T>)"),
170 }
171 }
172
173 let expanded = quote! {
174 impl #msg_path::Interface for #enum_name {
175 fn try_from_dyn_envelope(envelope: #msg_path::AnyEnvelope) -> Result<Self, #msg_path::AnyEnvelope> {
176 #(#try_from_matches)*
177 Err(envelope)
178 }
179
180 fn into_dyn_envelope(self) -> #msg_path::AnyEnvelope {
188 match self {
189 #(#into_matches)*
190 }
191 }
192
193 type Set = (#(#inner_types,)*);
194 }
195
196
197 impl #msg_path::Message for #enum_name {
198 type Output = ();
199 type Kind = #msg_path::Cast;
200 }
201
202
203 impl From<#msg_path::Envelope<#enum_name>> for #enum_name {
204 fn from(envelope: #msg_path::Envelope<#enum_name>) -> Self {
205 envelope.msg
206 }
207 }
208
209 impl TryInto<#msg_path::Envelope<#enum_name>> for #enum_name {
210 type Error = Self;
211
212 fn try_into(self) -> Result<#msg_path::Envelope<#enum_name>, Self> {
213 Ok(#msg_path::Envelope::new(self, ()))
214 }
215 }
216
217 #(#from_impls)*
218 };
219
220 TokenStream::from(expanded)
221}
222
223#[proc_macro_derive(HandlerInterface, attributes(zestors))]
262pub fn derive_actor_interface(input: TokenStream) -> TokenStream {
263 let input = parse_macro_input!(input as DeriveInput);
264 let enum_name = &input.ident;
265 let attrs = match Attrs::from_attributes(&input.attrs) {
266 Ok(attrs) => attrs,
267 Err(err) => return err.write_errors().into(),
268 };
269
270 let variants = match &input.data {
272 Data::Enum(data_enum) => &data_enum.variants,
273 _ => panic!("HandlerInterface derive can only be used on enums"),
274 };
275
276 let actor_path = attrs.actor_path();
277
278 let mut handle_matches = Vec::new();
279 let mut inner_types = Vec::new();
280
281 for variant in variants {
282 let variant_name = &variant.ident;
283
284 match &variant.fields {
285 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
286 let field_type = &fields.unnamed[0].ty;
287
288 let inner_type = extract_inner_envelope_type(field_type)
289 .expect("HandlerInterface variants must be of type Envelope<T>");
290
291 handle_matches.push(quote! {
292 Self::#variant_name(envelope) => {
293 <T as #actor_path::Handle<#inner_type>>::handle(actor, state, envelope.msg, envelope.req).await
294 }
295 });
296 inner_types.push(inner_type);
297 }
298 _ => panic!("HandlerInterface derive only supports variants with a single unnamed field, e.g., A(Envelope<T>)"),
299 }
300 }
301
302 let expanded = quote! {
303 impl<T> #actor_path::HandlerInterface<T> for #enum_name
304 where
305 T: #actor_path::Handler + #( #actor_path::Handle<#inner_types> + )*
306 {
307 async fn handle_with(self, state: #actor_path::HandlerContext<'_, T>, actor: &mut T) -> Result<(), ::rootcause::Report> {
308 match self {
309 #(#handle_matches)*
310 }
311 }
312 }
313 };
314
315 TokenStream::from(expanded)
316}
317
318#[proc_macro_derive(Message, attributes(msg, zestors))]
342pub fn derive_message(input: TokenStream) -> TokenStream {
343 let input = parse_macro_input!(input as DeriveInput);
344 let attrs = match Attrs::from_attributes(&input.attrs) {
345 Ok(attrs) => attrs,
346 Err(err) => return err.write_errors().into(),
347 };
348 let name = &input.ident;
349
350 let base_path: syn::Path = attrs.interface_path();
351 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
352
353 let expanded = if let Some(reply_type) = attrs.reply {
354 quote!(
355 impl #impl_generics #base_path::Message for #name #ty_generics #where_clause
356 {
357 type Output = #reply_type;
358 type Kind = #base_path::Call;
359 }
360 )
361 } else {
362 quote!(
363 impl #impl_generics #base_path::Message for #name #ty_generics #where_clause
364 {
365 type Output = ();
366 type Kind = #base_path::Cast;
367 }
368 )
369 };
370
371 TokenStream::from(expanded)
372}
373
374#[proc_macro_derive(StableId, attributes(msg, zestors))]
399pub fn derive_message_id(input: TokenStream) -> TokenStream {
400 let input = parse_macro_input!(input as DeriveInput);
401 let attrs = match Attrs::from_attributes(&input.attrs) {
402 Ok(attrs) => attrs,
403 Err(err) => return err.write_errors().into(),
404 };
405 let name = &input.ident;
406 let distr_path = attrs.distr_path();
407
408 let Some(id) = attrs.id else {
409 let suggestion = uuid::Uuid::new_v4();
410 return syn::Error::new_spanned(
411 name,
412 format!(
413 "`StableId` requires a unique id; add this attribute to the type: #[msg(id = \"{suggestion}\")]"
414 ),
415 )
416 .to_compile_error()
417 .into();
418 };
419
420 let uuid = match uuid::Uuid::parse_str(&id.value()) {
421 Ok(uuid) => uuid.as_u128(),
422 Err(err) => {
423 return syn::Error::new(Span::call_site(), format!("invalid message id: {err}"))
424 .to_compile_error()
425 .into();
426 }
427 };
428
429 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
430
431 let auto_register = (input.generics.params.is_empty() && !attrs.no_auto_register.is_present())
433 .then(|| quote!(#distr_path::__auto_register!(#name);));
434
435 TokenStream::from(quote! {
436 impl #impl_generics #distr_path::StableId for #name #ty_generics #where_clause {
437 const Id: #distr_path::MessageId = #distr_path::MessageId::from_u128(#uuid);
438 }
439 #auto_register
440 })
441}
442
443fn extract_inner_envelope_type(ty: &Type) -> Option<&Type> {
444 if let Type::Path(type_path) = ty {
445 let segment = type_path.path.segments.last()?;
446 if segment.ident == "Envelope" {
447 if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
448 if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
449 return Some(inner_ty);
450 }
451 }
452 }
453 }
454 None
455}