use proc_macro2::{Span, TokenStream, Ident};
use syn::{Data, Fields, Field, spanned::Spanned, Error};
use crate::utils;
pub fn quote(data: &Data, type_name: &Ident, input: &TokenStream) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(_) | Fields::Unnamed(_) => create_instance(
quote! { #type_name },
input,
&data.fields,
),
Fields::Unit => {
quote_spanned! { data.fields.span() =>
drop(#input);
Ok(#type_name)
}
},
},
Data::Enum(ref data) => {
let data_variants = || data.variants.iter().filter(|variant| crate::utils::get_skip(&variant.attrs).is_none());
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::index(v, i);
let create = create_instance(
quote! { #type_name :: #name },
input,
&v.fields,
);
quote_spanned! { v.span() =>
x if x == #index as u8 => {
#create
},
}
});
let err_msg = format!("No such variant in enum {}", type_name);
quote! {
match #input.read_byte()? {
#( #recurse )*
x => Err(#err_msg.into()),
}
}
},
Data::Union(_) => Error::new(Span::call_site(), "Union types are not supported.").to_compile_error(),
}
}
fn create_decode_expr(field: &Field, name: &String, input: &TokenStream) -> TokenStream {
let encoded_as = utils::get_encoded_as_type(field);
let compact = utils::get_enable_compact(field);
let skip = utils::get_skip(&field.attrs).is_some();
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!("Error decoding field {}", name);
if compact {
let field_type = &field.ty;
quote_spanned! { field.span() =>
{
let res = <
<#field_type as _parity_scale_codec::HasCompact>::Type as _parity_scale_codec::Decode
>::decode(#input);
match res {
Err(_) => return Err(#err_msg.into()),
Ok(a) => a.into(),
}
}
}
} else if let Some(encoded_as) = encoded_as {
quote_spanned! { field.span() =>
{
let res = <#encoded_as as _parity_scale_codec::Decode>::decode(#input);
match res {
Err(_) => return Err(#err_msg.into()),
Ok(a) => a.into(),
}
}
}
} else if skip {
quote_spanned! { field.span() => Default::default() }
} else {
quote_spanned! { field.span() =>
{
let res = _parity_scale_codec::Decode::decode(#input);
match res {
Err(_) => return Err(#err_msg.into()),
Ok(a) => a,
}
}
}
}
}
fn create_instance(
name: TokenStream,
input: &TokenStream,
fields: &Fields
) -> TokenStream {
match *fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name_ident = &f.ident;
let field = match name_ident {
Some(a) => format!("{}.{}", name, a),
None => format!("{}", name),
};
let decode = create_decode_expr(f, &field, input);
quote_spanned! { f.span() =>
#name_ident: #decode
}
});
quote_spanned! { fields.span() =>
Ok(#name {
#( #recurse, )*
})
}
},
Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f) | {
let name = format!("{}.{}", name, i);
create_decode_expr(f, &name, input)
});
quote_spanned! { fields.span() =>
Ok(#name (
#( #recurse, )*
))
}
},
Fields::Unit => {
quote_spanned! { fields.span() =>
Ok(#name)
}
},
}
}