pmhelp-internal-macros 0.0.2

Internal macros that will be used by `pmhelp`. This should not be used directly.
Documentation
use pmhelp::parse::token_stream::comma_separated;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
    parse::{Parse, ParseStream, Result as ParseResult},
    punctuated::Punctuated,
    Attribute, Data, DataEnum, DataStruct, DeriveInput, Expr, Fields, FieldsNamed, FieldsUnnamed,
    Ident, Token, Type,
};

enum Implementation {
    ParseStream,
    TokenStream,
}
impl Implementation {
    fn get_type(&self) -> TokenStream {
        match self {
            Self::ParseStream => quote! {::syn::parse::ParseStream<'_>},
            Self::TokenStream => quote! {::proc_macro2::TokenStream},
        }
    }

    fn get_error(&self) -> TokenStream {
        match self {
            Self::ParseStream => quote! {::syn::parse::Error},
            Self::TokenStream => quote! {::syn::parse::Error},
        }
    }

    fn get_method_body(&self) -> TokenStream {
        match self {
            Self::ParseStream => quote! {from.parse()},
            Self::TokenStream => quote! {::syn::parse2(from)},
        }
    }

    #[inline]
    fn parts(&self) -> (TokenStream, TokenStream, TokenStream) {
        (self.get_type(), self.get_error(), self.get_method_body())
    }

    #[inline]
    fn all() -> Vec<Self> {
        vec![Self::TokenStream, Self::ParseStream]
    }
}

impl Parse for Implementation {
    fn parse(stream: ParseStream) -> ParseResult<Self> {
        Ok(Self::from(stream.parse::<Ident>()?))
    }
}

impl From<Ident> for Implementation {
    fn from(ident: Ident) -> Self {
        if ident == "TokenStream" {
            Self::TokenStream
        } else if ident == "ParseStream" {
            Self::ParseStream
        } else {
            panic!("Invalid TryFrom Implementation: {}", ident)
        }
    }
}

fn get_types_to_implement(attrs: Vec<Attribute>) -> Vec<Implementation> {
    for attr in attrs {
        if attr.path.is_ident("try_from_parsable") && !attr.tokens.is_empty() {
            return comma_separated::<Implementation>(attr.tokens)
                .unwrap()
                .into_iter()
                .collect();
        }
    }
    Implementation::all()
}

pub fn derive_try_from_parsable(input: DeriveInput) -> TokenStream {
    let name = input.ident;
    let (impl_g, type_g, maybe_where) = input.generics.split_for_impl();
    let mut out = TokenStream::new();
    for (ty, error, body) in get_types_to_implement(input.attrs)
        .iter()
        .map(Implementation::parts)
    {
        out.extend(quote! {
            impl #impl_g ::std::convert::TryFrom<#ty> for #name {
                type Error = #error;

                fn try_from(from: #ty) -> ::std::result::Result<Self, Self::Error> {
                    #body
                }
            }
        });
    }
    out
}