use syn::parse::{Parse, ParseStream, Result};
#[derive(Debug)]
pub enum NoParams {}
impl Parse for NoParams {
fn parse( input: ParseStream ) -> Result<Self> {
Err( input.error( "Attribute does not accept positional parameters" ) )
}
}
#[derive(Debug)]
pub enum StrOption {
Str( ::syn::LitStr ),
None
}
impl Parse for StrOption {
fn parse( input: ParseStream ) -> Result<Self> {
if input.peek( ::syn::LitStr ) {
return Ok( StrOption::Str( input.parse()? ) );
}
let ident : ::syn::Ident = input.parse()?;
if ident == "None" {
return Ok( StrOption::None );
}
Err( input.error( "Expected string or `None`" ) )
}
}
macro_rules! intercom_attribute {
( $attr:ident < $attr_param:ident, $params:ident > { $( $name:ident : $type:ident, )* } ) => {
#[allow(non_camel_case_types)]
#[derive(Debug)]
enum $attr_param {
$( $name ( $type ), )*
args( $params ),
}
impl ::syn::parse::Parse for $attr_param {
fn parse( input : ::syn::parse::ParseStream ) -> ::syn::parse::Result<Self> {
if ! input.peek2( Token![=] ) {
return Ok( $attr_param::args( input.parse()? ) );
}
let ident : ::syn::Ident = input.parse()?;
let _punct : Token![=] = input.parse()?;
Ok( match ident.to_string().as_ref() {
$(
stringify!( $name ) => $attr_param::$name( input.parse()? ),
)*
other => return Err( input.error( format!( "Unexpected parameter: `{}`", other ) ) )
} )
}
}
struct $attr( Vec<$attr_param> );
impl ::syn::parse::Parse for $attr {
fn parse( input : ::syn::parse::ParseStream ) -> ::syn::parse::Result<Self> {
let params = match ::syn::group::parse_parens( &input ) {
Ok( parens ) => parens.content.call(
::syn::punctuated::Punctuated::<$attr_param, ::syn::token::Comma>::parse_terminated )?,
Err( _ ) => input.call(
::syn::punctuated::Punctuated::<$attr_param, ::syn::token::Comma>::parse_terminated )?,
};
Ok( $attr( params.into_iter().collect() ) )
}
}
#[allow(dead_code)]
impl $attr {
$(
pub fn $name( &self ) -> Result< Option< &$type >, String >
{
let v = self.0.iter().filter_map( |p| match p {
$attr_param::$name( v ) => Some( v ),
_ => None
} ).take( 2 ).collect::<Vec<_>>();
match v.len() {
0 => Ok( None ),
1 => Ok( Some( &v[0] ) ),
_ => Err( format!(
"Multiple {} arguments", stringify!( $name ) ) )
}
}
)*
pub fn args<'a>( &'a self ) -> Vec<&'a $params>
{
self.0.iter().filter_map( |p| match p {
$attr_param::args( v ) => Some( v ),
_ => None
} ).collect()
}
}
}
}