ggplot-derive 0.1.1

derive(Merge) for GGPlot
Documentation
mod field_kind;

pub use self::field_kind::MergeFieldKind;

use super::*;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote};
use syn::{Data, DataStruct, Field, Fields, GenericArgument, Path, PathArguments, Type, TypePath};

pub fn merge_expand(input: DeriveInput) -> TokenStream {
    let name = input.ident;
    let fields = match input.data {
        Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) => fields.named,
        _ => panic!("enum does not merge-able"),
    };
    let mut parsed = vec![];
    for i in fields {
        match MergeField::parse_field(&i) {
            Some(s) => parsed.push(s),
            None => {
                panic!("field does not merge-able")
            }
        }
    }
    MergeField::builder_pattern(name, &parsed)
}

pub struct MergeField {
    kind: MergeFieldKind,
    field_name: Ident,
}

impl MergeField {
    pub fn builder_pattern(name: Ident, fields: &[Self]) -> TokenStream {
        let getters = fields.iter().map(|field| field.as_getter());
        let setters = fields.iter().map(|field| field.as_setter());
        let builder = fields.iter().map(|field| field.as_builder());
        quote! {
            impl #name {
                #(#getters)*
                #(#setters)*
                #(#builder)*
            }
        }
    }
}

impl MergeField {
    pub fn parse_field(f: &Field) -> Option<Self> {
        let name = f.ident.as_ref()?.to_owned();
        let kind = MergeFieldKind::parse(&f.ty)?;
        Some(Self { kind, field_name: name })
    }
}

impl MergeField {
    fn getter_name(&self) -> Ident {
        let name = format!("get_{}", self.field_name);
        Ident::new(&name, Span::call_site())
    }
    fn getter_type(&self) -> &Type {
        self.kind.as_getter_type()
    }
    pub fn as_getter(&self) -> TokenStream {
        let field_name = &self.field_name;
        let field_ty = &self.getter_type();
        let getter = self.getter_name();

        quote! {
            #[inline]
            #[automatically_derived]
            pub fn #getter(&self) -> #field_ty {
                self.#field_name.to_owned().unwrap_or_default()
            }
        }
    }
}

impl MergeField {
    fn setter_name(&self) -> Ident {
        let name = format!("set_{}", self.field_name);
        Ident::new(&name, Span::call_site())
    }
    fn setter_type(&self) -> &Type {
        self.kind.as_setter_type()
    }
    pub fn as_setter(&self) -> TokenStream {
        let field_name = &self.field_name;
        let field_ty = &self.setter_type();
        let setter = self.setter_name();

        quote! {
            #[inline]
            #[automatically_derived]
            pub fn #setter(&mut self, #field_name: #field_ty) {
                self.#field_name = Some(#field_name)
            }
        }
    }
}

impl MergeField {
    fn builder_name(&self) -> Ident {
        let name = format!("with_{}", self.field_name);
        Ident::new(&name, Span::call_site())
    }
    fn builder_type(&self) -> &Type {
        self.kind.as_builder_type()
    }
    pub fn as_builder(&self) -> TokenStream {
        let field_name = &self.field_name;
        let field_ty = &self.builder_type();
        let builder = self.builder_name();

        quote! {
            #[inline]
            #[automatically_derived]
            #[allow(clippy::needless_update)]
            pub fn #builder(self, #field_name: #field_ty) -> Self {
                Self { #field_name: Some(#field_name), ..self }
            }
        }
    }
}