extern crate proc_macro;
use darling::FromAttributes;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields, Type};
#[proc_macro_derive(Interface, attributes(interface))]
pub fn derive_interface_polybox(input: TokenStream) -> TokenStream {
derive_interface(input, "::zestors::interface")
}
#[derive(darling::FromAttributes)]
#[darling(attributes(interface))]
struct InterfaceAttrs {
path: Option<syn::Path>,
}
fn derive_interface(input: TokenStream, base: &str) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let attrs = match InterfaceAttrs::from_attributes(&input.attrs) {
Ok(attrs) => attrs,
Err(err) => return err.write_errors().into(),
};
let enum_name = &input.ident;
let base_path: syn::Path = attrs.path.unwrap_or_else(|| syn::parse_str(base).unwrap());
let msg_path: syn::Path = base_path.clone();
let variants = match &input.data {
Data::Enum(data_enum) => &data_enum.variants,
_ => panic!("Interface derive can only be used on enums"),
};
let mut inner_types = Vec::new();
let mut try_from_matches = Vec::new();
let mut try_into_matches = Vec::new();
let mut into_matches = Vec::new();
let mut from_impls = Vec::new();
for variant in variants {
let variant_name = &variant.ident;
match &variant.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
let field_type = &fields.unnamed[0].ty;
let inner_type = extract_inner_envelope_type(field_type)
.expect("Interface variants must be of type Envelope<T>");
inner_types.push(inner_type);
try_from_matches.push(quote! {
let envelope = match envelope.downcast::<#inner_type>() {
Ok(envelope) => return Ok(Self::#variant_name(envelope)),
Err(envelope) => envelope,
};
});
try_into_matches.push(quote! {
if id == std::any::TypeId::of::<#inner_type>() {
if let Self::#variant_name(envelope) = self {
let converted = unsafe {
std::mem::transmute_copy::<#base_path::Envelope<#inner_type>, #base_path::Envelope<I>>(&envelope)
};
std::mem::forget(envelope);
return Ok(converted);
}
}
});
into_matches.push(quote! {
Self::#variant_name(envelope) => #msg_path::AnyEnvelope::new::<#inner_type>(envelope),
});
from_impls.push(quote! {
impl From<#msg_path::Envelope<#inner_type>> for #enum_name {
fn from(envelope: #msg_path::Envelope<#inner_type>) -> Self {
Self::#variant_name(envelope)
}
}
impl TryInto<#msg_path::Envelope<#inner_type>> for #enum_name {
type Error = Self;
fn try_into(self) -> Result<#msg_path::Envelope<#inner_type>, Self> {
if let #enum_name::#variant_name(envelope) = self {
Ok(envelope)
} else {
Err(self)
}
}
}
});
}
_ => panic!("Interface derive only supports variants with a single unnamed field, e.g., A(Envelope<T>)"),
}
}
let expanded = quote! {
impl #msg_path::Interface for #enum_name {
fn try_from_dyn_envelope(envelope: #msg_path::AnyEnvelope) -> Result<Self, #msg_path::AnyEnvelope> {
#(#try_from_matches)*
Err(envelope)
}
fn into_dyn_envelope(self) -> #msg_path::AnyEnvelope {
match self {
#(#into_matches)*
}
}
type Set = (#(#inner_types,)*);
}
impl #msg_path::Message for #enum_name {
type Output = ();
type Resolver = ();
type Receipt = ();
}
impl From<#msg_path::Envelope<#enum_name>> for #enum_name {
fn from(envelope: #msg_path::Envelope<#enum_name>) -> Self {
envelope.msg
}
}
impl TryInto<#msg_path::Envelope<#enum_name>> for #enum_name {
type Error = Self;
fn try_into(self) -> Result<#msg_path::Envelope<#enum_name>, Self> {
Ok(#msg_path::Envelope::new(self, ()))
}
}
#(#from_impls)*
};
TokenStream::from(expanded)
}
#[proc_macro_derive(HandlerInterface, attributes(interface))]
pub fn derive_actor_interface(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let enum_name = &input.ident;
let attrs = match InterfaceAttrs::from_attributes(&input.attrs) {
Ok(attrs) => attrs,
Err(err) => return err.write_errors().into(),
};
let variants = match &input.data {
Data::Enum(data_enum) => &data_enum.variants,
_ => panic!("HandlerInterface derive can only be used on enums"),
};
let base_path = attrs
.path
.unwrap_or_else(|| syn::parse_str("::zestors").unwrap());
let mut handle_matches = Vec::new();
let mut inner_types = Vec::new();
for variant in variants {
let variant_name = &variant.ident;
match &variant.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
let field_type = &fields.unnamed[0].ty;
let inner_type = extract_inner_envelope_type(field_type)
.expect("HandlerInterface variants must be of type Envelope<T>");
handle_matches.push(quote! {
Self::#variant_name(envelope) => {
<T as #base_path::actor::Handle<#inner_type>>::handle(actor, state, envelope.msg, envelope.req).await
}
});
inner_types.push(inner_type);
}
_ => panic!("HandlerInterface derive only supports variants with a single unnamed field, e.g., A(Envelope<T>)"),
}
}
let expanded = quote! {
impl<T> #base_path::actor::HandlerInterface<T> for #enum_name
where
T: #base_path::actor::Handler + #( #base_path::actor::Handle<#inner_types> + )*
{
async fn handle_with(self, state: #base_path::actor::HandlerContext<'_, T>, actor: &mut T) -> Result<(), ::rootcause::Report> {
match self {
#(#handle_matches)*
}
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_derive(Message, attributes(msg))]
pub fn derive_message(input: TokenStream) -> TokenStream {
_derive_message(input, "::zestors::interface")
}
#[derive(darling::FromAttributes)]
#[darling(attributes(msg))]
struct MessageAttrs {
reply: Option<syn::Type>,
path: Option<syn::Path>,
}
fn _derive_message(input: TokenStream, base: &str) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let attrs = match MessageAttrs::from_attributes(&input.attrs) {
Ok(attrs) => attrs,
Err(err) => return err.write_errors().into(),
};
let name = &input.ident;
let base_path: syn::Path = attrs.path.unwrap_or_else(|| syn::parse_str(base).unwrap());
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let expanded = if let Some(reply_type) = attrs.reply {
quote!(
impl #impl_generics #base_path::Message for #name #ty_generics #where_clause
{
type Output = #reply_type;
type Receipt = #base_path::Reply<#reply_type>;
type Resolver = #base_path::Request<#reply_type>;
}
)
} else {
quote!(
impl #impl_generics #base_path::Message for #name #ty_generics #where_clause
{
type Output = ();
type Resolver = ();
type Receipt = ();
}
)
};
TokenStream::from(expanded)
}
fn extract_inner_envelope_type(ty: &Type) -> Option<&Type> {
if let Type::Path(type_path) = ty {
let segment = type_path.path.segments.last()?;
if segment.ident == "Envelope" {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
return Some(inner_ty);
}
}
}
}
None
}