Skip to content

Commit

Permalink
feat: Add InstantiateBuilder and SubMsg trait
Browse files Browse the repository at this point in the history
  • Loading branch information
jawoznia committed Sep 27, 2024
1 parent 72ebc77 commit 8140714
Show file tree
Hide file tree
Showing 11 changed files with 448 additions and 114 deletions.
26 changes: 26 additions & 0 deletions sylvia-derive/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use communication::api::Api;
use communication::enum_msg::EnumMessage;
use communication::executor::Executor;
use communication::instantiate_builder::InstantiateBuilder;
use communication::querier::Querier;
use communication::reply::Reply;
use communication::struct_msg::StructMessage;
Expand Down Expand Up @@ -83,6 +84,7 @@ impl<'a> ContractInput<'a> {
let querier = self.emit_querier();
let executor = self.emit_executor();
let reply = self.emit_reply();
let instantiate_builder = self.emit_instantiate_builder_trait();

quote! {
pub mod sv {
Expand All @@ -99,6 +101,8 @@ impl<'a> ContractInput<'a> {
#reply

#contract_api

#instantiate_builder
}
}
}
Expand Down Expand Up @@ -190,4 +194,26 @@ impl<'a> ContractInput<'a> {
quote! {}
}
}

fn emit_instantiate_builder_trait(&self) -> TokenStream {
let item = self.item;
let variants = MsgVariants::new(
item.as_variants(),
MsgType::Instantiate,
&self.generics,
&item.generics.where_clause,
);
let where_clause = variants.where_clause();

match variants.get_only_variant() {
Some(variant) => InstantiateBuilder::new(
*item.self_ty.clone(),
variants.used_generics(),
&where_clause,
variant,
)
.emit(),
None => quote! {},

Check warning on line 216 in sylvia-derive/src/contract.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/contract.rs#L216

Added line #L216 was not covered by tests
}
}
}
68 changes: 68 additions & 0 deletions sylvia-derive/src/contract/communication/instantiate_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use convert_case::Case;
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{GenericParam, Type, WhereClause};

use crate::crate_module;
use crate::parser::MsgType;
use crate::types::msg_variant::MsgVariant;
use crate::utils::{get_ident_from_type, SvCasing};

pub struct InstantiateBuilder<'a> {
contract: Type,
used_generics: &'a [&'a GenericParam],
where_clause: &'a Option<WhereClause>,
instantiate_variant: &'a MsgVariant<'a>,
}

impl<'a> InstantiateBuilder<'a> {
pub fn new(
contract: Type,
used_generics: &'a [&'a syn::GenericParam],
where_clause: &'a Option<WhereClause>,
instantiate_variant: &'a MsgVariant<'a>,
) -> Self {
Self {
contract,
used_generics,
where_clause,
instantiate_variant,
}
}

pub fn emit(&self) -> TokenStream {
let sylvia = crate_module();
let Self {
contract,
used_generics,
where_clause,
instantiate_variant,
} = self;

let contract_name = get_ident_from_type(contract);
let msg_type = MsgType::Instantiate;

let trait_name = Ident::new(
&format!("{}InstantiateBuilder", &contract_name.to_string()),
contract_name.span(),
);
let method_name = contract_name.to_case(Case::Snake);
let fields_names = instantiate_variant.as_fields_names();
let parameters = instantiate_variant.emit_method_field();
let msg_name = msg_type.emit_msg_name();

quote! {
pub trait #trait_name {
fn #method_name < #(#used_generics),* > (code_id: u64, #(#parameters),* ) -> #sylvia ::cw_std::StdResult < #sylvia ::builder::instantiate::InstantiateBuilder> #where_clause;
}

impl #trait_name for #sylvia ::builder::instantiate::InstantiateBuilder {
fn #method_name < #(#used_generics),* > (code_id: u64, #(#parameters,)* ) -> #sylvia ::cw_std::StdResult< #sylvia ::builder::instantiate::InstantiateBuilder> #where_clause {
let msg = #msg_name ::< #(#used_generics),* > ::new( #(#fields_names),* );
let msg = #sylvia ::cw_std::to_json_binary(&msg)?;
Ok( #sylvia ::builder::instantiate::InstantiateBuilder::new(msg, code_id))
}
}
}
}
}
1 change: 1 addition & 0 deletions sylvia-derive/src/contract/communication/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod api;
pub mod enum_msg;
pub mod executor;
pub mod instantiate_builder;
pub mod querier;
pub mod reply;
pub mod struct_msg;
Expand Down
128 changes: 112 additions & 16 deletions sylvia-derive/src/contract/communication/reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use proc_macro_error::emit_error;
use quote::quote;
use syn::fold::Fold;
use syn::{parse_quote, GenericParam, Ident, ItemImpl, Type};

use crate::crate_module;
use crate::fold::StripGenerics;
use crate::parser::attributes::msg::ReplyOn;
use crate::parser::MsgType;
use crate::types::msg_variant::{MsgVariant, MsgVariants};
use crate::utils::emit_turbofish;

pub struct Reply<'a> {
source: &'a ItemImpl,
Expand All @@ -35,11 +34,14 @@ impl<'a> Reply<'a> {
pub fn emit(&self) -> TokenStream {
let unique_handlers: Vec<_> = self.emit_reply_ids().collect();
let dispatch = self.emit_dispatch();
let sub_msg_trait = self.emit_sub_msg_trait();

quote! {
#(#unique_handlers)*

#dispatch

#sub_msg_trait
}
}

Expand Down Expand Up @@ -101,6 +103,69 @@ impl<'a> Reply<'a> {
}
})
}

/// Generates `SubMsgMethods` trait with method for every reply id.
fn emit_sub_msg_trait(&self) -> TokenStream {
let Self { reply_data, .. } = self;

let sylvia = crate_module();

let methods_declaration = reply_data.iter().map(|data| {
let method_name = &data.handler_name;

quote! {
fn #method_name (self) -> #sylvia ::cw_std::SubMsg<CustomMsgT>;
}
});

let submsg_methods_implementation = reply_data.iter().map(|data| {
let method_name = &data.handler_name;
let reply_on = data.emit_cw_reply_on();
let reply_id = &data.reply_id;

quote! {
fn #method_name (self) -> #sylvia ::cw_std::SubMsg<CustomMsgT> {
#sylvia ::cw_std::SubMsg {
reply_on: #reply_on ,
id: #reply_id ,
..self
}
}
}
});

let wasmmsg_methods_implementation = reply_data.iter().map(|data| {
let method_name = &data.handler_name;
let reply_on = data.emit_cw_reply_on();
let reply_id = &data.reply_id;

quote! {
fn #method_name (self) -> #sylvia ::cw_std::SubMsg<CustomMsgT> {
#sylvia ::cw_std::SubMsg {
reply_on: #reply_on ,
id: #reply_id ,
msg: self.into(),
payload: Default::default(),
gas_limit: None,
}
}
}
});

quote! {
pub trait SubMsgMethods<CustomMsgT> {
#(#methods_declaration)*
}

impl<CustomMsgT> SubMsgMethods<CustomMsgT> for #sylvia ::cw_std::SubMsg<CustomMsgT> {
#(#submsg_methods_implementation)*
}

impl<CustomMsgT> SubMsgMethods<CustomMsgT> for #sylvia ::cw_std::WasmMsg {
#(#wasmmsg_methods_implementation)*
}
}
}
}

trait ReplyVariants<'a> {
Expand All @@ -115,7 +180,7 @@ impl<'a> ReplyVariants<'a> for MsgVariants<'a, GenericParam> {

self.variants()
.flat_map(ReplyVariant::as_reply_data)
.for_each(|(reply_id, function_name, reply_on)| {
.for_each(|(reply_id, handler_name,function_name, reply_on)| {
match reply_data
.iter_mut()
.find(|existing_data| existing_data.reply_id == reply_id)
Expand All @@ -135,7 +200,7 @@ impl<'a> ReplyVariants<'a> for MsgVariants<'a, GenericParam> {
)
}
Some(existing_data) => existing_data.handlers.push((function_name, reply_on)),
None => reply_data.push(ReplyData::new(reply_id, function_name, reply_on)),
None => reply_data.push(ReplyData::new(reply_id, function_name, reply_on, handler_name)),
}
});

Expand All @@ -146,28 +211,31 @@ impl<'a> ReplyVariants<'a> for MsgVariants<'a, GenericParam> {
/// Maps single reply id with its handlers.
struct ReplyData<'a> {
pub reply_id: Ident,
pub handler_name: &'a Ident,
pub handlers: Vec<(&'a Ident, ReplyOn)>,
}

impl<'a> ReplyData<'a> {
pub fn new(reply_id: Ident, function_name: &'a Ident, reply_on: ReplyOn) -> Self {
pub fn new(
reply_id: Ident,
function_name: &'a Ident,
reply_on: ReplyOn,
handler_name: &'a Ident,
) -> Self {
Self {
reply_id,
handler_name,
handlers: vec![(function_name, reply_on)],
}
}

/// Emits success and failure match arms for a single `ReplyId`.
fn emit_match_arms(&self, contract: &Type, generics: &[&GenericParam]) -> TokenStream {
let Self { reply_id, handlers } = self;

let contract_turbofish: Type = if !generics.is_empty() {
let contract_name = StripGenerics.fold_type((contract.clone()).clone());
parse_quote! { #contract_name :: < #(#generics),* > }
} else {
parse_quote! { #contract }
};
let Self {
reply_id, handlers, ..
} = self;

let contract_turbofish = emit_turbofish(contract, generics);
let success_match_arm = emit_success_match_arm(handlers, &contract_turbofish);
let failure_match_arm = emit_failure_match_arm(handlers, &contract_turbofish);

Expand All @@ -180,6 +248,33 @@ impl<'a> ReplyData<'a> {
}
}
}

fn emit_cw_reply_on(&self) -> TokenStream {
let sylvia = crate_module();
let is_always = self
.handlers
.iter()
.any(|(_, reply_on)| reply_on == &ReplyOn::Always);
let is_success = self
.handlers
.iter()
.any(|(_, reply_on)| reply_on == &ReplyOn::Success);
let is_failure = self
.handlers
.iter()
.any(|(_, reply_on)| reply_on == &ReplyOn::Failure);

if is_always || (is_success && is_failure) {
quote! { #sylvia ::cw_std::ReplyOn::Always }
} else if is_success {
quote! { #sylvia ::cw_std::ReplyOn::Success }
} else if is_failure {
quote! { #sylvia ::cw_std::ReplyOn::Error }
} else {
// This should never happen
quote! { #sylvia ::cw_std::ReplyOn::Never }

Check warning on line 275 in sylvia-derive/src/contract/communication/reply.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/contract/communication/reply.rs#L275

Added line #L275 was not covered by tests
}
}
}

/// Emits match arm for [ReplyOn::Success].
Expand Down Expand Up @@ -255,7 +350,7 @@ fn emit_failure_match_arm(

trait ReplyVariant<'a> {
fn as_handlers(&'a self) -> Vec<&'a Ident>;
fn as_reply_data(&self) -> Vec<(Ident, &Ident, ReplyOn)>;
fn as_reply_data(&self) -> Vec<(Ident, &Ident, &Ident, ReplyOn)>;
}

impl<'a> ReplyVariant<'a> for MsgVariant<'a> {
Expand All @@ -266,12 +361,13 @@ impl<'a> ReplyVariant<'a> for MsgVariant<'a> {
self.msg_attr().handlers().iter().collect()
}

fn as_reply_data(&self) -> Vec<(Ident, &Ident, ReplyOn)> {
fn as_reply_data(&self) -> Vec<(Ident, &Ident, &Ident, ReplyOn)> {
self.as_handlers()
.iter()
.map(|handler| {
.map(|&handler| {
(
handler.as_reply_id(),
handler,
self.function_name(),
self.msg_attr().reply_on(),
)
Expand Down
12 changes: 1 addition & 11 deletions sylvia-derive/src/contract/mt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,7 @@ use crate::parser::{
Custom, FilteredOverrideEntryPoints, OverrideEntryPoint, ParsedSylviaAttributes,
};
use crate::types::msg_variant::{MsgVariant, MsgVariants};
use crate::utils::{emit_bracketed_generics, SvCasing};

fn get_ident_from_type(contract_name: &Type) -> &Ident {
let Type::Path(type_path) = contract_name else {
unreachable!()
};
let segments = &type_path.path.segments;
assert!(!segments.is_empty());
let segment = &segments.last().unwrap();
&segment.ident
}
use crate::utils::{emit_bracketed_generics, get_ident_from_type, SvCasing};

/// Emits helpers for testing contract messages using MultiTest.
///
Expand Down
Loading

0 comments on commit 8140714

Please sign in to comment.