use quote::{ToTokens, TokenStreamExt};
use syn::parse_quote;
use crate::{ast::deployer_utils::EpcSignature, utils, ModuleImplIR};
pub struct FactoryDeployImplItem {
ident: syn::Ident,
epc_fn: FactoryContractEpcFn,
}
impl TryFrom<&'_ ModuleImplIR> for FactoryDeployImplItem {
type Error = syn::Error;
fn try_from(module: &'_ ModuleImplIR) -> Result<Self, Self::Error> {
Ok(Self {
ident: module.host_ref_ident()?,
epc_fn: module.try_into()?,
})
}
}
impl ToTokens for FactoryDeployImplItem {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let epc_ty = utils::ty::entry_point_caller_provider();
let ident = &self.ident;
let epc_fn = &self.epc_fn;
tokens.append_all(quote::quote! {
impl #epc_ty for #ident {
#epc_fn
}
});
}
}
struct FactoryContractEpcFn {
sig: EpcSignature,
entry_points_expr: syn::Expr,
caller: FactoryEntrypointCallerExpr
}
impl TryFrom<&'_ ModuleImplIR> for FactoryContractEpcFn {
type Error = syn::Error;
fn try_from(module: &'_ ModuleImplIR) -> Result<Self, Self::Error> {
let fun = module.factory_fn();
let entry_point = utils::expr::new_entry_point(fun.name_str(), fun.raw_typed_args(), fun.is_payable());
Ok(Self {
sig: module.try_into()?,
entry_points_expr: utils::expr::vec(entry_point),
caller: module.try_into()?
})
}
}
impl ToTokens for FactoryContractEpcFn {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let sig = &self.sig;
let entry_points_ident = utils::ident::entry_points();
let caller = &self.caller;
let entry_points_expr = &self.entry_points_expr;
tokens.append_all(quote::quote! {
#sig {
let #entry_points_ident = #entry_points_expr;
#caller
}
});
}
}
#[derive(syn_derive::ToTokens)]
struct FactoryEntrypointCallerExpr {
caller_expr: syn::Expr
}
impl TryFrom<&'_ ModuleImplIR> for FactoryEntrypointCallerExpr {
type Error = syn::Error;
fn try_from(module: &'_ ModuleImplIR) -> Result<Self, Self::Error> {
Ok(Self {
caller_expr: Self::entrypoint_caller(module)?
})
}
}
impl FactoryEntrypointCallerExpr {
fn entrypoint_caller(_module: &ModuleImplIR) -> syn::Result<syn::Expr> {
let env_ident = utils::ident::env();
let entry_points_ident = utils::ident::entry_points();
let contract_env_ident = utils::ident::contract_env();
let call_def_ident = utils::ident::call_def();
let ty_caller = utils::ty::entry_points_caller();
Ok(parse_quote!(
#ty_caller::new(#env_ident.clone(), #entry_points_ident, |#contract_env_ident, #call_def_ident| {
if #call_def_ident.entry_point() == "new_contract" {
return Err(OdraError::VmError(
odra::VmError::Other(odra::prelude::String::from("Factory is not supported for this configuration."))
));
}
Err(OdraError::VmError(odra::VmError::NoSuchMethod(odra::prelude::String::from(#call_def_ident.entry_point()))))
})
))
}
}