use crate::utils::crate_name_or_panic;
use proc_macro2::TokenStream;
use quote::quote;
#[derive(deluxe::ExtractAttributes)]
#[deluxe(attributes(from_tvm_stack))]
struct HeaderAttributes {
ensure_empty: Option<bool>, allow_extra: Option<bool>, }
pub fn from_tvm_stack_derive_impl(input: proc_macro::TokenStream) -> TokenStream {
let mut input = syn::parse::<syn::DeriveInput>(input).unwrap();
let header_attrs: HeaderAttributes = match deluxe::extract_attributes(&mut input) {
Ok(desc) => desc,
Err(e) => return e.into_compile_error(),
};
let crate_path = crate_name_or_panic("ton");
let name = input.ident;
let fields = if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
..
}) = input.data
{
named
} else {
unimplemented!("Now it is implemented only for ordinary structs with named fields")
};
let members_count = fields.len();
let names = fields.clone().into_iter().map(|f| {
let name = &f.ident;
quote! {#name}
});
let assigns = fields.into_iter().rev().map(|f| {
let name = &f.ident;
quote! {
let #name = #crate_path::block_tlb::FromTVMStack::from_stack(stack)?;
}
});
let ensure_empty = if header_attrs.ensure_empty.unwrap_or(false) {
quote! {stack.ensure_empty()?;}
} else {
quote! {}
};
let drop_extra = if header_attrs.allow_extra.unwrap_or(false) {
quote! {
while stack.len() > #members_count {
let _ = stack.pop_checked()?;
}
}
} else {
quote! {}
};
let expanded = quote! {
impl #crate_path::block_tlb::FromTVMStack for #name {
fn from_stack(stack: &mut #crate_path::block_tlb::TVMStack) -> #crate_path::errors::TonResult<Self> {
#drop_extra
#(#assigns)*
#ensure_empty
Ok(Self {
#(#names,) *
})
}
}
};
expanded
}