use proc_macro2::Ident;
use quote::quote;
use syn::parse::{Parse, Result};
use syn::{Attribute, Lit, LitInt, Pat, Type};
#[derive(Clone, Copy)]
pub enum Endianness {
Big,
Little,
Native,
}
#[derive(Clone)]
pub struct Attributes {
pub tag_type: Option<Ident>,
pub tag: Option<Pat>,
pub keep_tag: bool,
pub keep_diff: Option<LitInt>,
pub size_type: Option<Ident>,
pub byte_sized: bool,
pub size_is_next: bool,
pub endianness: Endianness,
pub magic: Option<(Ident, Lit)>,
pub skip: bool,
pub context_type: Type,
pub is_context: bool,
}
impl Default for Attributes {
fn default() -> Self {
Attributes {
tag_type: None,
tag: None,
keep_tag: false,
keep_diff: None,
size_type: None,
byte_sized: false,
size_is_next: false,
endianness: Endianness::Native,
magic: None,
skip: false,
context_type: Type::Verbatim(quote! { () }),
is_context: false,
}
}
}
impl Attributes {
pub fn parse(attrs: &Vec<Attribute>) -> Result<Self> {
let mut result = Attributes::default();
result._parse(attrs)?;
Ok(result)
}
fn _parse(&mut self, attrs: &Vec<Attribute>) -> Result<()> {
for attribute in attrs.iter() {
if !attribute.path().is_ident("plod") {
continue;
}
let meta_parser = syn::meta::parser(|meta| {
if meta.path.is_ident("tag") {
let value = Pat::parse_multi(meta.value()?)?;
self.tag = Some(value);
} else if meta.path.is_ident("keep_diff") {
let lit = LitInt::parse(meta.value()?)?;
self.keep_diff = Some(lit);
self.keep_tag = true;
} else if meta.path.is_ident("context") {
self.context_type = Type::parse(meta.value()?)?;
} else if meta.path.is_ident("big_endian") {
self.endianness = Endianness::Big;
} else if meta.path.is_ident("little_endian") {
self.endianness = Endianness::Little;
} else if meta.path.is_ident("native_endian") {
self.endianness = Endianness::Native;
} else if meta.path.is_ident("keep_tag") {
self.keep_tag = true;
} else if meta.path.is_ident("byte_sized") {
self.byte_sized = true;
} else if meta.path.is_ident("size_is_next") {
self.size_is_next = true;
} else if meta.path.is_ident("skip") {
self.skip = true;
} else if meta.path.is_ident("is_context") {
self.is_context = true;
} else if meta.path.is_ident("magic") {
meta.parse_nested_meta(|meta| {
let ident = meta.path.get_ident().ok_or(
meta.error("Magic must be of the form #[plod(magic(<type>=<value>))]"),
)?;
let lit = Lit::parse(meta.value()?)?;
self.magic = Some((ident.clone(), lit));
Ok(())
})?;
} else if meta.path.is_ident("tag_type") {
meta.parse_nested_meta(|meta| {
self.tag_type = meta.path.get_ident().cloned();
Ok(())
})?;
} else if meta.path.is_ident("size_type") {
meta.parse_nested_meta(|meta| {
self.size_type = meta.path.get_ident().cloned();
Ok(())
})?;
} else {
return Err(meta.error("Unsupported plod value"));
}
Ok(())
});
attribute.parse_args_with(meta_parser)?;
}
Ok(())
}
pub fn extend(&self, attrs: &Vec<Attribute>) -> Result<Self> {
let mut result = self.clone();
result.magic = None;
result.is_context = false;
result._parse(attrs)?;
Ok(result)
}
}