use proc_macro::TokenStream;
use quote::quote;
use crate::nested_en_derive::{dep_encode_or_exit_snippet, dep_encode_snippet};
use crate::util::*;
pub fn variant_top_encode_snippets(
name: &syn::Ident,
data_enum: &syn::DataEnum,
) -> Vec<proc_macro2::TokenStream> {
data_enum
.variants
.iter()
.enumerate()
.map(|(variant_index, variant)| {
let variant_index_u8 = variant_index as u8;
let variant_ident = &variant.ident;
if variant.fields.is_empty() {
quote! {
#name::#variant_ident =>
numbat_codec::TopEncode::top_encode(&#variant_index_u8, output),
}
} else {
let local_var_declarations =
fields_decl_syntax(&variant.fields, local_variable_for_field);
let variant_field_snippets = fields_snippets(&variant.fields, |index, field| {
dep_encode_snippet(&local_variable_for_field(index, field))
});
quote! {
#name::#variant_ident #local_var_declarations => {
let mut buffer = numbat_codec::Vec::<u8>::new();
let dest = &mut buffer;
numbat_codec::NestedEncode::dep_encode(&#variant_index_u8, dest)?;
#(#variant_field_snippets)*
output.set_slice_u8(&buffer[..]);
core::result::Result::Ok(())
},
}
}
})
.collect()
}
pub fn variant_top_encode_or_exit_snippets(
name: &syn::Ident,
data_enum: &syn::DataEnum,
) -> Vec<proc_macro2::TokenStream> {
data_enum
.variants
.iter()
.enumerate()
.map(|(variant_index, variant)| {
let variant_index_u8 = variant_index as u8;
let variant_ident = &variant.ident;
if variant.fields.is_empty() {
quote! {
#name::#variant_ident =>
numbat_codec::TopEncode::top_encode_or_exit(&#variant_index_u8, output, c.clone(), exit),
}
} else {
let local_var_declarations =
fields_decl_syntax(&variant.fields, local_variable_for_field);
let variant_field_snippets = fields_snippets(&variant.fields, |index, field| {
dep_encode_or_exit_snippet(&local_variable_for_field(index, field))
});
quote! {
#name::#variant_ident #local_var_declarations => {
let mut buffer = numbat_codec::Vec::<u8>::new();
let dest = &mut buffer;
numbat_codec::NestedEncode::dep_encode_or_exit(&#variant_index_u8, dest, c.clone(), exit);
#(#variant_field_snippets)*
output.set_slice_u8(&buffer[..]);
},
}
}
})
.collect()
}
fn top_encode_method_bodies(
ast: &syn::DeriveInput,
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
let name = &ast.ident;
match &ast.data {
syn::Data::Struct(data_struct) => {
let field_dep_encode_snippets = fields_snippets(&data_struct.fields, |index, field| {
dep_encode_snippet(&self_field_expr(index, field))
});
let field_dep_encode_or_exit_snippets =
fields_snippets(&data_struct.fields, |index, field| {
dep_encode_or_exit_snippet(&self_field_expr(index, field))
});
let top_encode_body = quote! {
let mut buffer = numbat_codec::Vec::<u8>::new();
let dest = &mut buffer;
#(#field_dep_encode_snippets)*
output.set_slice_u8(&buffer[..]);
core::result::Result::Ok(())
};
let top_encode_or_exit_body = quote! {
let mut buffer = numbat_codec::Vec::<u8>::new();
let dest = &mut buffer;
#(#field_dep_encode_or_exit_snippets)*
output.set_slice_u8(&buffer[..]);
};
(top_encode_body, top_encode_or_exit_body)
},
syn::Data::Enum(data_enum) => {
assert!(
data_enum.variants.len() < 256,
"enums with more than 256 variants not supported"
);
let variant_top_encode_snippets = variant_top_encode_snippets(name, data_enum);
let variant_top_encode_or_exit_snippets =
variant_top_encode_or_exit_snippets(name, data_enum);
let top_encode_body = quote! {
match self {
#(#variant_top_encode_snippets)*
}
};
let top_encode_or_exit_body = quote! {
match self {
#(#variant_top_encode_or_exit_snippets)*
}
};
(top_encode_body, top_encode_or_exit_body)
},
syn::Data::Union(_) => panic!("Union not supported"),
}
}
pub fn top_encode_impl(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl();
let (top_encode_body, top_encode_or_exit_body) = top_encode_method_bodies(ast);
let gen = quote! {
impl #impl_generics numbat_codec::TopEncode for #name #ty_generics #where_clause {
fn top_encode<O: numbat_codec::TopEncodeOutput>(&self, output: O) -> core::result::Result<(), numbat_codec::EncodeError> {
#top_encode_body
}
fn top_encode_or_exit<O: numbat_codec::TopEncodeOutput, ExitCtx: Clone>(
&self,
output: O,
c: ExitCtx,
exit: fn(ExitCtx, numbat_codec::EncodeError) -> !,
) {
#top_encode_or_exit_body
}
}
};
gen.into()
}
pub fn top_encode_or_default_impl(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl();
let (top_encode_body, top_encode_or_exit_body) = top_encode_method_bodies(ast);
let gen = quote! {
impl #impl_generics numbat_codec::TopEncode for #name #ty_generics #where_clause {
fn top_encode<O: numbat_codec::TopEncodeOutput>(&self, output: O) -> core::result::Result<(), numbat_codec::EncodeError> {
if numbat_codec::EncodeDefault::is_default(self) {
output.set_slice_u8(&[]);
Ok(())
} else {
#top_encode_body
}
}
fn top_encode_or_exit<O: numbat_codec::TopEncodeOutput, ExitCtx: Clone>(
&self,
output: O,
c: ExitCtx,
exit: fn(ExitCtx, numbat_codec::EncodeError) -> !,
) {
if numbat_codec::EncodeDefault::is_default(self) {
output.set_slice_u8(&[]);
} else {
#top_encode_or_exit_body
}
}
}
};
gen.into()
}