use syn::parse_quote;
use crate::ast::fn_utils::FnItem;
use crate::ast::utils::ImplItem;
use crate::ir::TypeIR;
use crate::utils::misc::AsBlock;
use crate::{ir::ModuleStructIR, utils};
#[derive(syn_derive::ToTokens)]
pub struct HasEventsImplItem {
impl_item: ImplItem,
#[syn(braced)]
brace_token: syn::token::Brace,
#[syn(in = brace_token)]
events_fn: EventsFnsItem
}
impl TryFrom<&'_ ModuleStructIR> for HasEventsImplItem {
type Error = syn::Error;
fn try_from(ir: &'_ ModuleStructIR) -> Result<Self, Self::Error> {
Ok(Self {
impl_item: ImplItem::has_events(ir)?,
brace_token: Default::default(),
events_fn: ir.try_into()?
})
}
}
impl TryFrom<&'_ TypeIR> for HasEventsImplItem {
type Error = syn::Error;
fn try_from(ir: &'_ TypeIR) -> Result<Self, Self::Error> {
Ok(Self {
impl_item: ImplItem::has_events(ir)?,
brace_token: Default::default(),
events_fn: EventsFnsItem::empty()
})
}
}
#[derive(syn_derive::ToTokens)]
pub struct EventsFnsItem {
events_fn: FnItem,
event_schemas_fn: FnItem
}
impl EventsFnsItem {
pub fn empty() -> Self {
Self {
events_fn: FnItem::new(
&utils::ident::events(),
vec![],
Self::events_ret_ty(),
utils::expr::empty_vec().as_block()
),
event_schemas_fn: FnItem::new(
&utils::ident::event_schemas(),
vec![],
Self::schemas_ret_ty(),
utils::expr::empty_btree_map().as_block()
)
}
}
pub fn single(ty: &syn::Type) -> Self {
let into_event = utils::expr::into_event(ty);
let name = utils::expr::event_instance_name(ty);
let schema = utils::expr::event_instance_schema(ty);
let content = quote::quote!((#name, #schema));
let schema_expr = utils::expr::vec(content);
Self {
events_fn: FnItem::new(
&utils::ident::events(),
vec![],
Self::events_ret_ty(),
utils::expr::vec(into_event).as_block()
),
event_schemas_fn: FnItem::new(
&utils::ident::event_schemas(),
vec![],
Self::schemas_ret_ty(),
utils::expr::btree_from_iter(&schema_expr).as_block()
)
}
}
fn events_ret_ty() -> syn::ReturnType {
let ev_ty = utils::ty::event();
let vec = utils::ty::vec_of(&ev_ty);
utils::misc::ret_ty(&vec)
}
fn schemas_ret_ty() -> syn::ReturnType {
let string_ty = utils::ty::string();
let schema_ty = utils::ty::schema();
let btree = utils::ty::typed_btree_map(&string_ty, &schema_ty);
utils::misc::ret_ty(&btree)
}
fn events_fn(ir: &ModuleStructIR) -> syn::Result<FnItem> {
let ident_events = utils::ident::events();
let struct_events_stmt = struct_events_stmt(ir);
let chain_events_expr = chain_events_expr(ir)?;
Ok(FnItem::new(
&ident_events,
vec![],
EventsFnsItem::events_ret_ty(),
parse_quote!({
#struct_events_stmt
#chain_events_expr
})
))
}
fn event_schemas_fn(ir: &ModuleStructIR) -> Result<FnItem, syn::Error> {
let ident_events = utils::ident::event_schemas();
let struct_events_stmt = struct_event_schemas_stmt(ir);
let chain_events_expr = chain_event_schemas_expr(ir)?;
Ok(FnItem::new(
&ident_events,
vec![],
EventsFnsItem::schemas_ret_ty(),
parse_quote!({
#struct_events_stmt
#chain_events_expr
})
))
}
}
impl TryFrom<&'_ ModuleStructIR> for EventsFnsItem {
type Error = syn::Error;
fn try_from(ir: &'_ ModuleStructIR) -> Result<Self, Self::Error> {
Ok(Self {
events_fn: Self::events_fn(ir)?,
event_schemas_fn: Self::event_schemas_fn(ir)?
})
}
}
fn struct_events_stmt(ir: &ModuleStructIR) -> syn::Stmt {
let events_ident = utils::ident::events();
let struct_events = ir
.events()
.iter()
.map(utils::expr::into_event)
.collect::<syn::punctuated::Punctuated<_, syn::token::Comma>>();
let vec = utils::expr::vec(struct_events);
parse_quote!(let #events_ident = #vec;)
}
fn chain_events_expr(ir: &ModuleStructIR) -> syn::Result<syn::Expr> {
let ev_ty = utils::ty::event();
let events_ident = utils::ident::events();
let fields_events = ir
.unique_fields_ty()?
.iter()
.map(utils::expr::events)
.map(|expr| quote::quote!(.chain(#expr)))
.collect::<Vec<_>>();
Ok(parse_quote!(
odra::prelude::BTreeSet::<#ev_ty>::new()
.into_iter()
.chain(#events_ident)
#(#fields_events)*
.collect()
))
}
fn struct_event_schemas_stmt(ir: &ModuleStructIR) -> syn::Stmt {
let result_ident = utils::ident::result();
let events = ir
.events()
.iter()
.map(|ty| {
let name = utils::expr::event_instance_name(ty);
let schema = utils::expr::event_instance_schema(ty);
quote::quote!((#name, #schema))
})
.collect::<syn::punctuated::Punctuated<_, syn::token::Comma>>();
let iter = utils::expr::vec(events);
let new_btree_map = utils::expr::btree_from_iter(&iter);
parse_quote!(let #result_ident = #new_btree_map;)
}
fn chain_event_schemas_expr(ir: &ModuleStructIR) -> syn::Result<syn::Expr> {
let result_ident = utils::ident::result();
let fields_events = ir
.unique_fields_ty()?
.iter()
.map(utils::expr::event_schemas)
.map(|expr| quote::quote!(.chain(#expr)))
.collect::<Vec<_>>();
Ok(parse_quote!(
#result_ident
.into_iter()
#(#fields_events)*
.collect()
))
}
#[cfg(test)]
mod test {
use crate::test_utils;
use quote::quote;
use super::HasEventsImplItem;
#[test]
fn counter_pack() {
let module = test_utils::mock::module_definition();
let expected = quote!(
impl odra::contract_def::HasEvents for CounterPack {
fn events() -> odra::prelude::vec::Vec<odra::contract_def::Event> {
let events = odra::prelude::vec![
<OnTransfer as odra::contract_def::IntoEvent>::into_event(),
<OnApprove as odra::contract_def::IntoEvent>::into_event()
];
odra::prelude::BTreeSet::<odra::contract_def::Event>::new()
.into_iter()
.chain(events)
.chain(<Mapping<u8, Counter> as odra::contract_def::HasEvents>::events())
.chain(<SubModule<Counter> as odra::contract_def::HasEvents>::events())
.chain(<Var<u32> as odra::contract_def::HasEvents>::events())
.collect()
}
fn event_schemas() -> odra::prelude::BTreeMap<odra::prelude::string::String, odra::casper_event_standard::Schema> {
let result = odra::prelude::BTreeMap::from_iter(
odra::prelude::vec![
(<OnTransfer as odra::casper_event_standard::EventInstance>::name(), <OnTransfer as odra::casper_event_standard::EventInstance>::schema()),
(<OnApprove as odra::casper_event_standard::EventInstance>::name(), <OnApprove as odra::casper_event_standard::EventInstance>::schema())
]
);
result
.into_iter()
.chain(<Mapping<u8, Counter> as odra::contract_def::HasEvents>::event_schemas())
.chain(<SubModule<Counter> as odra::contract_def::HasEvents>::event_schemas())
.chain(<Var<u32> as odra::contract_def::HasEvents>::event_schemas())
.collect()
}
}
);
let actual = HasEventsImplItem::try_from(&module).unwrap();
test_utils::assert_eq(actual, expected);
}
}