lain_derive 0.1.2

Derive macros for usage with lain
Documentation

use proc_macro2::TokenStream;
use std::str::FromStr;
use syn::Meta::{List, NameValue};
use syn::NestedMeta::Meta;

#[derive(Default)]
pub struct BitfieldMetadata {
    pub ty: Option<TokenStream>,
    pub ty_bits: usize,
    pub min: u64,
    pub max: u64,
    pub bit_count: usize,
}

pub(crate) fn get_bitfield_limits(
    meta: impl Iterator<Item = Vec<syn::NestedMeta>>,
) -> Option<BitfieldMetadata> {
    let mut bm = BitfieldMetadata::default();

    for meta_items in meta {
        for meta_item in meta_items {
            match meta_item {
                Meta(NameValue(ref m)) if m.ident == "backing_type" => {
                    let lit_str = get_lit_str(&m.lit).unwrap().value();
                    match lit_str.as_ref() {
                        "u8" => {
                            bm.ty_bits = 8;
                        }
                        "u16" => {
                            bm.ty_bits = 16;
                        }
                        "u32" => {
                            bm.ty_bits = 32;
                        }
                        "u64" => {
                            bm.ty_bits = 64;
                        }
                        _ => {
                            panic!("unsupported backing type for bitfield -- must be u8, u16, u32, or u64");
                        }
                    }

                    bm.ty = Some(TokenStream::from_str(&lit_str).unwrap());
                }
                Meta(NameValue(ref m)) if m.ident == "bits" => {
                    let bit_count = get_lit_number(&m.lit).unwrap().value();
                    if bit_count > 64 {
                        panic!("bit count is larger than 64");
                    }

                    bm.bit_count = bit_count as usize;
                    bm.max = 2_u64.pow(bit_count as u32);
                }
                _ => {
                    panic!(
                        "unexpected meta type in get_bitfield_limits -- should be literals only"
                    );
                }
            }
        }
    }

    if (bm.ty.is_some() && bm.bit_count == 0) || (bm.bit_count > 0 && bm.ty.is_none()) {
        panic!("#[bitfield] requires type and bits to be supplied. e.g. #[bitfield(backing_type=u32, bits=10)]")
    }

    if bm.ty.is_some() {
        return Some(bm);
    }

    None
}

pub(crate) fn get_bitfield_metadata(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> {
    get_attribute_metadata("bitfield", &attr)
}

pub(crate) fn get_attribute_metadata(
    ident: &'static str,
    attr: &syn::Attribute,
) -> Option<Vec<syn::NestedMeta>> {
    if attr.path.segments.len() == 1 && attr.path.segments[0].ident == ident {
        match attr.interpret_meta() {
            Some(List(ref meta)) => {
                return Some(meta.nested.iter().cloned().collect());
            }
            _ => return None,
        }
    }

    None
}

pub(crate) fn get_lit_number(lit: &syn::Lit) -> Result<&syn::LitInt, ()> {
    if let syn::Lit::Int(ref lit) = *lit {
        return Ok(lit);
    }
    // TODO: proper errors
    Err(())
}

pub(crate) fn get_lit_str(lit: &syn::Lit) -> Result<&syn::LitStr, ()> {
    if let syn::Lit::Str(ref lit) = *lit {
        return Ok(lit);
    }
    // TODO: proper errors
    Err(())
}

pub(crate) fn get_lit_bool(lit: &syn::Lit) -> Result<&syn::LitBool, ()> {
    if let syn::Lit::Bool(ref lit) = *lit {
        return Ok(lit);
    }
    // TODO: proper errors
    Err(())
}

pub(crate) fn get_fuzzer_metadata(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> {
    get_attribute_metadata("fuzzer", &attr)
}