use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, Attribute, Data, DeriveInput, Expr, Fields, Path};
#[proc_macro_derive(BorshDeserializeIncremental, attributes(incremental))]
pub fn derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let ident = input.ident;
let (impl_g, ty_g, where_c) = input.generics.split_for_impl();
let mut error_ty: Option<Path> = None;
for a in &input.attrs {
if a.path().is_ident("incremental") {
let _ = a.parse_nested_meta(|meta| {
if meta.path.is_ident("error") {
let value: Expr = meta.value()?.parse()?;
if let Expr::Path(p) = value {
error_ty = Some(p.path);
} else {
return Err(meta.error("expected path for error type"));
}
}
Ok(())
});
}
}
let error_ty = error_ty.unwrap_or_else(|| syn::parse_quote!(::std::io::Error));
let fields = match input.data {
Data::Struct(s) => match s.fields {
Fields::Named(n) => n.named,
_ => panic!("BorshDeserializeIncremental only supports named structs"),
},
_ => panic!("BorshDeserializeIncremental only supports structs"),
};
fn parse_field_attrs(attrs: &[Attribute]) -> (Option<Path>, Option<Expr>) {
let mut deser_with: Option<Path> = None;
let mut default_expr: Option<Expr> = None;
for a in attrs {
if a.path().is_ident("incremental") {
let _ = a.parse_nested_meta(|meta| {
if meta.path.is_ident("deser_with") {
let value: Expr = meta.value()?.parse()?;
if let Expr::Path(p) = value {
deser_with = Some(p.path);
}
} else if meta.path.is_ident("default") {
let value: Expr = meta.value()?.parse()?;
default_expr = Some(value);
}
Ok(())
});
}
}
(deser_with, default_expr)
}
struct F {
name: syn::Ident,
ty: syn::Type,
deser_with: Option<Path>,
default_expr: Option<Expr>,
}
let mut fdefs = Vec::new();
for f in fields {
let name = f.ident.unwrap();
let ty = f.ty;
let (deser_with, default_expr) = parse_field_attrs(&f.attrs);
fdefs.push(F {
name,
ty,
deser_with,
default_expr,
});
}
let temps: Vec<_> = fdefs
.iter()
.map(|f| format_ident!("__tmp_{}", f.name))
.collect();
let reads = fdefs.iter().zip(&temps).map(|(f, tmp)| {
let ty = &f.ty;
let fallback = f
.default_expr
.as_ref()
.map(|e| quote!(#e))
.unwrap_or_else(|| quote!(<#ty as ::core::default::Default>::default()));
if let Some(dw) = &f.deser_with {
quote! {
let #tmp: #ty = (#dw)(&mut data).ok().unwrap_or_else(|| #fallback);
}
} else {
quote! {
let #tmp: #ty = <#ty as ::borsh::BorshDeserialize>::deserialize(&mut data)
.ok()
.unwrap_or_else(|| #fallback);
}
}
});
let assigns = fdefs.iter().zip(&temps).map(|(f, tmp)| {
let name = &f.name;
quote!(#name: #tmp)
});
let expanded = quote! {
impl #impl_g ::core::convert::TryFrom<&[u8]> for #ident #ty_g #where_c {
type Error = #error_ty;
fn try_from(mut data: &[u8]) -> ::core::result::Result<Self, Self::Error> {
#( #reads )*
Ok(Self { #( #assigns ),* })
}
}
};
TokenStream::from(expanded)
}