use syn::{Path, Lit};
use syn::parse::{Parse, Result, ParseBuffer};
use syn::ext::IdentExt;
use syn::punctuated::Punctuated;
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use crate::{ArgValue, Error};
pub struct MetaArgList {
pub list: Punctuated<MetaArg, Token![,]>,
}
impl Parse for MetaArgList {
fn parse(input: &ParseBuffer) -> Result<Self> {
let content;
parenthesized!(content in input);
let list = Punctuated::parse_terminated(&content)?;
Ok(MetaArgList { list })
}
}
impl ToTokens for MetaArgList {
fn to_tokens(&self, tokens: &mut TokenStream) {
(quote! { ( list ) }).to_tokens(tokens);
}
}
pub enum MetaArg {
Literal(Lit),
Path(Path),
NameValue(MetaArgNameValue),
}
impl Parse for MetaArg {
fn parse(input: &ParseBuffer) -> Result<Self> {
if input.peek2(Token![=]) {
input.parse().map(MetaArg::NameValue)
} else if input.peek(Ident::peek_any)
|| input.peek(Token![::]) && input.peek3(Ident::peek_any)
{
input.parse().map(MetaArg::Path)
} else {
input.parse().map(MetaArg::Literal)
}
}
}
impl ToTokens for MetaArg {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
MetaArg::Literal(lit) => lit.to_tokens(tokens),
MetaArg::Path(path) => path.to_tokens(tokens),
MetaArg::NameValue(meta) => meta.to_tokens(tokens),
}
}
}
pub struct MetaArgNameValue {
pub name: Ident,
pub eq_token: Token![=],
pub value: ArgValue,
}
impl Parse for MetaArgNameValue {
fn parse(input: &ParseBuffer) -> Result<Self> {
let path: Path = input.parse()?;
Ok(MetaArgNameValue {
name: path.get_ident().ok_or(Error::ArgNameMustBeIdent)?.clone(),
eq_token: input.parse()?,
value: input.parse()?,
})
}
}
impl ToTokens for MetaArgNameValue {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.name.to_tokens(tokens);
self.eq_token.to_tokens(tokens);
self.value.to_tokens(tokens);
}
}
impl Parse for ArgValue {
fn parse(input: &ParseBuffer) -> Result<Self> {
if input.peek(Lit) {
input.parse().map(ArgValue::Literal)
} else {
input.parse().map(ArgValue::Type)
}
}
}
impl ToTokens for ArgValue {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
ArgValue::Literal(lit) => lit.to_tokens(tokens),
ArgValue::Type(ty) => ty.to_tokens(tokens),
ArgValue::None => quote! { ! }.to_tokens(tokens),
}
}
}