| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License.. |
| |
| //! |
| //! The mod implements the function of Serializable. |
| //! |
| |
| use syn::{self, Ident}; |
| |
| use quote::Tokens; |
| |
| use crate::internals::ast::{Body, Container, Field, Style, Variant}; |
| use crate::internals::{Ctxt}; |
| use crate::param::Parameters; |
| use crate::fragment::{Fragment, Stmts}; |
| |
| pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<Tokens, String> { |
| let ctxt = Ctxt::new(); |
| let cont = Container::from_ast(&ctxt, input); |
| ctxt.check()?; |
| |
| let ident = &cont.ident; |
| let params = Parameters::new(&cont); |
| let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl(); |
| |
| let body = Stmts(serialize_body(&cont, ¶ms)); |
| |
| let impl_block = quote! { |
| impl #impl_generics ::sgx_serialize::Serializable for #ident #ty_generics #where_clause { |
| fn encode<__S: ::sgx_serialize::Encoder>(&self, __arg_0: &mut __S) |
| -> ::std::result::Result<(), __S::Error> { |
| #body |
| } |
| } |
| }; |
| |
| Ok(impl_block) |
| } |
| |
| fn serialize_body(cont: &Container, params: &Parameters) -> Fragment { |
| |
| match cont.body { |
| Body::Enum(ref variants) => { |
| serialize_enum(cont, params, variants) |
| } |
| Body::Struct(Style::Struct, ref fields) => { |
| if fields.iter().any(|field| field.ident.is_none()) { |
| panic!("struct has unnamed fields"); |
| } |
| serialize_struct(cont, fields) |
| } |
| Body::Struct(Style::Tuple, ref fields) => { |
| if fields.iter().any(|field| field.ident.is_some()) { |
| panic!("tuple struct has named fields"); |
| } |
| serialize_tuple_struct(cont, fields) |
| } |
| Body::Struct(Style::Newtype, ref fields) => { |
| if fields.iter().any(|field| field.ident.is_some()) { |
| panic!("newtype struct has named fields"); |
| } |
| serialize_newtype_struct(cont) |
| } |
| Body::Struct(Style::Unit, _) => { |
| serialize_unit_struct(cont) |
| } |
| } |
| } |
| |
| fn fromat_ident(name: &syn::Ident) -> syn::Ident { |
| let mut name_str = String::from("\""); |
| name_str.push_str(name.clone().as_ref()); |
| name_str.push_str("\""); |
| syn::Ident::from(name_str) |
| } |
| |
| fn serialize_enum( |
| cont: &Container, |
| params: &Parameters, |
| variants: &[Variant] |
| ) -> Fragment { |
| assert!(variants.len() as u64 <= u32::max_value() as u64); |
| |
| let self_var = ¶ms.self_var; |
| |
| let arms: Vec<_> = variants |
| .iter() |
| .enumerate() |
| .map( |
| |(variant_index, variant)| { |
| serialize_variant(cont, variant, variant_index) |
| }, |
| ) |
| .collect(); |
| |
| quote_expr! { |
| match *#self_var { |
| #(#arms)* |
| } |
| } |
| } |
| |
| fn serialize_variant( |
| cont: &Container, |
| variant: &Variant, |
| variant_index: usize |
| ) -> Tokens { |
| |
| let case = serialize_variant_case(cont, variant); |
| |
| let body = serialize_variant_body(cont, variant, variant_index); |
| |
| quote! { |
| #case => #body |
| } |
| } |
| |
| fn serialize_variant_body( |
| cont: &Container, |
| variant: &Variant, |
| variant_index: usize |
| ) -> Tokens { |
| let name: syn::Ident = cont.ident.clone().into(); |
| let name_arg = fromat_ident(&name); |
| let variant_ident = variant.ident.clone(); |
| let variant_ident_arg = fromat_ident(&variant_ident); |
| |
| let body = match variant.style { |
| Style::Unit => { |
| quote! { { |
| let _e = __arg_0; |
| _e.emit_enum(#name_arg, |
| |_e| -> _ { |
| _e.emit_enum_variant(#variant_ident_arg, |
| #variant_index, |
| 0usize, |
| |_e| -> _ { |
| return ::std::result::Result::Ok(()) |
| }) |
| }) |
| } |
| } |
| } |
| Style::Newtype => { |
| quote! { { |
| let _e = __arg_0; |
| _e.emit_enum(#name_arg, |
| |_e| -> _ { |
| _e.emit_enum_variant(#variant_ident_arg, |
| #variant_index, |
| 1usize, |
| |_e| -> _ { |
| return _e.emit_enum_variant_arg( |
| 0usize, |
| |_e| -> _ { |
| ::sgx_serialize::Serializable::encode(&(*__self_0), _e) |
| }) |
| }) |
| }) |
| } |
| } |
| } |
| Style::Tuple => { |
| let variant_fields_len = variant.fields.len(); |
| let variant_args: Vec<Tokens> = |
| variant.fields |
| .iter() |
| .enumerate() |
| .map( |
| |(i, _)| { |
| let id = Ident::new(format!("__self_{}", i)); |
| if variant_fields_len == i+1 { |
| quote! { |
| return _e.emit_enum_variant_arg(#i, |
| |_e| -> _ { |
| ::sgx_serialize::Serializable::encode(&(*#id), |
| _e)}) |
| } |
| } else { |
| quote! { |
| match _e.emit_enum_variant_arg(#i, |
| |_e| -> _ { |
| ::sgx_serialize::Serializable::encode(&(*#id), |
| _e)}) |
| { |
| ::std::result::Result::Ok(__try_var) => __try_var, |
| ::std::result::Result::Err(__try_var) => return ::std::result::Result::Err(__try_var), |
| } |
| } |
| } |
| }) |
| .collect(); |
| quote! { { |
| let _e = __arg_0; |
| _e.emit_enum(#name_arg, |
| |_e| -> _ { |
| _e.emit_enum_variant(#variant_ident_arg, |
| #variant_index, #variant_fields_len, |
| |_e| -> _ { |
| #(#variant_args)* |
| }) |
| }) |
| } |
| } |
| } |
| Style::Struct => { |
| serialize_variant_body_visitor(cont, variant, variant_index) |
| } |
| }; |
| |
| quote! { #body } |
| } |
| |
| fn serialize_variant_body_visitor( |
| cont: &Container, |
| variant: &Variant, |
| variant_index: usize |
| ) -> Tokens { |
| let name: syn::Ident = cont.ident.clone().into(); |
| let name_arg = fromat_ident(&name); |
| |
| let variant_ident = variant.ident.clone(); |
| let variant_ident_arg = fromat_ident(&variant_ident); |
| let variant_fields_len = variant.fields.len(); |
| |
| let variant_args: Vec<Tokens> = |
| variant.fields |
| .iter() |
| .enumerate() |
| .map( |
| |(i, _)| { |
| let id = Ident::new(format!("__self_{}", i)); |
| if variant_fields_len == i+1 { |
| quote! { |
| return _e.emit_enum_variant_arg(#i, |
| |_e| -> _ { |
| ::sgx_serialize::Serializable::encode(&(*#id), |
| _e)}) |
| } |
| } else { |
| quote! { |
| match _e.emit_enum_variant_arg(#i, |
| |_e| -> _ { |
| ::sgx_serialize::Serializable::encode(&(*#id), |
| _e)}) |
| { |
| ::std::result::Result::Ok(__try_var) => __try_var, |
| ::std::result::Result::Err(__try_var) => return ::std::result::Result::Err(__try_var), |
| } |
| } |
| } |
| }) |
| .collect(); |
| quote! { { |
| let _e = __arg_0; |
| _e.emit_enum(#name_arg, |
| |_e| -> _ { |
| _e.emit_enum_variant(#variant_ident_arg, |
| #variant_index, #variant_fields_len, |
| |_e| -> _ { |
| #(#variant_args)* |
| }) |
| }) |
| } |
| } |
| } |
| |
| fn serialize_variant_case( |
| cont: &Container, |
| variant: &Variant |
| ) -> Tokens { |
| let this: syn::Ident = cont.ident.clone().into(); |
| let variant_ident = variant.ident.clone(); |
| |
| let case = match variant.style { |
| Style::Unit => { |
| quote! { |
| #this::#variant_ident |
| } |
| } |
| Style::Newtype => { |
| quote! { |
| #this::#variant_ident(ref __self_0) |
| } |
| } |
| Style::Tuple => { |
| let field_names = |
| (0..variant.fields.len()).map(|i| Ident::new(format!("__self_{}", i))); |
| quote! { |
| #this::#variant_ident(#(ref #field_names),*) |
| } |
| } |
| Style::Struct => { |
| let fields = variant.fields.iter().map( |
| |f| { |
| f.ident.clone().expect("struct variant has unnamed fields") |
| }, |
| ); |
| let selfs = variant.fields.iter().enumerate().map( |
| |(i, _)| { |
| Ident::new(format!("__self_{}", i)) |
| }, |
| ); |
| quote! { |
| #this::#variant_ident { #(#fields: ref #selfs),* } |
| } |
| } |
| }; |
| quote! { #case } |
| } |
| |
| fn serialize_struct( |
| cont: &Container, |
| fields: &[Field] |
| ) -> Fragment { |
| let name: syn::Ident = cont.ident.clone().into(); |
| let name_arg = fromat_ident(&name); |
| |
| let self_args:Vec<Tokens> = fields |
| .iter() |
| .enumerate() |
| .map(|(i, filed)| { |
| let arg = Ident::new(format!("ref __self_0_{}", i)); |
| let arg1 = match filed.ident { |
| Some(ref ident) => { |
| let id: Ident = ident.clone().into(); |
| quote!(#id) |
| } |
| None => { |
| panic!("struct filed must have name!") |
| } |
| }; |
| quote!(#arg1: #arg) |
| }) |
| .collect(); |
| |
| let self_args_cnt = fields.iter().count(); |
| |
| let serialize_stmts = serialize_tuple_struct_visitor( |
| fields, |
| true |
| ); |
| |
| quote_block! { |
| match *self { |
| #name{#(#self_args,)*} => |
| __arg_0.emit_struct(#name_arg, #self_args_cnt, |
| |_e| -> _ |
| { |
| #(#serialize_stmts)* |
| }) |
| } |
| } |
| } |
| |
| fn serialize_tuple_struct( |
| cont: &Container, |
| fields: &[Field], |
| ) -> Fragment { |
| let name: syn::Ident = cont.ident.clone().into(); |
| let name_arg = fromat_ident(&name); |
| |
| let self_args:Vec<Tokens> = fields |
| .iter() |
| .enumerate() |
| .map(|(i, _)| { |
| let arg = Ident::new(format!("ref __self_0_{}", i)); |
| quote!(#arg) |
| }) |
| .collect(); |
| |
| let self_args_cnt = fields.iter().count(); |
| |
| let serialize_stmts = serialize_tuple_struct_visitor( |
| fields, |
| false |
| ); |
| |
| quote_block! { |
| match *self { |
| #name(#(#self_args,)*) => |
| __arg_0.emit_struct(#name_arg, #self_args_cnt, |
| |_e| -> _ |
| { |
| #(#serialize_stmts)* |
| }) |
| } |
| } |
| } |
| |
| fn serialize_tuple_struct_visitor( |
| fields: &[Field], |
| is_struct: bool, |
| ) -> Vec<Tokens> { |
| fields |
| .iter() |
| .enumerate() |
| .map( |
| |(i, field)| { |
| let field_expr = if is_struct { |
| match field.ident { |
| Some(ref ident) => { |
| let id = Ident::new(format!("\"{}\"", ident.clone().as_ref())); |
| quote!(#id) |
| } |
| None => { |
| panic!("struct filed must have name!") |
| } |
| } |
| } else { |
| let id = Ident::new(format!("\"_field{}\"", i)); |
| quote!(#id) |
| }; |
| |
| let arg = Ident::new(format!("__self_0_{}", i)); |
| let field_arg = quote!(#arg); |
| |
| if fields.iter().count() == i+1 { |
| quote! { |
| return _e.emit_struct_field(#field_expr, |
| #i, |
| |_e| -> _ { |
| ::sgx_serialize::Serializable::encode(&(*#field_arg), _e) |
| }); |
| } |
| } else { |
| quote! { |
| match _e.emit_struct_field(#field_expr, |
| #i, |
| |_e| -> _ { |
| ::sgx_serialize::Serializable::encode(&(*#field_arg), |
| _e) |
| }) |
| { |
| ::std::result::Result::Ok(__try_var) |
| => __try_var, |
| ::std::result::Result::Err(__try_var) |
| => |
| return ::std::result::Result::Err(__try_var), |
| } |
| } |
| } |
| }, |
| ) |
| .collect() |
| } |
| |
| fn serialize_newtype_struct( |
| cont: &Container |
| ) -> Fragment { |
| let name: syn::Ident = cont.ident.clone().into(); |
| let name_arg = fromat_ident(&name); |
| |
| quote_expr! { |
| match *self { |
| #name(ref __self_0_0) => |
| __arg_0.emit_struct(#name_arg, 1usize, |
| |_e| -> _ |
| { |
| return _e.emit_struct_field("_field0", |
| 0usize, |
| |_e| -> _ { |
| ::sgx_serialize::Serializable::encode(&(*__self_0_0), _e) |
| }); |
| }), |
| } |
| } |
| } |
| |
| fn serialize_unit_struct(cont: &Container) -> Fragment { |
| let name: syn::Ident = cont.ident.clone().into(); |
| let name_arg = fromat_ident(&name); |
| |
| quote_expr! { |
| match *self { |
| #name => |
| __arg_0.emit_struct(#name_arg, 0usize, |
| |_e| -> _ { |
| return ::std::result::Result::Ok(()) |
| }), |
| } |
| } |
| } |