use proc_macro2::TokenStream;
use quote::ToTokens;
use std::fmt;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::{parenthesized, token, Ident, Token};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MetaAttrType {
AlignAfter,
AlignBefore,
BigEndian,
Complete,
Cond,
Count,
Debug,
DebugDerive,
Ignore,
InputName,
LittleEndian,
Map,
Parse,
PostExec,
PreExec,
Selector,
SkipAfter,
SkipBefore,
Take,
Value,
Verify,
}
impl MetaAttrType {
pub fn from_ident(ident: &syn::Ident) -> Option<Self> {
match ident.to_string().as_ref() {
"AlignAfter" => Some(MetaAttrType::AlignAfter),
"AlignBefore" => Some(MetaAttrType::AlignBefore),
"BigEndian" => Some(MetaAttrType::BigEndian),
"Complete" => Some(MetaAttrType::Complete),
"Cond" => Some(MetaAttrType::Cond),
"Count" => Some(MetaAttrType::Count),
"Debug" => Some(MetaAttrType::Debug),
"DebugDerive" => Some(MetaAttrType::DebugDerive),
"Default" => Some(MetaAttrType::Ignore),
"If" => Some(MetaAttrType::Cond),
"Ignore" => Some(MetaAttrType::Ignore),
"InputName" => Some(MetaAttrType::InputName),
"LittleEndian" => Some(MetaAttrType::LittleEndian),
"Map" => Some(MetaAttrType::Map),
"Parse" => Some(MetaAttrType::Parse),
"PostExec" => Some(MetaAttrType::PostExec),
"PreExec" => Some(MetaAttrType::PreExec),
"Selector" => Some(MetaAttrType::Selector),
"SkipAfter" => Some(MetaAttrType::SkipAfter),
"SkipBefore" => Some(MetaAttrType::SkipBefore),
"Take" => Some(MetaAttrType::Take),
"Value" => Some(MetaAttrType::Value),
"Verify" => Some(MetaAttrType::Verify),
_ => None,
}
}
pub fn takes_argument(&self) -> bool {
match *self {
MetaAttrType::AlignAfter
| MetaAttrType::AlignBefore
| MetaAttrType::Cond
| MetaAttrType::Count
| MetaAttrType::InputName
| MetaAttrType::Map
| MetaAttrType::Parse
| MetaAttrType::PostExec
| MetaAttrType::PreExec
| MetaAttrType::Selector
| MetaAttrType::SkipAfter
| MetaAttrType::SkipBefore
| MetaAttrType::Take
| MetaAttrType::Value
| MetaAttrType::Verify => true,
_ => false,
}
}
}
impl fmt::Display for MetaAttrType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
MetaAttrType::AlignAfter => "AlignAfter",
MetaAttrType::AlignBefore => "AlignBefore",
MetaAttrType::BigEndian => "BigEndian",
MetaAttrType::Complete => "Complete",
MetaAttrType::Cond => "Cond",
MetaAttrType::Count => "Count",
MetaAttrType::Debug => "Debug",
MetaAttrType::DebugDerive => "DebugDerive",
MetaAttrType::Ignore => "Ignore",
MetaAttrType::InputName => "InputName",
MetaAttrType::LittleEndian => "LittleEndian",
MetaAttrType::Map => "Map",
MetaAttrType::Parse => "Parse",
MetaAttrType::PostExec => "PostExec",
MetaAttrType::PreExec => "PreExec",
MetaAttrType::Selector => "Selector",
MetaAttrType::SkipAfter => "SkipAfter",
MetaAttrType::SkipBefore => "SkipBefore",
MetaAttrType::Take => "Take",
MetaAttrType::Value => "Value",
MetaAttrType::Verify => "Verify",
};
f.write_str(s)
}
}
#[derive(Debug)]
pub struct MetaAttr {
pub attr_type: MetaAttrType,
arg0: Option<TokenStream>,
}
impl MetaAttr {
pub fn new(attr_type: MetaAttrType, arg0: Option<TokenStream>) -> Self {
MetaAttr { attr_type, arg0 }
}
pub fn acceptable_tla(&self) -> bool {
match self.attr_type {
MetaAttrType::DebugDerive
| MetaAttrType::Debug
| MetaAttrType::InputName
| MetaAttrType::LittleEndian
| MetaAttrType::BigEndian
| MetaAttrType::Selector => true,
_ => false,
}
}
pub fn acceptable_fla(&self) -> bool {
match self.attr_type {
MetaAttrType::DebugDerive | MetaAttrType::InputName => false,
_ => true,
}
}
#[inline]
pub fn is_type(&self, attr_type: MetaAttrType) -> bool {
self.attr_type == attr_type
}
#[inline]
pub fn arg(&self) -> Option<&TokenStream> {
self.arg0.as_ref()
}
}
impl fmt::Display for MetaAttr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.attr_type)?;
if let Some(arg) = &self.arg0 {
write!(f, "({})", arg)?;
}
Ok(())
}
}
impl Parse for MetaAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let ident: Ident = input.parse()?;
let attr_type =
MetaAttrType::from_ident(&ident).expect(&format!("Wrong meta name {}", ident));
let arg0 = if attr_type.takes_argument() {
let token_stream = match attr_type {
MetaAttrType::PreExec | MetaAttrType::PostExec => {
parse_content::<syn::Stmt>(input)?
}
MetaAttrType::Selector => parse_content::<syn::Pat>(input)?,
_ => parse_content::<syn::Expr>(input)?,
};
Some(token_stream)
} else {
None
};
Ok(MetaAttr::new(attr_type, arg0))
}
}
fn parse_content<P>(input: ParseStream) -> syn::Result<TokenStream>
where
P: Parse + ToTokens + fmt::Debug,
{
if input.peek(Token![=]) {
let _: Token![=] = input.parse()?;
let x = syn::Lit::parse(input)?;
match x {
syn::Lit::Str(s) => {
let xx: P = s.parse()?;
Ok(quote! { #xx })
}
_ => Err(syn::Error::new(
x.span(),
"Unexpected type for nom attribute content (!LitStr)",
)),
}
} else if input.peek(token::Paren) {
let content;
let _paren_token = parenthesized!(content in input);
let x = P::parse(&content)?;
Ok(quote! { #x })
} else {
Err(syn::Error::new(
input.span(),
"Unexpected type for nom attribute content (!LitStr)",
))
}
}