taikai 0.1.0

kaitai-like serialization and deserialization
use proc_macro2::TokenStream;

use heck::CamelCase;

pub struct Enum {
    pub id: String,
    pub scope: Vec<TokenStream>,
    pub pairs: Vec<(String, String)>
}

impl Enum {
    pub fn name(&self) -> TokenStream {
        syn::parse_str(&self.id.to_camel_case()).unwrap()
    }

    pub fn absolute_final_path(&self) -> TokenStream {
        let path = self.scope.iter();
        let name = self.name();

        quote! {
            #(#path::)* #name
        }
    }

    pub fn variant_name(&self, variant: &str) -> TokenStream {
        let variant = variant.to_camel_case();
        let variant: TokenStream = syn::parse_str(&variant).unwrap();
        let typ = self.name();
        quote!( #typ::#variant )
    }

    pub fn match_from_primitive(&self, read: TokenStream) -> TokenStream
    {
        let pairs: (Vec<TokenStream>, Vec<syn::Expr>);
        pairs = self.pairs.iter().map(|(key, value)| {
            let key = self.variant_name(key);
            let value: syn::Expr = syn::parse_str(&value).unwrap();

            (key, value)
        }).unzip();
        let (key, value) = pairs;

        quote!(
            {
                let _v = match #read {
                    #(#value => Ok(#key), )*
                    _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidData,
                             "Invalid value for enum")),
                }?;

                Ok((_input, _v))
            }
        )
    }

    pub fn match_to_primitive(&self, variant: TokenStream) -> TokenStream
    {
        let pairs: (Vec<TokenStream>, Vec<syn::Expr>);
        pairs = self.pairs.iter().map(|(key, value)| {
            let key = self.variant_name(key);
            let value: syn::Expr = syn::parse_str(&value).unwrap();

            (key, value)
        }).unzip();
        let (key, value) = pairs;

        quote!(
            match #variant {
                #(#key => #value, )*
            }
        )
    }
}

impl std::hash::Hash for Enum {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        let scope = format!("{:?}", self.scope);

        self.id.hash(state);
        scope.hash(state);
    }
}

impl PartialEq for Enum {
    fn eq(&self, other: &Self) -> bool {
        let self_scope = format!("{:?}", self.scope);
        let other_scope = format!("{:?}", other.scope);

        self.id == other.id && self_scope == other_scope
    } 
}

impl Eq for Enum {}