use quote::{ToTokens, TokenStreamExt};
use syn::parse_quote;
use crate::{ir::FnIR, ModuleImplIR, ModuleStructIR};
pub mod impl_item;
mod parts;
pub mod struct_item;
pub struct FactoryModuleItem {
is_factory: bool,
factory_module_ident: syn::Ident,
}
impl TryFrom<&ModuleStructIR> for FactoryModuleItem {
type Error = syn::Error;
fn try_from(ir: &ModuleStructIR) -> Result<Self, Self::Error> {
Ok(Self {
is_factory: ir.is_factory(),
factory_module_ident: ir.factory_module_ident(),
})
}
}
impl ToTokens for FactoryModuleItem {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let is_factory = self.is_factory;
if !is_factory {
return;
}
let factory_module_ident = &self.factory_module_ident;
tokens.append_all(quote::quote! {
#[automatically_derived]
#[odra::factory]
pub struct #factory_module_ident;
});
}
}
pub struct FactoryModuleImplItem {
is_factory: bool,
factory_module_ident: syn::Ident,
functions: Vec<FnIR>
}
impl TryFrom<&ModuleImplIR> for FactoryModuleImplItem {
type Error = syn::Error;
fn try_from(ir: &ModuleImplIR) -> Result<Self, Self::Error> {
let functions = ir.functions()?;
Ok(Self {
is_factory: ir.is_factory(),
factory_module_ident: ir.factory_module_ident()?,
functions
})
}
}
impl ToTokens for FactoryModuleImplItem {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let is_factory = self.is_factory;
if !is_factory {
return;
}
let factory_module_ident = &self.factory_module_ident;
let functions = &self
.functions
.iter()
.filter_map(|f| match f {
FnIR::Impl(fn_impl_ir) => Some(fn_impl_ir),
_ => None
})
.map(|f| {
syn::ImplItemFn {
block: parse_quote!({
self.env().revert(OdraError::ExecutionError(ExecutionError::FactoryModuleCall));
}),
..f.raw()
}
})
.collect::<Vec<_>>();
tokens.append_all(quote::quote! {
#[automatically_derived]
#[odra::factory]
impl #factory_module_ident {
#(#functions)*
}
});
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test_utils::{self, mock};
#[test]
fn test_factory_module_item_generation() {
let ir = mock::factory_module_definition();
let actual = FactoryModuleItem::try_from(&ir).expect("A valid FactoryModuleItem");
let expected = quote::quote! {
#[automatically_derived]
#[odra::factory]
pub struct CounterPackFactory;
};
test_utils::assert_eq(actual, expected);
}
#[test]
fn test_non_factory_module_item_generation() {
let ir = mock::module_definition();
let actual = FactoryModuleItem::try_from(&ir).expect("A valid FactoryModuleItem");
let expected = quote::quote! {};
test_utils::assert_eq(actual, expected);
}
#[test]
fn test_factory_module_impl_item_generation() {
let ir = mock::module_factory_on();
let actual = FactoryModuleImplItem::try_from(&ir).expect("A valid FactoryModuleImplItem");
let expected = quote::quote! {
#[automatically_derived]
#[odra::factory]
impl Erc20Factory {
pub fn total_supply(&self) -> U256 {
self.env().revert(OdraError::ExecutionError(ExecutionError::FactoryModuleCall));
}
#[odra(payable)]
pub fn pay_to_mint(&mut self) {
self.env().revert(OdraError::ExecutionError(ExecutionError::FactoryModuleCall));
}
}
};
test_utils::assert_eq(actual, expected);
}
}