sabi_derive 0.2.0

Derive macros for replication features in sabi
Documentation
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::DeriveInput;

use crate::replicate::{attr, Ctxt};

pub fn derive(input: DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
    let mut base_ident = input.ident.clone();

    let ctxt = Ctxt::new();
    let attr = attr::Container::from_ast(&ctxt, &input);
    ctxt.check()?;

    let mut def = quote! { Self };
    let mut into_def = quote! { self };
    let mut from_def = quote! { def };

    let remote = if let Some(remote_path) = attr.remote {
        let last_segment = remote_path.segments.last().unwrap();
        base_ident = last_segment.ident.clone();

        let replicate_ident = Ident::new(&format!("Replicate{}", base_ident), Span::call_site());

        let remote_ident = &input.ident;
        let remote_ident_str = remote_ident.to_string();

        def = quote! { #replicate_ident };
        into_def = quote! { #replicate_ident(self) };
        from_def = quote! { def.0 };

        Some(quote! {
            #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
            pub struct #replicate_ident(#[serde(with = #remote_ident_str)] pub #remote_path);
        })
    } else {
        None
    };

    let where_clause = input.generics.where_clause.clone();
    let generics = input.generics;

    let (sabi_path, sabi_crate) = match attr.sabi_path {
        Some(path) => (quote! { #path }, None),
        None => (
            quote! { _sabi },
            Some(quote! {
                #[allow(unused_extern_crates,clippy::useless::attribute)]
                extern crate sabi as _sabi;
            }),
        ),
    };

    Ok(quote! {
        #remote

        #[doc(hidden)]
        #[allow(non_upper_case_globals,unused_attributes)]
        const _: () = {
            #sabi_crate

            impl #generics #sabi_path::Replicate for #base_ident #generics #where_clause {
                type Def = #def;
                fn into_def(self) -> Self::Def {
                    #into_def
                }
                fn from_def(def: Self::Def) -> Self {
                    #from_def
                }
            }
        };
    })
}