use syn::{
braced, parenthesized,
parse::{Parse, ParseStream},
punctuated::Punctuated,
token::{Brace, Paren},
Attribute, Generics, Ident, Meta, NestedMeta, Token, Type, Visibility,
};
#[cfg(feature = "prost")]
use syn::Lit;
struct Wrap<T>(pub T);
impl<T: Parse> Parse for Wrap<Punctuated<T, Token![,]>> {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self(input.parse_terminated(T::parse)?))
}
}
pub enum Item {
Struct(ItemStruct),
Enum(ItemEnum),
}
pub enum ItemType {
Struct,
Enum,
}
pub struct ItemStruct {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub struct_token: Token![struct],
pub ident: Ident,
pub generics: Generics,
pub brace_token: Brace,
pub fields: Punctuated<NamedField, Token![,]>,
}
pub struct ItemEnum {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub enum_token: Token![enum],
pub ident: Ident,
pub generics: Generics,
pub brace_token: Brace,
pub variants: Punctuated<SingleTupleVariant, Token![,]>,
}
pub struct NamedField {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub ident: Ident,
pub colon_token: Token![:],
pub ty: Type,
pub is_flatten: bool,
}
pub struct SingleTupleVariant {
pub attrs: Vec<Attribute>,
pub ident: Ident,
pub paren_token: Paren,
pub tuple_attrs: Vec<Attribute>,
pub ty: Type,
}
impl Parse for Item {
fn parse(input: ParseStream) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let vis = input.parse()?;
let lookahead = input.lookahead1();
if lookahead.peek(Token![struct]) {
let content;
Ok(Self::Struct(ItemStruct {
attrs,
vis,
struct_token: input.parse()?,
ident: input.parse()?,
generics: {
let mut generics: Generics = input.parse()?;
generics.where_clause = input.parse()?;
generics
},
brace_token: braced!(content in input),
fields: content.parse_terminated(NamedField::parse)?,
}))
} else if lookahead.peek(Token![enum]) {
let content;
Ok(Self::Enum(ItemEnum {
attrs,
vis,
enum_token: input.parse()?,
ident: input.parse()?,
generics: {
let mut generics: Generics = input.parse()?;
generics.where_clause = input.parse()?;
generics
},
brace_token: braced!(content in input),
variants: content.parse_terminated(SingleTupleVariant::parse)?,
}))
} else {
Err(lookahead.error())
}
}
}
#[derive(PartialEq)]
enum NamedFieldAttribute {
Flatten,
}
impl Parse for NamedFieldAttribute {
fn parse(input: ParseStream) -> syn::Result<Self> {
let meta: NestedMeta = input.parse()?;
match meta {
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("flatten") => Ok(Self::Flatten),
_ => Err(syn::Error::new_spanned(meta, "invalid meta")),
}
}
}
#[derive(PartialEq)]
#[non_exhaustive]
#[cfg(feature = "prost")]
enum ProstFieldAttribute {
OneOf(Lit),
Other,
}
#[cfg(feature = "prost")]
impl Parse for ProstFieldAttribute {
fn parse(input: ParseStream) -> syn::Result<Self> {
let meta: NestedMeta = input.parse()?;
match meta {
NestedMeta::Meta(Meta::NameValue(m)) if m.path.is_ident("oneof") => {
Ok(Self::OneOf(m.lit))
}
_ => Ok(Self::Other),
}
}
}
impl Parse for NamedField {
fn parse(input: ParseStream) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
#[allow(unused_mut)]
let mut is_flatten = attrs
.iter()
.filter(|attr| attr.path.is_ident("fieldmask"))
.map(|attr| attr.parse_args())
.collect::<syn::Result<Vec<_>>>()?
.iter()
.flat_map(|attrs: &Wrap<Punctuated<NamedFieldAttribute, Token![,]>>| &attrs.0)
.any(|meta| *meta == NamedFieldAttribute::Flatten);
#[cfg(feature = "prost")]
{
is_flatten = is_flatten
|| attrs
.iter()
.filter(|attr| attr.path.is_ident("prost"))
.map(|attr| attr.parse_args())
.collect::<syn::Result<Vec<_>>>()?
.iter()
.flat_map(|attrs: &Wrap<Punctuated<ProstFieldAttribute, Token![,]>>| &attrs.0)
.any(|meta| match meta {
ProstFieldAttribute::OneOf(_) => true,
_ => false,
});
}
Ok(NamedField {
attrs,
vis: input.parse()?,
ident: input.parse()?,
colon_token: input.parse()?,
ty: input.parse()?,
is_flatten,
})
}
}
impl Parse for SingleTupleVariant {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
Ok(SingleTupleVariant {
attrs: input.call(Attribute::parse_outer)?,
ident: {
let _vis: Visibility = input.parse()?;
input.parse()?
},
paren_token: parenthesized!(content in input),
tuple_attrs: content.call(Attribute::parse_outer)?,
ty: {
let _vis: Visibility = content.parse()?;
let ty = content.parse()?;
if !content.is_empty() {
let _punt: Token![,] = content.parse()?;
if !content.is_empty() {
return Err(
content.error("there can be at most one item in the tuple variant")
);
}
}
ty
},
})
}
}
pub struct Field<'a> {
pub ident: &'a Ident,
pub ty: &'a Type,
pub is_flatten: bool,
}
pub struct ItemInfo<'a> {
pub item_type: ItemType,
pub ident: &'a Ident,
pub generics: &'a Generics,
pub fields: Vec<Field<'a>>,
}
impl ItemEnum {
pub fn get_info(&self) -> ItemInfo {
let ident = &self.ident;
let generics = &self.generics;
let fields = self
.variants
.iter()
.map(|v| Field {
ident: &v.ident,
ty: &v.ty,
is_flatten: false,
})
.collect::<Vec<_>>();
ItemInfo {
item_type: ItemType::Enum,
ident,
generics,
fields,
}
}
}
impl ItemStruct {
pub fn get_info(&self) -> ItemInfo {
let ident = &self.ident;
let generics = &self.generics;
let fields = self
.fields
.iter()
.map(|f| Field {
ident: &f.ident,
ty: &f.ty,
is_flatten: f.is_flatten,
})
.collect::<Vec<_>>();
ItemInfo {
item_type: ItemType::Struct,
ident,
generics,
fields,
}
}
}
impl Item {
pub fn get_info(&self) -> ItemInfo {
match &self {
Item::Enum(input) => input.get_info(),
Item::Struct(input) => input.get_info(),
}
}
}