use proc_macro2::TokenStream;
use proc_macro_error::emit_error;
use quote::{ToTokens, TokenStreamExt};
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::{token, Attribute, Expr, Ident, Token, Type, Visibility};
#[derive(Debug)]
pub enum StructStyle {
Unit(Token![;]),
Tuple(token::Paren, Token![;]),
Regular(token::Brace),
}
#[derive(Debug)]
pub enum Fields {
Named(FieldsNamed),
Unnamed(FieldsUnnamed),
Unit,
}
#[derive(Debug)]
pub struct FieldsNamed {
pub brace_token: token::Brace,
pub fields: Punctuated<Field, Token![,]>,
}
#[derive(Debug)]
pub struct FieldsUnnamed {
pub paren_token: token::Paren,
pub fields: Punctuated<Field, Token![,]>,
}
#[derive(Debug)]
pub struct Field {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub ident: Option<Ident>,
pub colon_token: Option<Token![:]>,
pub ty: Type,
pub assign: Option<(Token![=], Expr)>,
}
pub(crate) mod parsing {
use super::*;
use syn::ext::IdentExt;
use syn::{braced, parenthesized, WhereClause};
impl Parse for FieldsNamed {
fn parse(input: ParseStream) -> Result<Self> {
let content;
let brace_token = braced!(content in input);
Ok(FieldsNamed {
brace_token,
fields: content.parse_terminated(Field::parse_named)?,
})
}
}
impl Parse for FieldsUnnamed {
fn parse(input: ParseStream) -> Result<Self> {
let content;
let paren_token = parenthesized!(content in input);
Ok(FieldsUnnamed {
paren_token,
fields: content.parse_terminated(Field::parse_unnamed)?,
})
}
}
impl Field {
pub fn parse_named(input: ParseStream) -> Result<Self> {
Ok(Field {
attrs: input.call(Attribute::parse_outer)?,
vis: input.parse()?,
ident: Some(if input.peek(Token![_]) {
input.call(Ident::parse_any)
} else {
input.parse()
}?),
colon_token: Some(input.parse()?),
ty: input.parse()?,
assign: if input.peek(Token![=]) {
Some((input.parse()?, input.parse()?))
} else {
None
},
})
}
pub fn parse_unnamed(input: ParseStream) -> Result<Self> {
Ok(Field {
attrs: input.call(Attribute::parse_outer)?,
vis: input.parse()?,
ident: None,
colon_token: None,
ty: input.parse()?,
assign: if input.peek(Token![=]) {
Some((input.parse()?, input.parse()?))
} else {
None
},
})
}
}
pub(crate) fn data_struct(
input: ParseStream,
) -> Result<(Option<WhereClause>, Fields, Option<Token![;]>)> {
let mut lookahead = input.lookahead1();
let mut where_clause = None;
if lookahead.peek(Token![where]) {
where_clause = Some(input.parse()?);
lookahead = input.lookahead1();
}
if where_clause.is_none() && lookahead.peek(token::Paren) {
let fields = input.parse()?;
lookahead = input.lookahead1();
if lookahead.peek(Token![where]) {
where_clause = Some(input.parse()?);
lookahead = input.lookahead1();
}
if lookahead.peek(Token![;]) {
let semi = input.parse()?;
Ok((where_clause, Fields::Unnamed(fields), Some(semi)))
} else {
Err(lookahead.error())
}
} else if lookahead.peek(token::Brace) {
let fields = parse_braced(input)?;
Ok((where_clause, Fields::Named(fields), None))
} else if lookahead.peek(Token![;]) {
let semi = input.parse()?;
Ok((where_clause, Fields::Unit, Some(semi)))
} else {
Err(lookahead.error())
}
}
fn parse_braced(input: ParseStream) -> Result<FieldsNamed> {
let content;
let brace_token = braced!(content in input);
let fields = content.parse_terminated(Field::parse_named)?;
Ok(FieldsNamed {
brace_token,
fields,
})
}
}
mod printing {
use super::*;
impl ToTokens for FieldsNamed {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.brace_token.surround(tokens, |tokens| {
self.fields.to_tokens(tokens);
});
}
}
impl ToTokens for FieldsUnnamed {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.paren_token.surround(tokens, |tokens| {
self.fields.to_tokens(tokens);
});
}
}
impl ToTokens for Field {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(&self.attrs);
self.vis.to_tokens(tokens);
if let Some(ident) = &self.ident {
ident.to_tokens(tokens);
self.colon_token.unwrap_or_default().to_tokens(tokens);
}
self.ty.to_tokens(tokens);
if let Some(ref assign) = self.assign {
emit_error!(
assign.0,
"default value on `struct` field in output";
help = "did you mean to use the `#[impl_default]` attribute?",
);
}
}
}
}