bind-conf/bind_conf_derive/src/gen_struct.rs

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
}