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, )* } ) => {
intercom_attribute!( INTERNAL
$attr,
stringify!( $attr ),
$attr_param,
$params,
$( $name : $type : stringify!( $name), )* );
};
( INTERNAL
$attr:ident,
$sattr:expr,
$attr_param:ident,
$params:ident,
$( $name:ident : $type:ident : $sname:expr, )* ) => {
#[doc = "`" ]
#[doc = $sattr ]
#[doc = "` attribute parameter type." ]
#[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 ) ) )
} )
}
}
#[doc = "`"]
#[doc = $sattr ]
#[doc = "` attribute data model"]
struct $attr( Vec<$attr_param> );
impl ::syn::parse::Parse for $attr {
fn parse( input : ::syn::parse::ParseStream ) -> ::syn::parse::Result<Self> {
fn parse_parens(input: ::syn::parse::ParseStream) -> ::syn::Result<::syn::parse::ParseBuffer> {
let content;
::syn::parenthesized!(content in input);
Ok(content)
}
let params = match parse_parens( &input ) {
Ok( content ) => 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 {
$(
#[doc = "Getter for the `"]
#[doc = $sname ]
#[doc = "` optional argument.\n\n"]
#[doc = "Returns a failure if the parameter was defined multiple times. "]
#[doc = "Otherwise results in an `Option` depending on whether the parameter"]
#[doc = "was defined or not."]
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 ) ) )
}
}
)*
#[doc = "Gets the positional arguments"]
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()
}
}
}
}