use quote::ToTokens;
use syn::parse_quote;
use crate::ast::wasm_parts_utils;
use crate::{ir::ModuleImplIR, utils};
pub struct FactoryHasEntrypointsImplItem {
has_ident_ty: syn::Type,
module_ident: syn::Ident,
events_fn: EntrypointsFnItem
}
impl TryFrom<&'_ ModuleImplIR> for FactoryHasEntrypointsImplItem {
type Error = syn::Error;
fn try_from(struct_ir: &'_ ModuleImplIR) -> Result<Self, Self::Error> {
Ok(Self {
has_ident_ty: utils::ty::has_entrypoints(),
module_ident: struct_ir.module_ident()?,
events_fn: struct_ir.try_into()?
})
}
}
impl ToTokens for FactoryHasEntrypointsImplItem {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let has_ident_ty = &self.has_ident_ty;
let module_ident = &self.module_ident;
let events_fn = &self.events_fn;
tokens.extend(quote::quote! {
impl #has_ident_ty for #module_ident {
#events_fn
}
});
}
}
pub struct EntrypointsFnItem {
fn_ident: syn::Ident,
ret_ty: syn::Type,
expr: syn::Expr
}
impl TryFrom<&'_ ModuleImplIR> for EntrypointsFnItem {
type Error = syn::Error;
fn try_from(struct_ir: &'_ ModuleImplIR) -> Result<Self, Self::Error> {
let entrypoint_ty = utils::ty::entry_point_def();
let expr = struct_entrypoints_expr(struct_ir)?;
Ok(Self {
fn_ident: utils::ident::entrypoints(),
ret_ty: utils::ty::vec_of(&entrypoint_ty),
expr
})
}
}
impl ToTokens for EntrypointsFnItem {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let fn_ident = &self.fn_ident;
let ret_ty = &self.ret_ty;
let expr = &self.expr;
tokens.extend(quote::quote! {
fn #fn_ident() -> #ret_ty {
#expr
}
});
}
}
fn struct_entrypoints_expr(ir: &ModuleImplIR) -> syn::Result<syn::Expr> {
let struct_entrypoints = [ir.constructor()]
.iter()
.filter_map(|f| f.as_ref())
.chain([ir.factory_fn(), ir.factory_upgrade_fn(), ir.factory_batch_upgrade_fn()].iter())
.map(|f| {
let args = wasm_parts_utils::param_parameters(f);
let expr = match f.fn_type() {
crate::ir::FnType::Constructor => utils::expr::constructor_ep(parse_quote!(odra::prelude::vec![])),
crate::ir::FnType::Upgrader => utils::expr::upgrader_ep(args),
crate::ir::FnType::Factory => utils::expr::factory_ep(args),
crate::ir::FnType::FactoryUpgrader => utils::expr::factory_upgrade_ep(args),
crate::ir::FnType::FactoryBatchUpgrader => utils::expr::factory_batch_upgrade_ep(),
crate::ir::FnType::Regular => utils::expr::regular_ep(
f.name_str(),
args,
wasm_parts_utils::param_ret_ty(f),
f.is_payable(),
f.is_non_reentrant()
),
};
Ok(expr)
})
.collect::<syn::Result<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>>()?;
Ok(utils::expr::vec_try_into(struct_entrypoints))
}
#[cfg(test)]
mod test {
use crate::test_utils;
use quote::quote;
use super::FactoryHasEntrypointsImplItem;
#[test]
fn test_entrypoints() {
let module = test_utils::mock::module_factory_impl();
let expected = quote!(
impl odra::contract_def::HasEntrypoints for Erc20Factory {
fn entrypoints() -> odra::prelude::vec::Vec<odra::contract_def::Entrypoint> {
odra::prelude::vec![
odra::entry_point::EntryPoint::Constructor {
args: odra::prelude::vec![]
},
odra::entry_point::EntryPoint::Factory {
args: vec![
odra::args::parameter::<odra::prelude::string::String>("contract_name"),
odra::args::parameter::<u32>("value")
],
},
odra::entry_point::EntryPoint::FactoryUpgrade {
args: vec![
odra::args::parameter::<odra::prelude::string::String>("contract_name")
],
},
odra::entry_point::EntryPoint::FactoryBatchUpgrade
].into_iter().map(TryInto::try_into).collect::<Result<_, _>>().unwrap_or_default()
}
}
);
let actual = FactoryHasEntrypointsImplItem::try_from(&module).unwrap();
test_utils::assert_eq(actual, expected);
}
}