bin_codec_derive 0.1.0

binary encoder decoder with bit field
Documentation
use proc_macro2::TokenStream;
use quote::*;
use syn::*;

pub struct FieldAttr {
    pub bits: Option<TokenStream>,
    pub is_some: Option<TokenStream>,
    pub has_next: Option<TokenStream>,
    pub count: Option<TokenStream>,
    pub value: Option<TokenStream>,
    pub skip: Option<TokenStream>,
    pub before_de: Option<TokenStream>,
    pub after_de: Option<TokenStream>,
    pub context: Option<TokenStream>,
}

impl FieldAttr {
    pub fn from_attrs(attrs: &[Attribute]) -> Self {
        FieldAttr {
            bits: find_in_attrs(attrs, "bits"),
            is_some: find_in_attrs(attrs, "is_some"),
            count: find_in_attrs(attrs, "count"),
            has_next: find_in_attrs(attrs, "has_next"),
            value: find_in_attrs(attrs, "value"),
            skip: find_in_attrs(attrs, "skip"),
            before_de: find_in_attrs(attrs, "before_de"),
            after_de: find_in_attrs(attrs, "after_de"),
            context: find_in_attrs(attrs, "context"),
        }
    }
}

fn find_in_attrs(attrs: &[Attribute], ident: &str) -> Option<TokenStream> {
    for attr in attrs {
        match attr.parse_meta() {
            Ok(meta) => {
                if let Some(lit) = find_in_meta(&meta, ident) {
                    return Some(lit_to_tokenstream(&lit))
                }
            }
            Err(_) => panic!("parse attribute fail: {}", attr.tts.to_string()),
        }
    }
    None
}

fn find_in_meta(meta: &Meta, ident: &str) -> Option<Lit> {
    match meta {
        Meta::Word(ref word) if word == ident => None,
        Meta::NameValue(ref m) if m.ident == ident => Some(m.lit.clone()),
        Meta::List(ref list) => find_in_meta_list(list, ident),
        _ => None,
    }
}

fn find_in_meta_list(list: &MetaList, ident: &str) -> Option<Lit> {
    for meta in list.nested.iter() {
        match meta {
            NestedMeta::Meta(ref meta) => {
                if let Some(lit) = find_in_meta(meta, ident) {
                    return Some(lit);
                }
            }
            NestedMeta::Literal(lit) => {
                if list.ident == ident {
                    return Some(lit.clone());
                }
            }
        }
    }
    None
}

fn lit_to_tokenstream(lit: &Lit) -> TokenStream {
    match lit {
        Lit::Str(litstr) => {
            litstr.value().parse().unwrap_or_else(|_| {
                crate::utils::error(litstr, "parse expr fail")
            })
        }
        _ => quote!(#lit)
    }
}