Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::Data;
use syn::DataEnum;
use syn::DeriveInput;
use syn::Result;

fn impl_try_from_num(data: &DataEnum) -> Result<TokenStream> {
    let mut branch_token = quote!();
    for variant in &data.variants {
        let var_ident = &variant.ident;
        branch_token = quote! {
            #branch_token
            if (Self::#var_ident as i32).eq(value) {
                return Ok(Self::#var_ident);
            }
        };
    }
    let result = quote! {
        fn try_from(value: &i32) -> Result<Self, Self::Error> {
            #branch_token
            Err(())
        }
    };
    Ok(result)
}

fn impl_try_from_str(data: &DataEnum) -> Result<TokenStream> {
    let mut branch_token = quote!();
    for variant in &data.variants {
        let var_ident = &variant.ident;
        let var_ident_str = var_ident.to_string();
        branch_token = quote! {
            #branch_token
            #var_ident_str => Ok(Self::#var_ident),
        };
    }
    let result = quote! {
        fn try_from(value: &String) -> Result<Self, Self::Error> {
            match value.as_str() {
                #branch_token
                _ => Err(()),
            }
        }
    };
    Ok(result)
}

pub fn expand(input: DeriveInput) -> Result<TokenStream> {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let result_token = match &input.data {
        Data::Struct(_) => unimplemented!(),
        Data::Enum(data) => {
            let try_from_num = impl_try_from_num(data)?;
            let try_from_str = impl_try_from_str(data)?;
            quote! {
                impl #impl_generics TryFrom<&i32> for #ty #ty_generics #where_clause {
                    type Error = ();
                    #try_from_num
                }

                impl #impl_generics TryFrom<&String> for #ty #ty_generics #where_clause {
                    type Error = ();
                    #try_from_str
                }
            }
        }
        Data::Union(_) => unimplemented!(),
    };
    Ok(result_token)
}