use proc_macro2::{Span, TokenStream, Ident};
use syn::{
spanned::Spanned,
Data, Fields, Field, Error,
};
use crate::utils;
pub fn quote(
data: &Data,
type_name: &Ident,
type_generics: &TokenStream,
input: &TokenStream,
crate_path: &syn::Path,
) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(_) | Fields::Unnamed(_) => create_instance(
quote! { #type_name #type_generics },
&type_name.to_string(),
input,
&data.fields,
crate_path,
),
Fields::Unit => {
quote_spanned! { data.fields.span() =>
::core::result::Result::Ok(#type_name)
}
},
},
Data::Enum(ref data) => {
let data_variants = || data.variants.iter().filter(|variant| !utils::should_skip(&variant.attrs));
if data_variants().count() > 256 {
return Error::new(
data.variants.span(),
"Currently only enums with at most 256 variants are encodable."
).to_compile_error();
}
let recurse = data_variants().enumerate().map(|(i, v)| {
let name = &v.ident;
let index = utils::variant_index(v, i);
let create = create_instance(
quote! { #type_name #type_generics :: #name },
&format!("{}::{}", type_name, name),
input,
&v.fields,
crate_path,
);
quote_spanned! { v.span() =>
#[allow(clippy::unnecessary_cast)]
__codec_x_edqy if __codec_x_edqy == #index as ::core::primitive::u8 => {
#[allow(clippy::redundant_closure_call)]
return (move || {
#create
})();
},
}
});
let read_byte_err_msg = format!(
"Could not decode `{}`, failed to read variant byte",
type_name,
);
let invalid_variant_err_msg = format!(
"Could not decode `{}`, variant doesn't exist",
type_name,
);
quote! {
match #input.read_byte()
.map_err(|e| e.chain(#read_byte_err_msg))?
{
#( #recurse )*
_ => {
#[allow(clippy::redundant_closure_call)]
return (move || {
::core::result::Result::Err(
<_ as ::core::convert::Into<_>>::into(#invalid_variant_err_msg)
)
})();
},
}
}
},
Data::Union(_) => Error::new(Span::call_site(), "Union types are not supported.").to_compile_error(),
}
}
pub fn quote_decode_into(
data: &Data,
crate_path: &syn::Path,
input: &TokenStream,
attrs: &[syn::Attribute]
) -> Option<TokenStream> {
if !crate::utils::is_transparent(attrs) {
return None;
}
let fields = match data {
Data::Struct(
syn::DataStruct {
fields: Fields::Named(syn::FieldsNamed { named: fields, .. }) |
Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }),
..
}
) => {
fields
},
_ => return None
};
if fields.is_empty() {
return None;
}
if fields.iter().any(|field|
utils::get_encoded_as_type(field).is_some() ||
utils::is_compact(field) ||
utils::should_skip(&field.attrs)
) {
return None;
}
let mut decode_fields = Vec::new();
let mut sizes = Vec::new();
let mut non_zst_field_count = Vec::new();
for field in fields {
let field_type = &field.ty;
decode_fields.push(quote! {{
let dst_: &mut ::core::mem::MaybeUninit<Self> = dst_; let dst_: &mut ::core::mem::MaybeUninit<#field_type> = unsafe {
&mut *dst_.as_mut_ptr().cast::<::core::mem::MaybeUninit<#field_type>>()
};
<#field_type as #crate_path::Decode>::decode_into(#input, dst_)?;
}});
if !sizes.is_empty() {
sizes.push(quote! { + });
}
sizes.push(quote! { ::core::mem::size_of::<#field_type>() });
if !non_zst_field_count.is_empty() {
non_zst_field_count.push(quote! { + });
}
non_zst_field_count.push(quote! { if ::core::mem::size_of::<#field_type>() > 0 { 1 } else { 0 } });
}
Some(quote!{
::core::assert_eq!(#(#sizes)*, ::core::mem::size_of::<Self>());
::core::assert!(#(#non_zst_field_count)* <= 1);
#(#decode_fields)*
unsafe { ::core::result::Result::Ok(#crate_path::DecodeFinished::assert_decoding_finished()) }
})
}
fn create_decode_expr(field: &Field, name: &str, input: &TokenStream, crate_path: &syn::Path) -> TokenStream {
let encoded_as = utils::get_encoded_as_type(field);
let compact = utils::is_compact(field);
let skip = utils::should_skip(&field.attrs);
let res = quote!(__codec_res_edqy);
if encoded_as.is_some() as u8 + compact as u8 + skip as u8 > 1 {
return Error::new(
field.span(),
"`encoded_as`, `compact` and `skip` can only be used one at a time!"
).to_compile_error();
}
let err_msg = format!("Could not decode `{}`", name);
if compact {
let field_type = &field.ty;
quote_spanned! { field.span() =>
{
let #res = <
<#field_type as #crate_path::HasCompact>::Type as #crate_path::Decode
>::decode(#input);
match #res {
::core::result::Result::Err(e) => return ::core::result::Result::Err(e.chain(#err_msg)),
::core::result::Result::Ok(#res) => #res.into(),
}
}
}
} else if let Some(encoded_as) = encoded_as {
quote_spanned! { field.span() =>
{
let #res = <#encoded_as as #crate_path::Decode>::decode(#input);
match #res {
::core::result::Result::Err(e) => return ::core::result::Result::Err(e.chain(#err_msg)),
::core::result::Result::Ok(#res) => #res.into(),
}
}
}
} else if skip {
quote_spanned! { field.span() => ::core::default::Default::default() }
} else {
let field_type = &field.ty;
quote_spanned! { field.span() =>
{
let #res = <#field_type as #crate_path::Decode>::decode(#input);
match #res {
::core::result::Result::Err(e) => return ::core::result::Result::Err(e.chain(#err_msg)),
::core::result::Result::Ok(#res) => #res,
}
}
}
}
}
fn create_instance(
name: TokenStream,
name_str: &str,
input: &TokenStream,
fields: &Fields,
crate_path: &syn::Path,
) -> TokenStream {
match *fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name_ident = &f.ident;
let field_name = match name_ident {
Some(a) => format!("{}::{}", name_str, a),
None => name_str.to_string(), };
let decode = create_decode_expr(f, &field_name, input, crate_path);
quote_spanned! { f.span() =>
#name_ident: #decode
}
});
quote_spanned! { fields.span() =>
::core::result::Result::Ok(#name {
#( #recurse, )*
})
}
},
Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f) | {
let field_name = format!("{}.{}", name_str, i);
create_decode_expr(f, &field_name, input, crate_path)
});
quote_spanned! { fields.span() =>
::core::result::Result::Ok(#name (
#( #recurse, )*
))
}
},
Fields::Unit => {
quote_spanned! { fields.span() =>
::core::result::Result::Ok(#name)
}
},
}
}