use proc_macro2::token_stream::IntoIter as TokenIter; use proc_macro2::{Ident, Literal, TokenStream, TokenTree}; use quote::quote; use crate::util::{expect_punct, is_punct}; pub enum NestedValue { /// `name = ...` Assign(TokenStream), /// `name "literal"` Literal(Literal), /// `name(...)` Group(TokenStream), /// `name ident = ...` KeywordAssign(Ident, TokenStream), } pub enum Nested { /// Unnamed nested attribute, such as a string, /// callback closure, or a lone ident/path /// /// Note: a lone ident will be Named with no value instead Unnamed(TokenStream), /// Named: name ... Named(Ident, NestedValue), /// Unexpected token, Unexpected(TokenStream), } pub struct AttributeParser { inner: TokenIter, } pub struct Empty; impl From for TokenStream { fn from(_: Empty) -> TokenStream { TokenStream::new() } } impl AttributeParser { pub fn new(stream: TokenStream) -> Self { AttributeParser { inner: stream.into_iter(), } } pub fn parsed(&mut self) -> Option> where T: syn::parse::Parse, { let tokens = self.collect_tail(TokenStream::new()); if tokens.is_empty() { return None; } Some(syn::parse2(tokens)) } fn next_tt(&mut self) -> Option { expect_punct(self.inner.next(), ',') } fn collect_tail(&mut self, first: T) -> TokenStream where T: Into, { let mut out = first.into(); while let Some(tt) = self.next_tt() { out.extend(Some(tt)); } out } fn parse_unnamed(&mut self, first: Ident, next: TokenTree) -> Nested { let mut out = TokenStream::from(TokenTree::Ident(first)); out.extend(self.collect_tail(next)); Nested::Unnamed(out.into_iter().collect()) } fn parse_assign(&mut self, name: Ident) -> Nested { let value = self.collect_tail(Empty); Nested::Named(name, NestedValue::Assign(value)) } fn parse_literal(&mut self, name: Ident, lit: Literal) -> Nested { // TODO: Error if there are any tokens following let _ = self.collect_tail(Empty); Nested::Named(name, NestedValue::Literal(lit)) } fn parse_group(&mut self, name: Ident, group: TokenStream) -> Nested { Nested::Named(name, NestedValue::Group(group)) } fn parse_keyword(&mut self, keyword: Ident, name: Ident) -> Nested { let error = expect_punct(self.next_tt(), '='); match error { Some(error) => { let error = self.collect_tail(error); Nested::Unexpected(error) } None => { let value = self.collect_tail(Empty); Nested::Named(keyword, NestedValue::KeywordAssign(name, value)) } } } } impl Iterator for AttributeParser { type Item = Nested; fn next(&mut self) -> Option { let first = self.inner.next()?; let name = match first { TokenTree::Ident(ident) => ident, tt => { let stream = self.collect_tail(tt); return Some(Nested::Unnamed(stream.into_iter().collect())); } }; match self.next_tt() { Some(tt) if is_punct(&tt, '=') => Some(self.parse_assign(name)), Some(TokenTree::Literal(lit)) => Some(self.parse_literal(name, lit)), Some(TokenTree::Group(group)) => Some(self.parse_group(name, group.stream())), Some(TokenTree::Ident(next)) => Some(self.parse_keyword(name, next)), Some(next) => Some(self.parse_unnamed(name, next)), None => Some(Nested::Unnamed(quote!(#name))), } } }