teleparse-macros 0.1.0

Proc-macro crate for teleparse
Documentation
use crate::*;

pub fn expand(input: &syn::DeriveInput) -> syn::Result<TokenStream2> {
    let teleparse = crate_ident();
    let ident = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let data = match &input.data {
        syn::Data::Struct(data) => data,
        _ => {
            syn_error!(input, "Node can only be derived for structs")
        }
    };

    let mut from_body = TokenStream2::new();
    let mut field_expr = TokenStream2::new();
    let mut target_ty = TokenStream2::new();

    match &data.fields {
        syn::Fields::Named(fields) => {
            let (ident, ty) = match fields.named.first() {
                Some(syn::Field {
                    ident: Some(id),
                    ty,
                    ..
                }) => (id, ty),
                _ => {
                    return unsupported_empty_field(&data.fields);
                }
            };
            let ty = parse_node_type(ty)?;
            from_body = quote! { Self { #ident, ::core::default::Default::default() } };
            field_expr = quote! { self.#ident };
            target_ty = quote! { #ty };
        }
        syn::Fields::Unnamed(fields) => {
            let ty = match fields.unnamed.first() {
                Some(syn::Field { ty, .. }) => ty,
                _ => {
                    return unsupported_empty_field(&data.fields);
                }
            };
            let ty = parse_node_type(ty)?;

            from_body.extend(quote! { node });
            for _ in 1..fields.unnamed.len() {
                from_body.extend(quote! { , ::core::default::Default::default() });
            }
            from_body = quote! { Self(#from_body) };
            field_expr = quote! { self.0 };
            target_ty = quote! { #ty };
        }
        syn::Fields::Unit => {
            unsupported_empty_field(&data.fields)?;
        }
    }

    let out = quote! {
        #[automatically_derived]
        impl #impl_generics #ident #ty_generics #where_clause {
            pub fn into_inner(self) -> #target_ty {
                #field_expr.value
            }
        }
        #[automatically_derived]
        impl #impl_generics ::core::convert::From<#teleparse::tp::Node<#target_ty>> for #ident #ty_generics #where_clause {
            #[inline]
            fn from(node: #teleparse::tp::Node<#target_ty>) -> Self {
                #from_body
            }
        }
        #[automatically_derived]
        impl #impl_generics ::core::ops::Deref for #ident #ty_generics #where_clause {
            type Target = #target_ty;
            #[inline]
            fn deref(&self) -> &Self::Target {
                &#field_expr.value
            }
        }
        #[automatically_derived]
        impl #impl_generics ::core::ops::DerefMut for #ident #ty_generics #where_clause {
            #[inline]
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut #field_expr.value
            }
        }
    };

    Ok(out)
}

fn parse_node_type(ty: &syn::Type) -> syn::Result<TokenStream2> {
    let path = match ty {
        syn::Type::Path(ty) => &ty.path,
        _ => {
            syn_error!(ty, "Expected a path")
        }
    };

    match path.segments.last() {
        Some(syn::PathSegment {
            arguments: syn::PathArguments::AngleBracketed(inner),
            ..
        }) => {
            let args = &inner.args;
            Ok(quote! { #args })
        }
        _ => {
            syn_error!(path, "Expected Node type with generic")
        }
    }
}

fn unsupported_empty_field<T>(fields: &syn::Fields) -> syn::Result<T> {
    syn_error!(fields, "Must have at least one field to derive Node");
}