use super::*;
use quote::*;
use syn::*;
pub fn derive_encode(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let core_path = utils::core_path_attr(&input.attrs);
let ident = input.ident.clone();
let implementation = match &input.data {
Data::Struct(data_struct) => generate_struct_impl(core_path, ident, data_struct),
Data::Enum(data_enum) => generate_enum_impl(core_path, ident, data_enum),
Data::Union(_) => panic!("Unions are not supported")
};
implementation.into()
}
fn generate_struct_impl(core_path: TokenStream2, ident: Ident, data_struct: &DataStruct) -> TokenStream2 {
match &data_struct.fields {
Fields::Named(fields_named) => {
let fields = &fields_named.named;
let encode_fields: Vec<TokenStream2> = fields.iter().filter_map(|field| {
match encode_attr(&field.attrs) {
EncodeAttr::CompileError { compile_error } => return Some(compile_error.into()),
EncodeAttr::Ok { skip } => if skip { return None },
}
let field_name = &field.ident;
Some(
quote! {
encoder.output_map_key(::core::stringify!(#field_name));
encoder.delegate_encoding(&self.#field_name);
}
)
}).collect();
let decode_fields: Vec<TokenStream2> = fields.iter().filter_map(|field| {
match encode_attr(&field.attrs) {
EncodeAttr::CompileError { compile_error } => return Some(compile_error.into()),
EncodeAttr::Ok { skip } => if skip { return None },
}
let field_name = &field.ident;
Some(
quote! {
::core::stringify!(#field_name) => decoder.delegate_decoding(&mut self.#field_name)?,
}
)
}).collect();
let field_count = encode_fields.len();
quote! {
impl #core_path::encode::Encode for #ident {
fn encode(&self, encoder: &mut #core_path::encode::Encoder) {
encoder.output_map_start(#field_count);
#(#encode_fields)*
}
fn decode(&mut self, decoder: &mut #core_path::encode::Decoder) -> Result<(), Box<dyn ::std::error::Error>> {
match decoder.maybe_map_start()? {
None => decoder.skip_value()?,
Some(len) => for _ in 0..len {
match decoder.expect_map_key()?.as_str() {
#(#decode_fields)*
_ => decoder.skip_value()?,
}
}
}
Ok(())
}
}
}
},
Fields::Unnamed(fields_unnamed) => {
let fields = &fields_unnamed.unnamed;
let encode_fields: Vec<TokenStream2> = fields.iter().enumerate().filter_map(|(i, field)| {
match encode_attr(&field.attrs) {
EncodeAttr::CompileError { compile_error } => return Some(compile_error.into()),
EncodeAttr::Ok { skip } => if skip { return None },
}
let index = syn::Index::from(i);
Some(
quote! {
encoder.delegate_encoding(&self.#index);
}
)
}).collect();
let decode_fields: Vec<TokenStream2> = fields.iter().enumerate().filter_map(|(i, field)| {
match encode_attr(&field.attrs) {
EncodeAttr::CompileError { compile_error } => return Some(compile_error.into()),
EncodeAttr::Ok { skip } => if skip { return None },
}
let index = syn::Index::from(i);
Some(
quote! {
decoder.expect_array_item()?;
decoder.delegate_decoding(&mut self.#index)?;
}
)
}).collect();
let field_count = encode_fields.len();
quote! {
impl #core_path::encode::Encode for #ident {
fn encode(&self, encoder: &mut #core_path::encode::Encoder) {
encoder.output_array_start(#field_count);
#(#encode_fields)*
}
fn decode(&mut self, decoder: &mut #core_path::encode::Decoder) -> Result<(), Box<dyn ::std::error::Error>> {
match decoder.maybe_array_start()? {
None => decoder.skip_value()?,
Some(#field_count) => {
#(#decode_fields)*
}
Some(_) => decoder.skip_value()?,
}
Ok(())
}
}
}
},
Fields::Unit => {
quote! {
impl #core_path::encode::Encode for #ident {
fn encode(&self, encoder: &mut #core_path::encode::Encoder) {
encoder.output_null();
}
fn decode(&mut self, decoder: &mut #core_path::encode::Decoder) -> Result<(), Box<dyn std::error::Error>> {
match decoder.maybe_null()? {
None => decoder.skip_value()?,
Some(()) => (),
}
Ok(())
}
}
}
}
}
}
fn generate_enum_impl(core_path: TokenStream2, ident: Ident, data_enum: &DataEnum) -> TokenStream2 {
let encode_variants = data_enum.variants.iter().map(|variant| {
let variant_name = &variant.ident;
match &variant.fields {
Fields::Unit => quote! {
#ident::#variant_name => {
encoder.output_enum_variant(::core::stringify!(#variant_name));
encoder.output_null();
}
},
Fields::Named(inner_fields_named) => {
let inner_fields = &inner_fields_named.named;
let inner_field_idents = inner_fields.iter().map(|f| &f.ident);
let inner_encode_fields: Vec<TokenStream2> = inner_fields.iter().filter_map(|field| {
match encode_attr(&field.attrs) {
EncodeAttr::CompileError { compile_error } => return Some(compile_error.into()),
EncodeAttr::Ok { skip } => if skip { return None },
}
let inner_field_name = &field.ident;
Some(
quote! {
encoder.output_map_key(::core::stringify!(#inner_field_name));
encoder.delegate_encoding(#inner_field_name);
}
)
}).collect();
let inner_field_count = inner_encode_fields.len();
quote! {
#ident::#variant_name {
#(#inner_field_idents),*
} => {
encoder.output_enum_variant(::core::stringify!(#variant_name));
encoder.output_map_start(#inner_field_count);
#(#inner_encode_fields)*
}
}
},
Fields::Unnamed(inner_fields_unnamed) => {
let inner_fields = &inner_fields_unnamed.unnamed;
let inner_vars = inner_fields.iter().enumerate().map(|(i, _)| format_ident!("v{}", i));
let inner_encode_fields: Vec<TokenStream2> = inner_fields.iter().enumerate().filter_map(|(i, field)| {
match encode_attr(&field.attrs) {
EncodeAttr::CompileError { compile_error } => return Some(compile_error.into()),
EncodeAttr::Ok { skip } => if skip { return None },
}
let inner_var = format_ident!("v{}", i);
Some(
quote! {
encoder.delegate_encoding(#inner_var);
}
)
}).collect();
let inner_field_count = inner_encode_fields.len();
quote! {
#ident::#variant_name(#(#inner_vars),*) => {
encoder.output_enum_variant(::core::stringify!(#variant_name));
encoder.output_array_start(#inner_field_count);
#(#inner_encode_fields)*
}
}
}
}
});
let decode_variants = data_enum.variants.iter().map(|variant| {
let variant_name = &variant.ident;
match &variant.fields {
Fields::Unit => quote! {
::core::stringify!(#variant_name) => {
decoder.skip_value()?;
*self = #ident::#variant_name;
}
},
Fields::Named(inner_fields_named) => {
let inner_fields = &inner_fields_named.named;
let inner_var_defaults = inner_fields.iter().map(|field| {
let ident = &field.ident;
let ty = &field.ty;
quote! {
let mut #ident = #ty::default();
}
});
let inner_match_arms = inner_fields.iter().filter_map(|field| {
match encode_attr(&field.attrs) {
EncodeAttr::CompileError { compile_error } => return Some(compile_error.into()),
EncodeAttr::Ok { skip } => if skip { return None },
}
let ident = &field.ident;
Some(
quote! {
::core::stringify!(#ident) => decoder.delegate_decoding(&mut #ident)?,
}
)
});
let inner_struct_vars = inner_fields.iter().map(|field| &field.ident);
quote! {
::core::stringify!(#variant_name) => {
match decoder.maybe_map_start()? {
None => decoder.skip_value()?,
Some(len) => {
#(#inner_var_defaults)*
for _ in 0..len {
match decoder.expect_map_key()?.as_str() {
#(#inner_match_arms)*
_ => decoder.skip_value()?,
}
}
*self = #ident::#variant_name {
#(#inner_struct_vars),*
}
}
}
}
}
},
Fields::Unnamed(inner_fields_unnamed) => {
let inner_fields = &inner_fields_unnamed.unnamed;
let inner_var_defaults = inner_fields.iter().enumerate().map(|(i, field)| {
let inner_var_name = format_ident!("v{}", i);
let ty = &field.ty;
quote! {
let mut #inner_var_name = #ty::default();
}
});
let inner_decode_items: Vec<TokenStream2> = inner_fields.iter().enumerate().filter_map(|(i, field)| {
match encode_attr(&field.attrs) {
EncodeAttr::CompileError { compile_error } => return Some(compile_error.into()),
EncodeAttr::Ok { skip } => if skip { return None },
}
let inner_var_name = format_ident!("v{}", i);
Some(
quote! {
decoder.expect_array_item()?;
decoder.delegate_decoding(&mut #inner_var_name)?;
}
)
}).collect();
let inner_field_count = inner_decode_items.len();
let inner_struct_vars = inner_fields.iter().enumerate().map(|(i, _)| format_ident!("v{}", i));
quote! {
::core::stringify!(#variant_name) => {
match decoder.maybe_array_start()? {
None => decoder.skip_value()?,
Some(#inner_field_count) => {
#(#inner_var_defaults)*
#(#inner_decode_items)*
*self = #ident::#variant_name(#(#inner_struct_vars),*);
}
Some(_) => decoder.skip_value()?,
}
}
}
},
}
});
quote! {
impl #core_path::encode::Encode for #ident {
fn encode(&self, encoder: &mut #core_path::encode::Encoder) {
encoder.output_enum_start();
match self {
#(#encode_variants)*
}
}
fn decode(&mut self, decoder: &mut #core_path::encode::Decoder) -> Result<(), Box<dyn ::std::error::Error>> {
match decoder.maybe_enum_start()? {
None => decoder.skip_value()?,
Some(_) => {
match decoder.expect_enum_variant()?.as_str() {
#(#decode_variants)*
_ => decoder.skip_value()?,
}
}
}
Ok(())
}
}
}
}
pub enum EncodeAttr {
Ok{
skip: bool
},
CompileError{
compile_error: TokenStream
},
}
fn encode_attr(attrs: &[Attribute]) -> EncodeAttr {
let mut skip = false;
let mut attr_error: Option<TokenStream> = None;
attrs.iter().for_each(|attr| {
if attr.path().is_ident("encode") {
match attr.parse_args::<Meta>() {
Ok(meta) => match meta {
Meta::Path(path) => {
if let Some(ident) = path.get_ident() {
match ident.to_string().as_str() {
"skip" => skip = true,
other => attr_error = Some(quote!{
::core::compile_error!(::core::concat!("Unrecognized attribute ", ::core::stringify!(#other), " in #[encode]"));
}.into()),
}
} else {
attr_error = Some(quote!{
::core::compile_error!(::core::concat!("Unrecognized attribute ", ::core::stringify!(#path), " in #[encode]"));
}.into())
}
}
_ => {
attr_error = Some(quote!{
::core::compile_error!("Unrecognized attribute in #[encode]");
}.into());
}
}
Err(_) => {
attr_error = Some(quote!{
::core::compile_error!("Unrecognized attribute in #[encode]");
}.into());
}
}
}
});
if let Some(compile_error) = attr_error {
return EncodeAttr::CompileError { compile_error }
} else {
return EncodeAttr::Ok { skip }
}
}