polybox-codegen 0.0.1

Message-passing abstractions to make working with channels and actors a more seamless experience
Documentation
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{parse::Parse, parse2, Attribute, Error, Generics, Item, Type};

struct DeriveMessage {
    ident: Ident,
    attrs: Vec<Attribute>,
    generics: Generics,
}

impl Parse for DeriveMessage {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        match input.parse::<Item>()? {
            Item::Enum(item) => Ok(DeriveMessage {
                ident: item.ident,
                attrs: item.attrs,
                generics: item.generics,
            }),
            Item::Struct(item) => Ok(DeriveMessage {
                ident: item.ident,
                attrs: item.attrs,
                generics: item.generics,
            }),
            Item::Union(item) => Ok(DeriveMessage {
                ident: item.ident,
                attrs: item.attrs,
                generics: item.generics,
            }),
            item => Err(Error::new_spanned(item, "Must be enum, struct or union")),
        }
    }
}

pub fn derive_message(item: TokenStream) -> Result<TokenStream, Error> {
    let DeriveMessage {
        ident,
        attrs,
        generics,
    } = parse2::<DeriveMessage>(item)?;
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    let msg_type = get_msg_type(&attrs)?;

    Ok(quote! {
        impl #impl_generics ::polybox::messaging::Message for #ident #ty_generics #where_clause {
            type Returned = <#msg_type as ::polybox::messaging::MessageDerive<Self>>::Returned;
            type Payload = <#msg_type as ::polybox::messaging::MessageDerive<Self>>::Payload;
            fn create(self) -> (Self::Payload, Self::Returned) {
                <#msg_type as ::polybox::messaging::MessageDerive<Self>>::create(self)
            }
            fn cancel(payload: Self::Payload, returned: Self::Returned) -> Self {
                <#msg_type as ::polybox::messaging::MessageDerive<Self>>::cancel(payload, returned)
            }
        }
    })
}

pub fn get_msg_type(attrs: &Vec<Attribute>) -> Result<TokenStream, Error> {
    let mut msg_type = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("msg")) {
        let ty = attr.parse_args::<Type>()?;
        Some(quote! { #ty })
    } else {
        None
    };

    if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("request")) {
        if msg_type.is_some() {
            Err(Error::new_spanned(
                attr,
                "Can't have both #[msg(..)] and #[request(..)]",
            ))?
        }
        let ty = attr.parse_args::<Type>()?;
        msg_type = Some(quote! { ::polybox::messaging::Rx<#ty> })
    };

    Ok(msg_type.unwrap_or(quote! { () }))
}