196 lines
4.9 KiB
Rust
196 lines
4.9 KiB
Rust
use quote::quote;
|
|
use syn;
|
|
use proc_macro2;
|
|
|
|
use super::attr::{self, NamedIdent};
|
|
|
|
struct Field {
|
|
var: syn::Ident,
|
|
ty: syn::Type,
|
|
is_option: bool,
|
|
attr: attr::FieldAttr
|
|
}
|
|
|
|
impl Field {
|
|
fn from_syn_field(field: &syn::Field) -> Self {
|
|
let attr = attr::FieldAttr::from_ast(field);
|
|
let var = field.ident.clone().unwrap();
|
|
|
|
let mut ty = None;
|
|
let mut is_option = false;
|
|
|
|
if let syn::Type::Path(ty_path) = &field.ty {
|
|
if let Some(inner_ty) = get_option_inner_type(ty_path) {
|
|
ty = Some(inner_ty);
|
|
is_option = true;
|
|
}
|
|
}
|
|
|
|
if !is_option {
|
|
ty = Some(field.ty.clone());
|
|
}
|
|
|
|
Field {
|
|
var,
|
|
ty: ty.unwrap(),
|
|
is_option,
|
|
attr
|
|
}
|
|
}
|
|
|
|
fn gen_field_def(&self) -> proc_macro2::TokenStream {
|
|
let var = &self.var;
|
|
if self.is_option || self.attr.inline() {
|
|
quote! { #var }
|
|
} else {
|
|
if let Some(path) = self.attr.default() {
|
|
quote! { #var: #var.unwrap_or(#path()) }
|
|
} else {
|
|
quote! { #var: #var.ok_or(Error::MissingStatement)? }
|
|
}
|
|
}
|
|
}
|
|
|
|
fn gen_parse_field(&self) -> Option<proc_macro2::TokenStream> {
|
|
if !self.attr.inline() {
|
|
let statements = self.attr.names(&self.var);
|
|
let var = &self.var;
|
|
|
|
Some(quote! {
|
|
#(#statements)|* => {
|
|
if #var.is_some() {
|
|
return Err(Error::DuplicatedStatement);
|
|
}
|
|
#var = Some(pa.read()?);
|
|
}
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn gen_parse_field_inline(&self) -> Option<proc_macro2::TokenStream> {
|
|
if self.attr.inline() {
|
|
let var = &self.var;
|
|
if let Some(path) = self.attr.default() {
|
|
Some(quote! {
|
|
let #var = match pa.peek_char()? {
|
|
'{' | ';' => #path(),
|
|
_ => pa.read()?
|
|
};
|
|
})
|
|
} else {
|
|
Some(quote! {
|
|
let #var = pa.read()?;
|
|
})
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
|
|
}
|
|
|
|
fn gen_decl_var(&self) -> Option<proc_macro2::TokenStream> {
|
|
if !self.attr.inline() {
|
|
let var = &self.var;
|
|
let ty = &self.ty;
|
|
Some(quote! {
|
|
let mut #var: Option<#ty> = None;
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn gen_parse_struct(
|
|
data: &syn::DataStruct,
|
|
gattr: &attr::Global
|
|
) -> proc_macro2::TokenStream
|
|
{
|
|
match &data.fields {
|
|
syn::Fields::Named(fields) => {
|
|
let fields = fields.named.iter().map(|field| Field::from_syn_field(field)).collect();
|
|
gen_parse_struct_named(fields, &gattr)
|
|
},
|
|
syn::Fields::Unnamed(fields) => {
|
|
if fields.unnamed.len() != 1 {
|
|
panic!("Tuple stuct are only supported with one element.");
|
|
}
|
|
gen_parse_struct_unnamed(&gattr)
|
|
},
|
|
syn::Fields::Unit => panic!("Parse derive can not be applied on Unit struct.")
|
|
}
|
|
}
|
|
|
|
fn gen_parse_struct_named(
|
|
fields: Vec<Field>,
|
|
gattr: &attr::Global
|
|
) -> proc_macro2::TokenStream
|
|
{
|
|
let name = gattr.name();
|
|
|
|
let decl_var = fields.iter().filter_map(|f| f.gen_decl_var());
|
|
let mut parse_field = fields.iter().filter_map(|f| f.gen_parse_field()).peekable();
|
|
let parse_field_inline = fields.iter().filter_map(|f| f.gen_parse_field_inline());
|
|
let field_def = fields.iter().map(|f| f.gen_field_def());
|
|
|
|
let parse_block = if let Some(_) = parse_field.peek() {
|
|
quote! {
|
|
pa.read_opening_brace()?;
|
|
|
|
#( #decl_var )*
|
|
|
|
while pa.peek_char()? != '}' {
|
|
match pa.read_word()? {
|
|
#( #parse_field, )*
|
|
_ => {
|
|
pa.discard_statement()?;
|
|
}
|
|
}
|
|
|
|
pa.read_semicolon()?;
|
|
}
|
|
|
|
pa.read_closing_brace()?;
|
|
}
|
|
} else {
|
|
quote! {}
|
|
};
|
|
|
|
quote! {
|
|
#( #parse_field_inline )*
|
|
|
|
#parse_block
|
|
|
|
Ok(#name {
|
|
#( #field_def, )*
|
|
})
|
|
}
|
|
}
|
|
|
|
fn gen_parse_struct_unnamed(gattr: &attr::Global) -> proc_macro2::TokenStream
|
|
{
|
|
let name = gattr.name();
|
|
|
|
quote! {
|
|
Ok(#name(pa.read()?))
|
|
}
|
|
}
|
|
|
|
fn get_option_inner_type(data: &syn::TypePath) -> Option<syn::Type> {
|
|
let segments = &data.path.segments;
|
|
|
|
if segments.len() == 1 && segments[0].ident == "Option" {
|
|
if let syn::PathArguments::AngleBracketed(args) = &segments[0].arguments {
|
|
if args.args.len() == 1 {
|
|
if let syn::GenericArgument::Type(ty) = &args.args[0] {
|
|
return Some(ty.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|