remote-trait-object-macro 0.4.1

Macro that expands a Rust trait to a remote-trait-object service
Documentation
use proc_macro2::TokenStream as TokenStream2;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::Token;

pub mod dispatcher;
pub mod from_skeleton;
pub mod id;
pub mod proxy;

struct SingleArg<T: Parse> {
    pub arg_name: syn::Ident,
    pub arg_value: T,
}

impl<T: Parse> Parse for SingleArg<T> {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        let arg_name = input.parse()?;
        input.parse::<Token![=]>()?;
        let arg_value = input.parse()?;
        Ok(Self {
            arg_name,
            arg_value,
        })
    }
}

#[derive(Default)]
struct MacroArgsRaw {
    pub serde_format: Option<syn::Path>,
    pub no_proxy: Option<()>,
    pub no_skeleton: Option<()>,
}

struct MacroArgs {
    pub serde_format: syn::Path,
    pub no_proxy: bool,
    pub no_skeleton: bool,
}

impl MacroArgsRaw {
    fn update(&mut self, ts: TokenStream2) -> syn::parse::Result<()> {
        if let Ok(arg) = syn::parse2::<syn::Ident>(ts.clone()) {
            return if arg == quote::format_ident!("no_proxy") {
                if self.no_proxy.replace(()).is_some() {
                    Err(syn::parse::Error::new_spanned(ts, "Duplicated arguments"))
                } else {
                    Ok(())
                }
            } else if arg == quote::format_ident!("no_skeleton") {
                if self.no_skeleton.replace(()).is_some() {
                    Err(syn::parse::Error::new_spanned(ts, "Duplicated arguments"))
                } else {
                    Ok(())
                }
            } else {
                Err(syn::parse::Error::new_spanned(ts, "Unsupported argument"))
            }
        }

        let arg: SingleArg<TokenStream2> = syn::parse2(ts.clone())?;
        if arg.arg_name == quote::format_ident!("serde_format") {
            let value = syn::parse2(arg.arg_value)?;
            if self.serde_format.replace(value).is_some() {
                Err(syn::parse::Error::new_spanned(ts, "Duplicated arguments"))
            } else {
                Ok(())
            }
        } else {
            Err(syn::parse::Error::new_spanned(ts, "Unsupported argument"))
        }
    }

    fn fill_default_values(self) -> MacroArgs {
        MacroArgs {
            serde_format: self
                .serde_format
                .unwrap_or_else(|| syn::parse2(quote! {remote_trait_object::macro_env::DefaultSerdeFormat}).unwrap()),
            no_proxy: self.no_proxy.map(|_| true).unwrap_or(false),
            no_skeleton: self.no_skeleton.map(|_| true).unwrap_or(false),
        }
    }
}

impl Parse for MacroArgsRaw {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        let mut result = MacroArgsRaw::default();
        let args = Punctuated::<syn::Expr, Token![,]>::parse_terminated(input)?;
        for arg in args {
            result.update(quote! {#arg})?;
        }
        Ok(result)
    }
}

pub fn service(args: TokenStream2, input: TokenStream2) -> Result<TokenStream2, TokenStream2> {
    let args: MacroArgsRaw = syn::parse2(args).map_err(|e| e.to_compile_error())?;
    let args = args.fill_default_values();

    let source_trait = match syn::parse2::<syn::ItemTrait>(input.clone()) {
        Ok(x) => x,
        Err(_) => {
            return Err(syn::Error::new_spanned(input, "You can use #[service] only on a trait").to_compile_error())
        }
    };

    let id = id::generate_id(&source_trait, &args)?;
    let dispatcher = dispatcher::generate_dispatcher(&source_trait, &args)?;
    let proxy = proxy::generate_proxy(&source_trait, &args)?;
    let from_skeleton = from_skeleton::generate_from_skeleton(&source_trait, &args)?;

    Ok(quote! {
        #source_trait
        #id
        #dispatcher
        #proxy
        #from_skeleton
    })
}