From 286f76dcd54a9ef9d662e1cff0b528bf9df56a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Mon, 18 Sep 2023 23:08:59 +0200 Subject: [PATCH 01/15] feat: Add support for generics in interface --- sylvia-derive/src/check_generics.rs | 1 + sylvia-derive/src/input.rs | 40 +++++---- sylvia-derive/src/message.rs | 128 ++++++++++++++++++++-------- sylvia-derive/src/multitest.rs | 12 +-- sylvia-derive/src/utils.rs | 32 ++++--- sylvia/examples/generics.rs | 17 ---- sylvia/src/into_response.rs | 2 +- sylvia/src/lib.rs | 1 + sylvia/src/types.rs | 3 + sylvia/tests/generics.rs | 44 ++++++++++ 10 files changed, 187 insertions(+), 93 deletions(-) delete mode 100644 sylvia/examples/generics.rs create mode 100644 sylvia/tests/generics.rs diff --git a/sylvia-derive/src/check_generics.rs b/sylvia-derive/src/check_generics.rs index fbd2188a..edba0ce4 100644 --- a/sylvia-derive/src/check_generics.rs +++ b/sylvia-derive/src/check_generics.rs @@ -1,6 +1,7 @@ use syn::visit::Visit; use syn::GenericParam; +#[derive(Debug)] pub struct CheckGenerics<'g> { generics: &'g [&'g GenericParam], used: Vec<&'g GenericParam>, diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 6fe658c5..83e05237 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -62,7 +62,14 @@ impl<'a> TraitInput<'a> { let messages = self.emit_messages(); let multitest_helpers = self.emit_helpers(); let remote = Remote::new(&Interfaces::default()).emit(); - let querier = MsgVariants::new(self.item.as_variants(), &self.generics).emit_querier(); + + let querier = MsgVariants::new( + self.item.as_variants(), + MsgType::Query, + &self.generics, + &self.item.generics.where_clause, + ) + .emit_querier(); #[cfg(not(tarpaulin_include))] { @@ -159,22 +166,26 @@ impl<'a> ImplInput<'a> { quote! {} }; - let interfaces = Interfaces::new(self.item); - let variants = MsgVariants::new(self.item.as_variants(), &self.generics); + let unbonded_generics = &vec![]; + let variants = MsgVariants::new( + self.item.as_variants(), + MsgType::Query, + unbonded_generics, + &None, + ); match is_trait { - true => self.process_interface(&interfaces, variants, multitest_helpers), - false => self.process_contract(&interfaces, variants, multitest_helpers), + true => self.process_interface(variants, multitest_helpers), + false => self.process_contract(variants, multitest_helpers), } } fn process_interface( &self, - interfaces: &Interfaces, variants: MsgVariants<'a>, multitest_helpers: TokenStream, ) -> TokenStream { - let querier_bound_for_impl = self.emit_querier_for_bound_impl(interfaces, variants); + let querier_bound_for_impl = self.emit_querier_for_bound_impl(variants); #[cfg(not(tarpaulin_include))] quote! { @@ -186,14 +197,14 @@ impl<'a> ImplInput<'a> { fn process_contract( &self, - interfaces: &Interfaces, variants: MsgVariants<'a>, multitest_helpers: TokenStream, ) -> TokenStream { let messages = self.emit_messages(); - let remote = Remote::new(interfaces).emit(); + let remote = Remote::new(&self.interfaces).emit(); + let querier = variants.emit_querier(); - let querier_from_impl = interfaces.emit_querier_from_impl(); + let querier_from_impl = self.interfaces.emit_querier_from_impl(); #[cfg(not(tarpaulin_include))] { @@ -268,12 +279,9 @@ impl<'a> ImplInput<'a> { .emit() } - fn emit_querier_for_bound_impl( - &self, - interfaces: &Interfaces, - variants: MsgVariants<'a>, - ) -> TokenStream { - let trait_module = interfaces + fn emit_querier_for_bound_impl(&self, variants: MsgVariants<'a>) -> TokenStream { + let trait_module = self + .interfaces .interfaces() .first() .map(|interface| &interface.module); diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 3bb14c52..1a410453 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -303,7 +303,7 @@ impl<'a> EnumMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, cosmwasm_schema::QueryResponses)] #[serde(rename_all="snake_case")] - pub enum #unique_enum_name #generics #where_clause { + pub enum #unique_enum_name #generics { #(#variants,)* } pub type #name #generics = #unique_enum_name #generics; @@ -314,7 +314,7 @@ impl<'a> EnumMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] #[serde(rename_all="snake_case")] - pub enum #unique_enum_name #generics #where_clause { + pub enum #unique_enum_name #generics { #(#variants,)* } pub type #name #generics = #unique_enum_name #generics; @@ -506,7 +506,9 @@ impl<'a> MsgVariant<'a> { let return_type = if let MsgAttr::Query { resp_type } = msg_attr { match resp_type { - Some(resp_type) => quote! {#resp_type}, + Some(resp_type) => { + quote! {#resp_type} + } None => { let return_type = extract_return_type(&sig.output); quote! {#return_type} @@ -667,12 +669,20 @@ impl<'a> MsgVariant<'a> { } } -pub struct MsgVariants<'a>(Vec>); +pub struct MsgVariants<'a> { + variants: Vec>, + unbonded_generics: Vec<&'a GenericParam>, + where_clause: Option, +} impl<'a> MsgVariants<'a> { - pub fn new(source: VariantDescs<'a>, generics: &[&'a GenericParam]) -> Self { + pub fn new( + source: VariantDescs<'a>, + msg_type: MsgType, + generics: &'a Vec<&'a GenericParam>, + unfiltered_where_clause: &'a Option, + ) -> Self { let mut generics_checker = CheckGenerics::new(generics); - let variants: Vec<_> = source .filter_map(|variant_desc| { let msg_attr = variant_desc.attr_msg()?; @@ -684,6 +694,10 @@ impl<'a> MsgVariants<'a> { } }; + if attr.msg_type() != msg_type { + return None; + } + Some(MsgVariant::new( variant_desc.into_sig(), &mut generics_checker, @@ -691,12 +705,38 @@ impl<'a> MsgVariants<'a> { )) }) .collect(); - Self(variants) + + let (unbonded_generics, _) = generics_checker.used_unused(); + let wheres = filter_wheres( + unfiltered_where_clause, + generics.as_slice(), + &unbonded_generics, + ); + let where_clause = if !wheres.is_empty() { + Some(parse_quote! { where #(#wheres),* }) + } else { + None + }; + + Self { + variants, + unbonded_generics, + where_clause, + } + } + + pub fn variants(&self) -> &Vec> { + &self.variants } pub fn emit_querier(&self) -> TokenStream { let sylvia = crate_module(); - let variants = &self.0; + let Self { + variants, + unbonded_generics, + where_clause, + .. + } = self; let methods_impl = variants .iter() @@ -708,6 +748,12 @@ impl<'a> MsgVariants<'a> { .filter(|variant| variant.msg_type == MsgType::Query) .map(MsgVariant::emit_querier_declaration); + let querier = if !unbonded_generics.is_empty() { + quote! { Querier < #(#unbonded_generics,)* > } + } else { + quote! { Querier } + }; + #[cfg(not(tarpaulin_include))] { quote! { @@ -730,12 +776,11 @@ impl<'a> MsgVariants<'a> { } } - impl <'a, C: #sylvia ::cw_std::CustomQuery> Querier for BoundQuerier<'a, C> { + impl <'a, C: #sylvia ::cw_std::CustomQuery, #(#unbonded_generics,)*> #querier for BoundQuerier<'a, C> #where_clause { #(#methods_impl)* } - - pub trait Querier { + pub trait #querier { #(#methods_declaration)* } } @@ -748,24 +793,33 @@ impl<'a> MsgVariants<'a> { contract_module: Option<&Path>, ) -> TokenStream { let sylvia = crate_module(); - let variants = &self.0; + let Self { + variants, + unbonded_generics, + where_clause, + .. + } = self; let methods_impl = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) .map(|variant| variant.emit_querier_impl(trait_module)); - let querier = trait_module + let mut querier = trait_module .map(|module| quote! { #module ::Querier }) .unwrap_or_else(|| quote! { Querier }); let bound_querier = contract_module .map(|module| quote! { #module ::BoundQuerier}) .unwrap_or_else(|| quote! { BoundQuerier }); + if !unbonded_generics.is_empty() { + querier = quote! { #querier < #(#unbonded_generics,)* > }; + } + #[cfg(not(tarpaulin_include))] { quote! { - impl <'a, C: #sylvia ::cw_std::CustomQuery> #querier for #bound_querier<'a, C> { + impl <'a, C: #sylvia ::cw_std::CustomQuery, #(#unbonded_generics,)*> #querier for #bound_querier<'a, C> #where_clause { #(#methods_impl)* } } @@ -886,7 +940,7 @@ impl<'a> GlueMessage<'a> { interfaces, } = self; let contract = StripGenerics.fold_type((*contract).clone()); - let contract_name = Ident::new(&format!("Contract{}", name), name.span()); + let enum_name = Ident::new(&format!("Contract{}", name), name.span()); let variants = interfaces.emit_glue_message_variants(msg_ty, name); @@ -916,15 +970,15 @@ impl<'a> GlueMessage<'a> { match (msg_ty, customs.has_msg) { (MsgType::Exec, true) => quote! { - #contract_name :: #variant(msg) => #sylvia ::into_response::IntoResponse::into_response(msg.dispatch(contract, Into::into( #ctx ))?) + #enum_name:: #variant(msg) => #sylvia ::into_response::IntoResponse::into_response(msg.dispatch(contract, Into::into( #ctx ))?) }, _ => quote! { - #contract_name :: #variant(msg) => msg.dispatch(contract, Into::into( #ctx )) + #enum_name :: #variant(msg) => msg.dispatch(contract, Into::into( #ctx )) }, } }); - let dispatch_arm = quote! {#contract_name :: #contract (msg) =>msg.dispatch(contract, ctx)}; + let dispatch_arm = quote! {#enum_name :: #contract (msg) => msg.dispatch(contract, ctx)}; let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(name); @@ -951,7 +1005,7 @@ impl<'a> GlueMessage<'a> { { quote! { #[cfg(not(target_arch = "wasm32"))] - impl cosmwasm_schema::QueryResponses for #contract_name { + impl #sylvia ::cw_schema::QueryResponses for #enum_name { fn response_schemas_impl() -> std::collections::BTreeMap { let responses = [#(#response_schemas_calls),*]; responses.into_iter().flatten().collect() @@ -971,12 +1025,12 @@ impl<'a> GlueMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] #[serde(rename_all="snake_case", untagged)] - pub enum #contract_name { + pub enum #enum_name { #(#variants,)* #msg_name } - impl #contract_name { + impl #enum_name { pub fn dispatch( self, contract: &#contract, @@ -996,7 +1050,7 @@ impl<'a> GlueMessage<'a> { #response_schemas - impl<'de> serde::Deserialize<'de> for #contract_name { + impl<'de> serde::Deserialize<'de> for #enum_name { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { @@ -1043,7 +1097,8 @@ pub struct EntryPoints<'a> { error: Type, custom: Custom<'a>, override_entry_points: OverrideEntryPoints, - variants: MsgVariants<'a>, + has_migrate: bool, + reply: Option, } impl<'a> EntryPoints<'a> { @@ -1067,9 +1122,15 @@ impl<'a> EntryPoints<'a> { ) .unwrap_or_else(|| parse_quote! { #sylvia ::cw_std::StdError }); - let generics: Vec<_> = source.generics.params.iter().collect(); + let has_migrate = !MsgVariants::new(source.as_variants(), MsgType::Migrate, &vec![], &None) + .variants() + .is_empty(); - let variants = MsgVariants::new(source.as_variants(), &generics); + let reply = MsgVariants::new(source.as_variants(), MsgType::Reply, &vec![], &None) + .variants() + .iter() + .map(|variant| variant.function_name.clone()) + .next(); let custom = Custom::new(&source.attrs); Self { @@ -1077,7 +1138,8 @@ impl<'a> EntryPoints<'a> { error, custom, override_entry_points, - variants, + has_migrate, + reply, } } @@ -1087,17 +1149,13 @@ impl<'a> EntryPoints<'a> { error, custom, override_entry_points, - variants, + has_migrate, + reply, } = self; let sylvia = crate_module(); let custom_msg = custom.msg_or_default(); let custom_query = custom.query_or_default(); - let reply = variants - .0 - .iter() - .find(|variant| variant.msg_type == MsgType::Reply) - .map(|variant| variant.function_name.clone()); #[cfg(not(tarpaulin_include))] { @@ -1119,12 +1177,8 @@ impl<'a> EntryPoints<'a> { let migrate_not_overridden = override_entry_points .get_entry_point(MsgType::Migrate) .is_none(); - let migrate_msg_defined = variants - .0 - .iter() - .any(|variant| variant.msg_type == MsgType::Migrate); - let migrate = if migrate_not_overridden && migrate_msg_defined { + let migrate = if migrate_not_overridden && *has_migrate { OverrideEntryPoint::emit_default_entry_point( &custom_msg, &custom_query, diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs index 8b871d36..3183e1ef 100644 --- a/sylvia-derive/src/multitest.rs +++ b/sylvia-derive/src/multitest.rs @@ -42,7 +42,9 @@ pub struct MultitestHelpers<'a> { fn interface_name(source: &ItemImpl) -> &Ident { let trait_name = &source.trait_; - let Some(trait_name) = trait_name else {unreachable!()}; + let Some(trait_name) = trait_name else { + unreachable!() + }; let (_, Path { segments, .. }, _) = &trait_name; assert!(!segments.is_empty()); @@ -50,9 +52,9 @@ fn interface_name(source: &ItemImpl) -> &Ident { } fn extract_contract_name(contract: &Type) -> &Ident { - let Type::Path(type_path) = contract else { - unreachable!() - }; + let Type::Path(type_path) = contract else { + unreachable!() + }; let segments = &type_path.path.segments; assert!(!segments.is_empty()); let segment = &segments[0]; @@ -540,7 +542,7 @@ impl<'a> MultitestHelpers<'a> { let mut generics_checker = CheckGenerics::new(generics); let parsed = parse_struct_message(source, MsgType::Instantiate); - let Some((method,_)) = parsed else { + let Some((method, _)) = parsed else { return quote! {}; }; diff --git a/sylvia-derive/src/utils.rs b/sylvia-derive/src/utils.rs index 108e6db4..609ebea0 100644 --- a/sylvia-derive/src/utils.rs +++ b/sylvia-derive/src/utils.rs @@ -2,7 +2,7 @@ use proc_macro_error::emit_error; use syn::spanned::Spanned; use syn::visit::Visit; use syn::{ - FnArg, GenericArgument, GenericParam, PathArguments, PathSegment, ReturnType, Signature, Type, + FnArg, GenericArgument, GenericParam, Path, PathArguments, ReturnType, Signature, Type, WhereClause, WherePredicate, }; @@ -52,14 +52,14 @@ pub fn process_fields<'s>( .collect() } -pub fn extract_return_type(ret_type: &ReturnType) -> &PathSegment { - let ReturnType::Type(_, ty) = ret_type else { - unreachable!() - }; +pub fn extract_return_type(ret_type: &ReturnType) -> &Path { + let ReturnType::Type(_, ty) = ret_type else { + unreachable!() + }; - let Type::Path(type_path) = ty.as_ref() else { - unreachable!() - }; + let Type::Path(type_path) = ty.as_ref() else { + unreachable!() + }; let segments = &type_path.path.segments; assert!(!segments.is_empty()); let segment = &segments[0]; @@ -73,16 +73,14 @@ pub fn extract_return_type(ret_type: &ReturnType) -> &PathSegment { Please use #[msg(return_type=)]" ); } - let PathArguments::AngleBracketed(args) = &segments[0].arguments else{ - unreachable!() - }; + let PathArguments::AngleBracketed(args) = &segments[0].arguments else { + unreachable!() + }; let args = &args.args; assert!(!args.is_empty()); - let GenericArgument::Type(Type::Path(type_path)) = &args[0] else{ - unreachable!() - }; - let segments = &type_path.path.segments; - assert!(!segments.is_empty()); + let GenericArgument::Type(Type::Path(type_path)) = &args[0] else { + unreachable!() + }; - &segments[0] + &type_path.path } diff --git a/sylvia/examples/generics.rs b/sylvia/examples/generics.rs deleted file mode 100644 index 75acbed5..00000000 --- a/sylvia/examples/generics.rs +++ /dev/null @@ -1,17 +0,0 @@ -use cosmwasm_std::{CosmosMsg, Response}; - -use sylvia::types::ExecCtx; -use sylvia_derive::interface; - -#[interface(module=msg)] -pub trait Cw1 -where - Msg: std::fmt::Debug + PartialEq + Clone + schemars::JsonSchema, -{ - type Error; - - #[msg(exec)] - fn execute(&self, ctx: ExecCtx, msgs: Vec>) -> Result; -} - -fn main() {} diff --git a/sylvia/src/into_response.rs b/sylvia/src/into_response.rs index b3a3e73d..06f974f3 100644 --- a/sylvia/src/into_response.rs +++ b/sylvia/src/into_response.rs @@ -52,7 +52,7 @@ impl IntoResponse for Response { .map(|msg| msg.into_msg()) .collect::>()?; let mut resp = Response::new() - .add_submessages(messages.into_iter()) + .add_submessages(messages) .add_events(self.events) .add_attributes(self.attributes); resp.data = self.data; diff --git a/sylvia/src/lib.rs b/sylvia/src/lib.rs index f3502c47..66afeb6f 100644 --- a/sylvia/src/lib.rs +++ b/sylvia/src/lib.rs @@ -10,6 +10,7 @@ pub mod utils; #[cfg(feature = "mt")] pub use anyhow; +pub use cosmwasm_schema as cw_schema; pub use cosmwasm_std as cw_std; #[cfg(feature = "mt")] pub use cw_multi_test; diff --git a/sylvia/src/types.rs b/sylvia/src/types.rs index 725d57d3..71495870 100644 --- a/sylvia/src/types.rs +++ b/sylvia/src/types.rs @@ -1,4 +1,5 @@ use cosmwasm_std::{CustomQuery, Deps, DepsMut, Empty, Env, MessageInfo}; +use serde::de::DeserializeOwned; pub struct ReplyCtx<'a, C: CustomQuery = Empty> { pub deps: DepsMut<'a, C>, @@ -93,3 +94,5 @@ impl<'a, C: CustomQuery> From<(Deps<'a, C>, Env)> for QueryCtx<'a, C> { Self { deps, env } } } + +pub trait CustomMsg: cosmwasm_std::CustomMsg + DeserializeOwned {} diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs new file mode 100644 index 00000000..de3eaccd --- /dev/null +++ b/sylvia/tests/generics.rs @@ -0,0 +1,44 @@ +use cosmwasm_schema::cw_serde; + +pub mod cw1 { + use cosmwasm_std::{CosmosMsg, CustomMsg, Response, StdError}; + + use serde::Deserialize; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::interface; + + #[interface(module=msg)] + pub trait Cw1 + where + for<'msg_de> Msg: CustomMsg + Deserialize<'msg_de>, + Param: sylvia::types::CustomMsg, + { + type Error: From; + + #[msg(exec)] + fn execute(&self, ctx: ExecCtx, msgs: Vec>) + -> Result; + + #[msg(query)] + fn query(&self, ctx: QueryCtx, param: Param) -> Result; + } +} + +#[cw_serde] +pub struct ExternalMsg; +impl cosmwasm_std::CustomMsg for ExternalMsg {} +impl sylvia::types::CustomMsg for ExternalMsg {} + +#[cfg(test)] +mod tests { + use cosmwasm_std::{CosmosMsg, Empty}; + + use crate::ExternalMsg; + + #[test] + fn construct_messages() { + let _ = crate::cw1::QueryMsg::query(ExternalMsg {}); + let _ = crate::cw1::ExecMsg::execute(vec![CosmosMsg::Custom(ExternalMsg {})]); + let _ = crate::cw1::ExecMsg::execute(vec![CosmosMsg::Custom(Empty {})]); + } +} From db172655a053d7689b33d00dd2142f24572bd9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Tue, 19 Sep 2023 13:40:16 +0200 Subject: [PATCH 02/15] feat: Check interfaces return type for used generics --- sylvia-derive/src/message.rs | 84 ++++++++++++++++++++---------------- sylvia-derive/src/utils.rs | 20 ++++++++- sylvia/tests/generics.rs | 37 ++++++++++++---- 3 files changed, 92 insertions(+), 49 deletions(-) diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 1a410453..67bbbe3f 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -6,7 +6,9 @@ use crate::parser::{ Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints, }; use crate::strip_generics::StripGenerics; -use crate::utils::{extract_return_type, filter_wheres, process_fields}; +use crate::utils::{ + as_where_clause, brace_generics, extract_return_type, filter_wheres, process_fields, +}; use crate::variant_descs::{AsVariantDescs, VariantDescs}; use convert_case::{Case, Casing}; use proc_macro2::{Span, TokenStream}; @@ -100,14 +102,6 @@ impl<'a> StructMessage<'a> { custom, } = self; - let where_clause = if !wheres.is_empty() { - quote! { - where #(#wheres,)* - } - } else { - quote! {} - }; - let ctx_type = msg_attr .msg_type() .emit_ctx_type(&custom.query_or_default()); @@ -119,21 +113,9 @@ impl<'a> StructMessage<'a> { }); let fields = fields.iter().map(MsgField::emit); - let generics = if generics.is_empty() { - quote! {} - } else { - quote! { - <#(#generics,)*> - } - }; - - let unused_generics = if unused_generics.is_empty() { - quote! {} - } else { - quote! { - <#(#unused_generics,)*> - } - }; + let where_clause = as_where_clause(wheres); + let generics = brace_generics(generics); + let unused_generics = brace_generics(unused_generics); #[cfg(not(tarpaulin_include))] { @@ -282,18 +264,33 @@ impl<'a> EnumMessage<'a> { let ctx_type = msg_ty.emit_ctx_type(query_type); let dispatch_type = msg_ty.emit_result_type(resp_type, &parse_quote!(C::Error)); - let all_generics = if all_generics.is_empty() { + let all_generics = brace_generics(all_generics); + let phantom = if generics.is_empty() { quote! {} + } else if MsgType::Query == *msg_ty { + quote! { + #[returns((#(#generics,)*))] + _Phantom(std::marker::PhantomData<( #(#generics,)* )>), + } } else { - quote! { <#(#all_generics,)*> } + quote! { + _Phantom(std::marker::PhantomData<( #(#generics,)* )>), + } }; - let generics = if generics.is_empty() { - quote! {} + let match_arms = if !generics.is_empty() { + quote! { + #(#match_arms,)* + _Phantom(_) => unreachable!(), + } } else { - quote! { <#(#generics,)*> } + quote! { + #(#match_arms,)* + } }; + let generics = brace_generics(generics); + let unique_enum_name = Ident::new(&format!("{}{}", trait_name, name), name.span()); #[cfg(not(tarpaulin_include))] @@ -305,6 +302,7 @@ impl<'a> EnumMessage<'a> { #[serde(rename_all="snake_case")] pub enum #unique_enum_name #generics { #(#variants,)* + #phantom } pub type #name #generics = #unique_enum_name #generics; } @@ -316,6 +314,7 @@ impl<'a> EnumMessage<'a> { #[serde(rename_all="snake_case")] pub enum #unique_enum_name #generics { #(#variants,)* + #phantom } pub type #name #generics = #unique_enum_name #generics; } @@ -334,7 +333,7 @@ impl<'a> EnumMessage<'a> { use #unique_enum_name::*; match self { - #(#match_arms,)* + #match_arms } } pub const fn messages() -> [&'static str; #msgs_cnt] { @@ -507,10 +506,12 @@ impl<'a> MsgVariant<'a> { let return_type = if let MsgAttr::Query { resp_type } = msg_attr { match resp_type { Some(resp_type) => { + generics_checker.visit_path(&parse_quote! { #resp_type }); quote! {#resp_type} } None => { let return_type = extract_return_type(&sig.output); + generics_checker.visit_path(return_type); quote! {#return_type} } } @@ -621,7 +622,11 @@ impl<'a> MsgVariant<'a> { } } - pub fn emit_querier_impl(&self, trait_module: Option<&Path>) -> TokenStream { + pub fn emit_querier_impl( + &self, + trait_module: Option<&Path>, + unbonded_generics: &Vec<&GenericParam>, + ) -> TokenStream { let sylvia = crate_module(); let Self { name, @@ -637,6 +642,12 @@ impl<'a> MsgVariant<'a> { .map(|module| quote! { #module ::QueryMsg }) .unwrap_or_else(|| quote! { QueryMsg }); + let msg = if !unbonded_generics.is_empty() { + quote! { #msg ::< #(#unbonded_generics,)* > } + } else { + quote! { #msg } + }; + #[cfg(not(tarpaulin_include))] { quote! { @@ -741,18 +752,15 @@ impl<'a> MsgVariants<'a> { let methods_impl = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) - .map(|variant| variant.emit_querier_impl(None)); + .map(|variant| variant.emit_querier_impl(None, unbonded_generics)); let methods_declaration = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) .map(MsgVariant::emit_querier_declaration); - let querier = if !unbonded_generics.is_empty() { - quote! { Querier < #(#unbonded_generics,)* > } - } else { - quote! { Querier } - }; + let braced_generics = brace_generics(unbonded_generics); + let querier = quote! { Querier #braced_generics }; #[cfg(not(tarpaulin_include))] { @@ -803,7 +811,7 @@ impl<'a> MsgVariants<'a> { let methods_impl = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) - .map(|variant| variant.emit_querier_impl(trait_module)); + .map(|variant| variant.emit_querier_impl(trait_module, unbonded_generics)); let mut querier = trait_module .map(|module| quote! { #module ::Querier }) diff --git a/sylvia-derive/src/utils.rs b/sylvia-derive/src/utils.rs index 609ebea0..90ced524 100644 --- a/sylvia-derive/src/utils.rs +++ b/sylvia-derive/src/utils.rs @@ -1,9 +1,11 @@ +use proc_macro2::TokenStream; use proc_macro_error::emit_error; +use quote::quote; use syn::spanned::Spanned; use syn::visit::Visit; use syn::{ - FnArg, GenericArgument, GenericParam, Path, PathArguments, ReturnType, Signature, Type, - WhereClause, WherePredicate, + parse_quote, FnArg, GenericArgument, GenericParam, Path, PathArguments, ReturnType, Signature, + Type, WhereClause, WherePredicate, }; use crate::check_generics::CheckGenerics; @@ -84,3 +86,17 @@ pub fn extract_return_type(ret_type: &ReturnType) -> &Path { &type_path.path } + +pub fn as_where_clause(where_predicates: &[&WherePredicate]) -> Option { + match where_predicates.is_empty() { + true => None, + false => Some(parse_quote! { where #(#where_predicates),* }), + } +} + +pub fn brace_generics(unbonded_generics: &[&GenericParam]) -> TokenStream { + match unbonded_generics.is_empty() { + true => quote! {}, + false => quote! { < #(#unbonded_generics,)* > }, + } +} diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs index de3eaccd..a10f743b 100644 --- a/sylvia/tests/generics.rs +++ b/sylvia/tests/generics.rs @@ -1,26 +1,31 @@ use cosmwasm_schema::cw_serde; pub mod cw1 { - use cosmwasm_std::{CosmosMsg, CustomMsg, Response, StdError}; + use cosmwasm_std::{CosmosMsg, CustomMsg, CustomQuery, Response, StdError}; - use serde::Deserialize; + use serde::{de::DeserializeOwned, Deserialize}; use sylvia::types::{ExecCtx, QueryCtx}; use sylvia_derive::interface; #[interface(module=msg)] - pub trait Cw1 + #[sv::custom(msg=Msg)] + pub trait Cw1 where for<'msg_de> Msg: CustomMsg + Deserialize<'msg_de>, Param: sylvia::types::CustomMsg, + for<'msg_de> QueryRet: CustomQuery + DeserializeOwned, { type Error: From; #[msg(exec)] - fn execute(&self, ctx: ExecCtx, msgs: Vec>) - -> Result; + fn execute( + &self, + ctx: ExecCtx, + msgs: Vec>, + ) -> Result, Self::Error>; #[msg(query)] - fn query(&self, ctx: QueryCtx, param: Param) -> Result; + fn some_query(&self, ctx: QueryCtx, param: Param) -> Result; } } @@ -29,16 +34,30 @@ pub struct ExternalMsg; impl cosmwasm_std::CustomMsg for ExternalMsg {} impl sylvia::types::CustomMsg for ExternalMsg {} +#[cw_serde] +pub struct ExternalQuery; +impl cosmwasm_std::CustomQuery for ExternalQuery {} + #[cfg(test)] mod tests { - use cosmwasm_std::{CosmosMsg, Empty}; + use cosmwasm_std::{testing::mock_dependencies, Addr, CosmosMsg, Empty, QuerierWrapper}; - use crate::ExternalMsg; + use crate::{cw1::Querier, ExternalMsg, ExternalQuery}; #[test] fn construct_messages() { - let _ = crate::cw1::QueryMsg::query(ExternalMsg {}); + let contract = Addr::unchecked("contract"); + + let _ = crate::cw1::QueryMsg::<_, Empty>::some_query(ExternalMsg {}); let _ = crate::cw1::ExecMsg::execute(vec![CosmosMsg::Custom(ExternalMsg {})]); let _ = crate::cw1::ExecMsg::execute(vec![CosmosMsg::Custom(Empty {})]); + + // Generic Querier + let deps = mock_dependencies(); + let querier: QuerierWrapper = QuerierWrapper::new(&deps.querier); + + let cw1_querier = crate::cw1::BoundQuerier::borrowed(&contract, &querier); + let _: Result = Querier::some_query(&cw1_querier, ExternalMsg {}); + let _: Result = cw1_querier.some_query(ExternalMsg {}); } } From 11996976226fe5281d309c42ca1834413c576c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Mon, 25 Sep 2023 12:36:35 +0200 Subject: [PATCH 03/15] feat: Emit InterfaceTypes --- sylvia-derive/src/input.rs | 8 ++- sylvia-derive/src/message.rs | 111 +++++++++++++++++++++++++++-------- sylvia-derive/src/utils.rs | 2 +- sylvia/src/types.rs | 7 +++ sylvia/tests/generics.rs | 13 +++- 5 files changed, 115 insertions(+), 26 deletions(-) diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 83e05237..718cb043 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -7,7 +7,9 @@ use syn::{parse_quote, GenericParam, Ident, ItemImpl, ItemTrait, TraitItem, Type use crate::crate_module; use crate::interfaces::Interfaces; -use crate::message::{ContractEnumMessage, EnumMessage, GlueMessage, MsgVariants, StructMessage}; +use crate::message::{ + ContractEnumMessage, EnumMessage, GlueMessage, InterfaceMessages, MsgVariants, StructMessage, +}; use crate::multitest::{MultitestHelpers, TraitMultitestHelpers}; use crate::parser::{ContractArgs, ContractErrorAttr, Custom, MsgType, OverrideEntryPoints}; use crate::remote::Remote; @@ -71,6 +73,8 @@ impl<'a> TraitInput<'a> { ) .emit_querier(); + let interface_messages = InterfaceMessages::new(self.item, &self.generics).emit(); + #[cfg(not(tarpaulin_include))] { quote! { @@ -81,6 +85,8 @@ impl<'a> TraitInput<'a> { #remote #querier + + #interface_messages } } } diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 67bbbe3f..9825f8e8 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -7,7 +7,7 @@ use crate::parser::{ }; use crate::strip_generics::StripGenerics; use crate::utils::{ - as_where_clause, brace_generics, extract_return_type, filter_wheres, process_fields, + as_where_clause, emit_bracketed_generics, extract_return_type, filter_wheres, process_fields, }; use crate::variant_descs::{AsVariantDescs, VariantDescs}; use convert_case::{Case, Casing}; @@ -114,8 +114,8 @@ impl<'a> StructMessage<'a> { let fields = fields.iter().map(MsgField::emit); let where_clause = as_where_clause(wheres); - let generics = brace_generics(generics); - let unused_generics = brace_generics(unused_generics); + let generics = emit_bracketed_generics(generics); + let unused_generics = emit_bracketed_generics(unused_generics); #[cfg(not(tarpaulin_include))] { @@ -264,7 +264,7 @@ impl<'a> EnumMessage<'a> { let ctx_type = msg_ty.emit_ctx_type(query_type); let dispatch_type = msg_ty.emit_result_type(resp_type, &parse_quote!(C::Error)); - let all_generics = brace_generics(all_generics); + let all_generics = emit_bracketed_generics(all_generics); let phantom = if generics.is_empty() { quote! {} } else if MsgType::Query == *msg_ty { @@ -289,7 +289,7 @@ impl<'a> EnumMessage<'a> { } }; - let generics = brace_generics(generics); + let generics = emit_bracketed_generics(generics); let unique_enum_name = Ident::new(&format!("{}{}", trait_name, name), name.span()); @@ -683,14 +683,14 @@ impl<'a> MsgVariant<'a> { pub struct MsgVariants<'a> { variants: Vec>, unbonded_generics: Vec<&'a GenericParam>, - where_clause: Option, + where_predicates: Vec<&'a WherePredicate>, } impl<'a> MsgVariants<'a> { pub fn new( source: VariantDescs<'a>, msg_type: MsgType, - generics: &'a Vec<&'a GenericParam>, + generics: &'a [&'a GenericParam], unfiltered_where_clause: &'a Option, ) -> Self { let mut generics_checker = CheckGenerics::new(generics); @@ -718,21 +718,21 @@ impl<'a> MsgVariants<'a> { .collect(); let (unbonded_generics, _) = generics_checker.used_unused(); - let wheres = filter_wheres( - unfiltered_where_clause, - generics.as_slice(), - &unbonded_generics, - ); - let where_clause = if !wheres.is_empty() { - Some(parse_quote! { where #(#wheres),* }) - } else { - None - }; + let where_predicates = filter_wheres(unfiltered_where_clause, generics, &unbonded_generics); Self { variants, unbonded_generics, - where_clause, + where_predicates, + } + } + + pub fn where_clause(&self) -> Option { + let where_predicates = &self.where_predicates; + if !where_predicates.is_empty() { + Some(parse_quote! { where #(#where_predicates),* }) + } else { + None } } @@ -745,9 +745,9 @@ impl<'a> MsgVariants<'a> { let Self { variants, unbonded_generics, - where_clause, .. } = self; + let where_clause = self.where_clause(); let methods_impl = variants .iter() @@ -759,7 +759,7 @@ impl<'a> MsgVariants<'a> { .filter(|variant| variant.msg_type == MsgType::Query) .map(MsgVariant::emit_querier_declaration); - let braced_generics = brace_generics(unbonded_generics); + let braced_generics = emit_bracketed_generics(unbonded_generics); let querier = quote! { Querier #braced_generics }; #[cfg(not(tarpaulin_include))] @@ -804,9 +804,9 @@ impl<'a> MsgVariants<'a> { let Self { variants, unbonded_generics, - where_clause, .. } = self; + let where_clause = self.where_clause(); let methods_impl = variants .iter() @@ -1100,6 +1100,71 @@ impl<'a> GlueMessage<'a> { } } +pub struct InterfaceMessages<'a> { + exec_variants: MsgVariants<'a>, + query_variants: MsgVariants<'a>, + generics: &'a [&'a GenericParam], +} + +impl<'a> InterfaceMessages<'a> { + pub fn new(source: &'a ItemTrait, generics: &'a [&'a GenericParam]) -> Self { + let exec_variants = MsgVariants::new( + source.as_variants(), + MsgType::Exec, + generics, + &source.generics.where_clause, + ); + + let query_variants = MsgVariants::new( + source.as_variants(), + MsgType::Query, + generics, + &source.generics.where_clause, + ); + + Self { + exec_variants, + query_variants, + generics, + } + } + + pub fn emit(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { + exec_variants, + query_variants, + generics, + } = self; + + let exec_generics = &exec_variants.unbonded_generics; + let query_generics = &query_variants.unbonded_generics; + + let bracket_generics = emit_bracketed_generics(generics); + let exec_bracketed_generics = emit_bracketed_generics(exec_generics); + let query_bracketed_generics = emit_bracketed_generics(query_generics); + + let phantom = if !generics.is_empty() { + quote! { + _phantom: std::marker::PhantomData<( #(#generics,)* )>, + } + } else { + quote! {} + }; + + quote! { + pub struct InterfaceTypes #bracket_generics { + #phantom + } + + impl #bracket_generics #sylvia ::types::InterfaceMessages for InterfaceTypes #bracket_generics { + type Exec = ExecMsg #exec_bracketed_generics; + type Query = QueryMsg #query_bracketed_generics; + } + } + } +} + pub struct EntryPoints<'a> { name: Type, error: Type, @@ -1130,11 +1195,11 @@ impl<'a> EntryPoints<'a> { ) .unwrap_or_else(|| parse_quote! { #sylvia ::cw_std::StdError }); - let has_migrate = !MsgVariants::new(source.as_variants(), MsgType::Migrate, &vec![], &None) + let has_migrate = !MsgVariants::new(source.as_variants(), MsgType::Migrate, &[], &None) .variants() .is_empty(); - let reply = MsgVariants::new(source.as_variants(), MsgType::Reply, &vec![], &None) + let reply = MsgVariants::new(source.as_variants(), MsgType::Reply, &[], &None) .variants() .iter() .map(|variant| variant.function_name.clone()) diff --git a/sylvia-derive/src/utils.rs b/sylvia-derive/src/utils.rs index 90ced524..16857d4d 100644 --- a/sylvia-derive/src/utils.rs +++ b/sylvia-derive/src/utils.rs @@ -94,7 +94,7 @@ pub fn as_where_clause(where_predicates: &[&WherePredicate]) -> Option TokenStream { +pub fn emit_bracketed_generics(unbonded_generics: &[&GenericParam]) -> TokenStream { match unbonded_generics.is_empty() { true => quote! {}, false => quote! { < #(#unbonded_generics,)* > }, diff --git a/sylvia/src/types.rs b/sylvia/src/types.rs index 71495870..c70ed7b3 100644 --- a/sylvia/src/types.rs +++ b/sylvia/src/types.rs @@ -96,3 +96,10 @@ impl<'a, C: CustomQuery> From<(Deps<'a, C>, Env)> for QueryCtx<'a, C> { } pub trait CustomMsg: cosmwasm_std::CustomMsg + DeserializeOwned {} + +impl CustomMsg for T where T: cosmwasm_std::CustomMsg + DeserializeOwned {} + +pub trait InterfaceMessages { + type Exec; + type Query; +} diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs index a10f743b..f6721c67 100644 --- a/sylvia/tests/generics.rs +++ b/sylvia/tests/generics.rs @@ -32,7 +32,6 @@ pub mod cw1 { #[cw_serde] pub struct ExternalMsg; impl cosmwasm_std::CustomMsg for ExternalMsg {} -impl sylvia::types::CustomMsg for ExternalMsg {} #[cw_serde] pub struct ExternalQuery; @@ -44,6 +43,8 @@ mod tests { use crate::{cw1::Querier, ExternalMsg, ExternalQuery}; + use crate::cw1::InterfaceTypes; + use sylvia::types::InterfaceMessages; #[test] fn construct_messages() { let contract = Addr::unchecked("contract"); @@ -59,5 +60,15 @@ mod tests { let cw1_querier = crate::cw1::BoundQuerier::borrowed(&contract, &querier); let _: Result = Querier::some_query(&cw1_querier, ExternalMsg {}); let _: Result = cw1_querier.some_query(ExternalMsg {}); + + // Construct messages with Interface extension + let _ = + as InterfaceMessages>::Query::some_query( + ExternalMsg {}, + ); + let _= + as InterfaceMessages>::Exec::execute(vec![ + CosmosMsg::Custom(ExternalMsg {}), + ]); } } From 6445cc5ae1d387770e7633e21b17a36cf9d3cac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 27 Sep 2023 17:45:12 +0200 Subject: [PATCH 04/15] feat: Support generic interface implemented on contract --- sylvia-derive/src/check_generics.rs | 68 ++++++++++++++----- sylvia-derive/src/input.rs | 71 ++++++++++++++------ sylvia-derive/src/interfaces.rs | 48 ++++++++----- sylvia-derive/src/message.rs | 100 ++++++++++++++++------------ sylvia-derive/src/multitest.rs | 65 ++++++++---------- sylvia-derive/src/parser.rs | 61 +++-------------- sylvia-derive/src/utils.rs | 25 ++++--- sylvia/tests/generics.rs | 54 ++++++++++++++- 8 files changed, 291 insertions(+), 201 deletions(-) diff --git a/sylvia-derive/src/check_generics.rs b/sylvia-derive/src/check_generics.rs index edba0ce4..c31ca402 100644 --- a/sylvia-derive/src/check_generics.rs +++ b/sylvia-derive/src/check_generics.rs @@ -1,26 +1,57 @@ use syn::visit::Visit; -use syn::GenericParam; +use syn::{parse_quote, GenericArgument, GenericParam, Type}; + +pub trait GetPath { + fn get_path(&self) -> Option; +} + +impl GetPath for GenericParam { + fn get_path(&self) -> Option { + match self { + GenericParam::Type(ty) => { + let ident = &ty.ident; + Some(parse_quote! { #ident }) + } + _ => None, + } + } +} + +impl GetPath for GenericArgument { + fn get_path(&self) -> Option { + match self { + GenericArgument::Type(Type::Path(path)) => { + let path = &path.path; + Some(parse_quote! { #path }) + } + _ => None, + } + } +} #[derive(Debug)] -pub struct CheckGenerics<'g> { - generics: &'g [&'g GenericParam], - used: Vec<&'g GenericParam>, +pub struct CheckGenerics<'g, Generic> { + generics: &'g [&'g Generic], + used: Vec<&'g Generic>, } -impl<'g> CheckGenerics<'g> { - pub fn new(generics: &'g [&'g GenericParam]) -> Self { +impl<'g, Generic> CheckGenerics<'g, Generic> +where + Generic: GetPath + PartialEq, +{ + pub fn new(generics: &'g [&'g Generic]) -> Self { Self { generics, used: vec![], } } - pub fn used(self) -> Vec<&'g GenericParam> { + pub fn used(self) -> Vec<&'g Generic> { self.used } /// Returns split between used and unused generics - pub fn used_unused(self) -> (Vec<&'g GenericParam>, Vec<&'g GenericParam>) { + pub fn used_unused(self) -> (Vec<&'g Generic>, Vec<&'g Generic>) { let unused = self .generics .iter() @@ -32,17 +63,18 @@ impl<'g> CheckGenerics<'g> { } } -impl<'ast, 'g> Visit<'ast> for CheckGenerics<'g> { +impl<'ast, 'g, Generic> Visit<'ast> for CheckGenerics<'g, Generic> +where + Generic: GetPath + PartialEq, +{ fn visit_path(&mut self, p: &'ast syn::Path) { - if let Some(p) = p.get_ident() { - if let Some(gen) = self - .generics - .iter() - .find(|gen| matches!(gen, GenericParam::Type(ty) if ty.ident == *p)) - { - if !self.used.contains(gen) { - self.used.push(gen); - } + if let Some(gen) = self + .generics + .iter() + .find(|gen| gen.get_path().as_ref() == Some(p)) + { + if !self.used.contains(gen) { + self.used.push(gen); } } diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 718cb043..80b8929a 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -3,7 +3,10 @@ use proc_macro_error::emit_error; use quote::quote; use syn::parse::{Parse, Parser}; use syn::spanned::Spanned; -use syn::{parse_quote, GenericParam, Ident, ItemImpl, ItemTrait, TraitItem, Type}; +use syn::{ + parse_quote, GenericArgument, GenericParam, Ident, ItemImpl, ItemTrait, PathArguments, + TraitItem, Type, +}; use crate::crate_module; use crate::interfaces::Interfaces; @@ -156,42 +159,48 @@ impl<'a> ImplInput<'a> { } pub fn process(&self) -> TokenStream { - let is_trait = self.item.trait_.is_some(); + let Self { + item, + generics, + error, + custom, + override_entry_points, + interfaces, + .. + } = self; + let is_trait = item.trait_.is_some(); let multitest_helpers = if cfg!(feature = "mt") { + let interface_generics = self.extract_generic_argument(); MultitestHelpers::new( - self.item, + item, is_trait, - &self.error, - &self.generics, - &self.custom, - &self.override_entry_points, - &self.interfaces, + error, + &interface_generics, + custom, + override_entry_points, + interfaces, ) .emit() } else { quote! {} }; - let unbonded_generics = &vec![]; + let where_clause = &item.generics.where_clause; let variants = MsgVariants::new( self.item.as_variants(), MsgType::Query, - unbonded_generics, - &None, + generics, + where_clause, ); match is_trait { - true => self.process_interface(variants, multitest_helpers), + true => self.process_interface(multitest_helpers), false => self.process_contract(variants, multitest_helpers), } } - fn process_interface( - &self, - variants: MsgVariants<'a>, - multitest_helpers: TokenStream, - ) -> TokenStream { - let querier_bound_for_impl = self.emit_querier_for_bound_impl(variants); + fn process_interface(&self, multitest_helpers: TokenStream) -> TokenStream { + let querier_bound_for_impl = self.emit_querier_for_bound_impl(); #[cfg(not(tarpaulin_include))] quote! { @@ -203,7 +212,7 @@ impl<'a> ImplInput<'a> { fn process_contract( &self, - variants: MsgVariants<'a>, + variants: MsgVariants<'a, GenericParam>, multitest_helpers: TokenStream, ) -> TokenStream { let messages = self.emit_messages(); @@ -285,13 +294,31 @@ impl<'a> ImplInput<'a> { .emit() } - fn emit_querier_for_bound_impl(&self, variants: MsgVariants<'a>) -> TokenStream { + /// This method should only be called for trait impl block + fn extract_generic_argument(&self) -> Vec<&GenericArgument> { + let interface_generics = &self.item.trait_.as_ref(); + let args = match interface_generics { + Some((_, path, _)) => path.segments.last().map(|segment| &segment.arguments), + None => None, + }; + + match args { + Some(PathArguments::AngleBracketed(args)) => { + args.args.pairs().map(|pair| *pair.value()).collect() + } + _ => vec![], + } + } + + fn emit_querier_for_bound_impl(&self) -> TokenStream { let trait_module = self .interfaces - .interfaces() - .first() + .get_only_interface() .map(|interface| &interface.module); let contract_module = self.attributes.module.as_ref(); + let generics = self.extract_generic_argument(); + + let variants = MsgVariants::new(self.item.as_variants(), MsgType::Query, &generics, &None); variants.emit_querier_for_bound_impl(trait_module, contract_module) } diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs index 94f0d575..f1d2a909 100644 --- a/sylvia-derive/src/interfaces.rs +++ b/sylvia-derive/src/interfaces.rs @@ -83,30 +83,23 @@ impl Interfaces { .collect() } - pub fn emit_glue_message_variants( - &self, - msg_ty: &MsgType, - msg_name: &Ident, - ) -> Vec { + pub fn emit_glue_message_variants(&self, msg_ty: &MsgType) -> Vec { + let sylvia = crate_module(); + self.interfaces .iter() .map(|interface| { let ContractMessageAttr { - module, - exec_generic_params, - query_generic_params, - variant, - .. + module, variant, .. } = interface; - let generics = match msg_ty { - MsgType::Exec => exec_generic_params.as_slice(), - MsgType::Query => query_generic_params.as_slice(), - _ => &[], - }; - - let enum_name = Self::merge_module_with_name(interface, msg_name); - quote! { #variant(#module :: #enum_name<#(#generics,)*>) } + let interface_enum = + quote! { <#module ::InterfaceTypes as #sylvia ::types::InterfaceMessages> }; + if msg_ty == &MsgType::Query { + quote! { #variant ( #interface_enum :: Query) } + } else { + quote! { #variant ( #interface_enum :: Exec)} + } }) .collect() } @@ -158,4 +151,23 @@ impl Interfaces { pub fn as_modules(&self) -> impl Iterator { self.interfaces.iter().map(|interface| &interface.module) } + + pub fn get_only_interface(&self) -> Option<&ContractMessageAttr> { + let interfaces = &self.interfaces; + match interfaces.len() { + 0 => None, + 1 => Some(&interfaces[0]), + _ => { + let first = &interfaces[0]; + for redefined in &interfaces[1..] { + emit_error!( + redefined.module, "The attribute `messages` is redefined"; + note = first.module.span() => "Previous definition of the attribute `messsages`"; + note = "Only one `messages` attribute can exist on an interface implementation on contract" + ); + } + None + } + } + } } diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 9825f8e8..3e46a990 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -1,4 +1,4 @@ -use crate::check_generics::CheckGenerics; +use crate::check_generics::{CheckGenerics, GetPath}; use crate::crate_module; use crate::interfaces::Interfaces; use crate::parser::{ @@ -13,7 +13,7 @@ use crate::variant_descs::{AsVariantDescs, VariantDescs}; use convert_case::{Case, Casing}; use proc_macro2::{Span, TokenStream}; use proc_macro_error::emit_error; -use quote::quote; +use quote::{quote, ToTokens}; use syn::fold::Fold; use syn::parse::{Parse, Parser}; use syn::spanned::Spanned; @@ -269,11 +269,13 @@ impl<'a> EnumMessage<'a> { quote! {} } else if MsgType::Query == *msg_ty { quote! { + #[serde(skip)] #[returns((#(#generics,)*))] _Phantom(std::marker::PhantomData<( #(#generics,)* )>), } } else { quote! { + #[serde(skip)] _Phantom(std::marker::PhantomData<( #(#generics,)* )>), } }; @@ -281,7 +283,7 @@ impl<'a> EnumMessage<'a> { let match_arms = if !generics.is_empty() { quote! { #(#match_arms,)* - _Phantom(_) => unreachable!(), + _Phantom(_) => Err(#sylvia ::cw_std::StdError::generic_err("Phantom message should not be constructed.")).map_err(Into::into), } } else { quote! { @@ -489,11 +491,14 @@ pub struct MsgVariant<'a> { impl<'a> MsgVariant<'a> { /// Creates new message variant from trait method - pub fn new( + pub fn new( sig: &'a Signature, - generics_checker: &mut CheckGenerics, + generics_checker: &mut CheckGenerics, msg_attr: MsgAttr, - ) -> MsgVariant<'a> { + ) -> MsgVariant<'a> + where + Generic: GetPath + PartialEq, + { let function_name = &sig.ident; let name = Ident::new( @@ -622,10 +627,10 @@ impl<'a> MsgVariant<'a> { } } - pub fn emit_querier_impl( + pub fn emit_querier_impl( &self, trait_module: Option<&Path>, - unbonded_generics: &Vec<&GenericParam>, + unbonded_generics: &Vec<&Generic>, ) -> TokenStream { let sylvia = crate_module(); let Self { @@ -680,17 +685,20 @@ impl<'a> MsgVariant<'a> { } } -pub struct MsgVariants<'a> { +pub struct MsgVariants<'a, Generic> { variants: Vec>, - unbonded_generics: Vec<&'a GenericParam>, + used_generics: Vec<&'a Generic>, where_predicates: Vec<&'a WherePredicate>, } -impl<'a> MsgVariants<'a> { +impl<'a, Generic> MsgVariants<'a, Generic> +where + Generic: GetPath + PartialEq + ToTokens, +{ pub fn new( source: VariantDescs<'a>, msg_type: MsgType, - generics: &'a [&'a GenericParam], + generics: &'a [&'a Generic], unfiltered_where_clause: &'a Option, ) -> Self { let mut generics_checker = CheckGenerics::new(generics); @@ -717,12 +725,12 @@ impl<'a> MsgVariants<'a> { }) .collect(); - let (unbonded_generics, _) = generics_checker.used_unused(); - let where_predicates = filter_wheres(unfiltered_where_clause, generics, &unbonded_generics); + let (used_generics, _) = generics_checker.used_unused(); + let where_predicates = filter_wheres(unfiltered_where_clause, generics, &used_generics); Self { variants, - unbonded_generics, + used_generics, where_predicates, } } @@ -744,7 +752,7 @@ impl<'a> MsgVariants<'a> { let sylvia = crate_module(); let Self { variants, - unbonded_generics, + used_generics, .. } = self; let where_clause = self.where_clause(); @@ -752,14 +760,14 @@ impl<'a> MsgVariants<'a> { let methods_impl = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) - .map(|variant| variant.emit_querier_impl(None, unbonded_generics)); + .map(|variant| variant.emit_querier_impl(None, used_generics)); let methods_declaration = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) .map(MsgVariant::emit_querier_declaration); - let braced_generics = emit_bracketed_generics(unbonded_generics); + let braced_generics = emit_bracketed_generics(used_generics); let querier = quote! { Querier #braced_generics }; #[cfg(not(tarpaulin_include))] @@ -784,7 +792,7 @@ impl<'a> MsgVariants<'a> { } } - impl <'a, C: #sylvia ::cw_std::CustomQuery, #(#unbonded_generics,)*> #querier for BoundQuerier<'a, C> #where_clause { + impl <'a, C: #sylvia ::cw_std::CustomQuery, #(#used_generics,)*> #querier for BoundQuerier<'a, C> #where_clause { #(#methods_impl)* } @@ -803,7 +811,7 @@ impl<'a> MsgVariants<'a> { let sylvia = crate_module(); let Self { variants, - unbonded_generics, + used_generics, .. } = self; let where_clause = self.where_clause(); @@ -811,23 +819,25 @@ impl<'a> MsgVariants<'a> { let methods_impl = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) - .map(|variant| variant.emit_querier_impl(trait_module, unbonded_generics)); + .map(|variant| variant.emit_querier_impl(trait_module, used_generics)); - let mut querier = trait_module + let querier = trait_module .map(|module| quote! { #module ::Querier }) .unwrap_or_else(|| quote! { Querier }); let bound_querier = contract_module .map(|module| quote! { #module ::BoundQuerier}) .unwrap_or_else(|| quote! { BoundQuerier }); - if !unbonded_generics.is_empty() { - querier = quote! { #querier < #(#unbonded_generics,)* > }; - } + let querier = if !used_generics.is_empty() { + quote! { #querier < #(#used_generics,)* > } + } else { + quote! { #querier } + }; #[cfg(not(tarpaulin_include))] { quote! { - impl <'a, C: #sylvia ::cw_std::CustomQuery, #(#unbonded_generics,)*> #querier for #bound_querier<'a, C> #where_clause { + impl <'a, C: #sylvia ::cw_std::CustomQuery> #querier for #bound_querier<'a, C > #where_clause { #(#methods_impl)* } } @@ -844,7 +854,13 @@ pub struct MsgField<'a> { impl<'a> MsgField<'a> { /// Creates new field from trait method argument - pub fn new(item: &'a PatType, generics_checker: &mut CheckGenerics) -> Option> { + pub fn new( + item: &'a PatType, + generics_checker: &mut CheckGenerics, + ) -> Option> + where + Generic: GetPath + PartialEq, + { let name = match &*item.pat { Pat::Ident(p) => Some(&p.ident), pat => { @@ -950,7 +966,7 @@ impl<'a> GlueMessage<'a> { let contract = StripGenerics.fold_type((*contract).clone()); let enum_name = Ident::new(&format!("Contract{}", name), name.span()); - let variants = interfaces.emit_glue_message_variants(msg_ty, name); + let variants = interfaces.emit_glue_message_variants(msg_ty); let msg_name = quote! {#contract ( #name)}; let mut messages_call_on_all_variants: Vec = @@ -1101,8 +1117,8 @@ impl<'a> GlueMessage<'a> { } pub struct InterfaceMessages<'a> { - exec_variants: MsgVariants<'a>, - query_variants: MsgVariants<'a>, + exec_variants: MsgVariants<'a, GenericParam>, + query_variants: MsgVariants<'a, GenericParam>, generics: &'a [&'a GenericParam], } @@ -1137,8 +1153,8 @@ impl<'a> InterfaceMessages<'a> { generics, } = self; - let exec_generics = &exec_variants.unbonded_generics; - let query_generics = &query_variants.unbonded_generics; + let exec_generics = &exec_variants.used_generics; + let query_generics = &query_variants.used_generics; let bracket_generics = emit_bracketed_generics(generics); let exec_bracketed_generics = emit_bracketed_generics(exec_generics); @@ -1195,15 +1211,17 @@ impl<'a> EntryPoints<'a> { ) .unwrap_or_else(|| parse_quote! { #sylvia ::cw_std::StdError }); - let has_migrate = !MsgVariants::new(source.as_variants(), MsgType::Migrate, &[], &None) - .variants() - .is_empty(); - - let reply = MsgVariants::new(source.as_variants(), MsgType::Reply, &[], &None) - .variants() - .iter() - .map(|variant| variant.function_name.clone()) - .next(); + let has_migrate = + !MsgVariants::::new(source.as_variants(), MsgType::Migrate, &[], &None) + .variants() + .is_empty(); + + let reply = + MsgVariants::::new(source.as_variants(), MsgType::Reply, &[], &None) + .variants() + .iter() + .map(|variant| variant.function_name.clone()) + .next(); let custom = Custom::new(&source.attrs); Self { diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs index 3183e1ef..2600f92a 100644 --- a/sylvia-derive/src/multitest.rs +++ b/sylvia-derive/src/multitest.rs @@ -1,20 +1,18 @@ use proc_macro2::{Ident, TokenStream}; use proc_macro_error::emit_error; -use quote::quote; +use quote::{quote, ToTokens}; use syn::parse::{Parse, Parser}; use syn::spanned::Spanned; -use syn::{ - parse_quote, FnArg, GenericParam, ImplItem, ItemImpl, ItemTrait, Pat, PatType, Path, Type, -}; +use syn::{parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Pat, PatType, Path, Type}; -use crate::check_generics::CheckGenerics; +use crate::check_generics::{CheckGenerics, GetPath}; use crate::crate_module; use crate::interfaces::Interfaces; use crate::message::MsgField; use crate::parser::{ parse_struct_message, Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints, }; -use crate::utils::{extract_return_type, process_fields}; +use crate::utils::{emit_bracketed_generics, extract_return_type, process_fields}; struct MessageSignature<'a> { pub name: &'a Ident, @@ -24,7 +22,7 @@ struct MessageSignature<'a> { pub return_type: TokenStream, } -pub struct MultitestHelpers<'a> { +pub struct MultitestHelpers<'a, Generics> { messages: Vec>, error_type: TokenStream, contract: &'a Type, @@ -32,7 +30,7 @@ pub struct MultitestHelpers<'a> { is_migrate: bool, reply: Option, source: &'a ItemImpl, - generics: &'a [&'a GenericParam], + generics: &'a [&'a Generics], contract_name: &'a Ident, proxy_name: Ident, custom: &'a Custom<'a>, @@ -61,12 +59,15 @@ fn extract_contract_name(contract: &Type) -> &Ident { &segment.ident } -impl<'a> MultitestHelpers<'a> { +impl<'a, Generics> MultitestHelpers<'a, Generics> +where + Generics: ToTokens + PartialEq + GetPath, +{ pub fn new( source: &'a ItemImpl, is_trait: bool, contract_error: &'a Type, - generics: &'a [&'a GenericParam], + generics: &'a [&'a Generics], custom: &'a Custom, override_entry_points: &'a OverrideEntryPoints, interfaces: &'a Interfaces, @@ -380,6 +381,7 @@ impl<'a> MultitestHelpers<'a> { error_type, custom, interfaces, + generics, .. } = self; @@ -389,29 +391,13 @@ impl<'a> MultitestHelpers<'a> { let proxy_name = &self.proxy_name; let trait_name = Ident::new(&format!("{}", interface_name), interface_name.span()); - let modules: Vec<&Path> = interfaces.as_modules().collect(); - - #[cfg(not(tarpaulin_include))] - let module = match modules.len() { - 0 => { - quote! {} - } - 1 => { - let module = &modules[0]; - quote! {#module ::} - } - _ => { - let first = &modules[0]; - for redefined in &modules[1..] { - emit_error!( - redefined, "The attribute `messages` is redefined"; - note = first.span() => "Previous definition of the attribute `messsages`"; - note = "Only one `messages` attribute can exist on an interface implementation on contract" - ); - } - quote! {} - } - }; + let module = interfaces + .get_only_interface() + .map(|interface| { + let module = &interface.module; + quote! { #module :: } + }) + .unwrap_or(quote! {}); let custom_msg = custom.msg_or_default(); @@ -430,6 +416,9 @@ impl<'a> MultitestHelpers<'a> { > }; + let bracketed_generics = emit_bracketed_generics(generics); + let interface_enum = quote! { < #module InterfaceTypes #bracketed_generics as #sylvia ::types::InterfaceMessages> }; + #[cfg(not(tarpaulin_include))] let methods_definitions = messages.iter().map(|msg| { let MessageSignature { @@ -439,11 +428,12 @@ impl<'a> MultitestHelpers<'a> { msg_ty, return_type, } = msg; + let type_name = msg_ty.as_accessor_name(); if msg_ty == &MsgType::Exec { quote! { #[track_caller] - fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #module ExecMsg, #mt_app, #custom_msg> { - let msg = #module ExecMsg:: #name ( #(#arguments),* ); + fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, #mt_app, #custom_msg> { + let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* ); #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) } @@ -451,7 +441,7 @@ impl<'a> MultitestHelpers<'a> { } else { quote! { fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { - let msg = #module QueryMsg:: #name ( #(#arguments),* ); + let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* ); (*self.app) .app() @@ -472,9 +462,10 @@ impl<'a> MultitestHelpers<'a> { return_type, .. } = msg; + let type_name = msg_ty.as_accessor_name(); if msg_ty == &MsgType::Exec { quote! { - fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #module ExecMsg, MtApp, #custom_msg>; + fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, MtApp, #custom_msg>; } } else { quote! { diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs index dbf6440f..d6586327 100644 --- a/sylvia-derive/src/parser.rs +++ b/sylvia-derive/src/parser.rs @@ -152,6 +152,14 @@ impl MsgType { MsgType::Sudo => todo!(), } } + + pub fn as_accessor_name(&self) -> Option { + match self { + MsgType::Exec => Some(parse_quote! { Exec }), + MsgType::Query => Some(parse_quote! { Query }), + _ => None, + } + } } impl PartialEq for MsgAttr { @@ -238,36 +246,10 @@ pub struct Customs { #[derive(Debug)] pub struct ContractMessageAttr { pub module: Path, - pub exec_generic_params: Vec, - pub query_generic_params: Vec, pub variant: Ident, pub customs: Customs, } -#[cfg(not(tarpaulin_include))] -// False negative. Called in function below -fn parse_generics(content: &ParseBuffer) -> Result> { - let _: Token![<] = content.parse()?; - let mut params = vec![]; - - loop { - let param: Path = content.parse()?; - params.push(param); - - let generics_close: Option]> = content.parse()?; - if generics_close.is_some() { - break; - } - - let comma: Option = content.parse()?; - if comma.is_none() { - return Err(Error::new(content.span(), "Expected comma or `>`")); - } - } - - Ok(params) -} - fn interface_has_custom(content: ParseStream) -> Result { let mut customs = Customs { has_msg: false, @@ -316,31 +298,6 @@ impl Parse for ContractMessageAttr { let module = content.parse()?; - let generics_open: Option = content.parse()?; - let mut exec_generic_params = vec![]; - let mut query_generic_params = vec![]; - - if generics_open.is_some() { - loop { - let ty: Ident = content.parse()?; - let params = if ty == "exec" { - &mut exec_generic_params - } else if ty == "query" { - &mut query_generic_params - } else { - return Err(Error::new(ty.span(), "Invalid message type")); - }; - - *params = parse_generics(&content)?; - - if content.peek(Token![as]) { - break; - } - - let _: Token![,] = content.parse()?; - } - } - let _: Token![as] = content.parse()?; let variant = content.parse()?; @@ -355,8 +312,6 @@ impl Parse for ContractMessageAttr { Ok(Self { module, - exec_generic_params, - query_generic_params, variant, customs, }) diff --git a/sylvia-derive/src/utils.rs b/sylvia-derive/src/utils.rs index 16857d4d..276deb3d 100644 --- a/sylvia-derive/src/utils.rs +++ b/sylvia-derive/src/utils.rs @@ -1,21 +1,21 @@ use proc_macro2::TokenStream; use proc_macro_error::emit_error; -use quote::quote; +use quote::{quote, ToTokens}; use syn::spanned::Spanned; use syn::visit::Visit; use syn::{ - parse_quote, FnArg, GenericArgument, GenericParam, Path, PathArguments, ReturnType, Signature, - Type, WhereClause, WherePredicate, + parse_quote, FnArg, GenericArgument, Path, PathArguments, ReturnType, Signature, Type, + WhereClause, WherePredicate, }; -use crate::check_generics::CheckGenerics; +use crate::check_generics::{CheckGenerics, GetPath}; use crate::message::MsgField; #[cfg(not(tarpaulin_include))] -pub fn filter_wheres<'a>( +pub fn filter_wheres<'a, Generic: GetPath + PartialEq>( clause: &'a Option, - generics: &[&GenericParam], - used_generics: &[&GenericParam], + generics: &[&Generic], + used_generics: &[&Generic], ) -> Vec<&'a WherePredicate> { clause .as_ref() @@ -36,10 +36,13 @@ pub fn filter_wheres<'a>( .unwrap_or_default() } -pub fn process_fields<'s>( +pub fn process_fields<'s, Generic>( sig: &'s Signature, - generics_checker: &mut CheckGenerics, -) -> Vec> { + generics_checker: &mut CheckGenerics, +) -> Vec> +where + Generic: GetPath + PartialEq, +{ sig.inputs .iter() .skip(2) @@ -94,7 +97,7 @@ pub fn as_where_clause(where_predicates: &[&WherePredicate]) -> Option TokenStream { +pub fn emit_bracketed_generics(unbonded_generics: &[&Generic]) -> TokenStream { match unbonded_generics.is_empty() { true => quote! {}, false => quote! { < #(#unbonded_generics,)* > }, diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs index f6721c67..bc9e982d 100644 --- a/sylvia/tests/generics.rs +++ b/sylvia/tests/generics.rs @@ -29,6 +29,58 @@ pub mod cw1 { } } +pub mod cw1_contract { + use cosmwasm_std::{Response, StdResult}; + use sylvia::types::InstantiateCtx; + use sylvia_derive::contract; + + pub struct Cw1Contract; + + #[contract] + impl Cw1Contract { + pub const fn new() -> Self { + Self + } + + #[msg(instantiate)] + pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult { + Ok(Response::new()) + } + } +} + +pub mod impl_cw1 { + use cosmwasm_std::{CosmosMsg, Response, StdError}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; + + use crate::{cw1::Cw1, cw1_contract::Cw1Contract, ExternalMsg}; + + #[contract(module = crate::cw1_contract)] + #[messages(crate::cw1 as Cw1)] + impl Cw1 for Cw1Contract { + type Error = StdError; + + #[msg(exec)] + fn execute( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> Result, Self::Error> { + Ok(Response::new()) + } + + #[msg(query)] + fn some_query( + &self, + _ctx: QueryCtx, + _param: crate::ExternalMsg, + ) -> Result { + Ok(crate::ExternalQuery {}) + } + } +} + #[cw_serde] pub struct ExternalMsg; impl cosmwasm_std::CustomMsg for ExternalMsg {} @@ -37,7 +89,7 @@ impl cosmwasm_std::CustomMsg for ExternalMsg {} pub struct ExternalQuery; impl cosmwasm_std::CustomQuery for ExternalQuery {} -#[cfg(test)] +#[cfg(all(test, feature = "mt"))] mod tests { use cosmwasm_std::{testing::mock_dependencies, Addr, CosmosMsg, Empty, QuerierWrapper}; From f0820cdd99e467eec671e31542901271e5580e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Fri, 6 Oct 2023 17:42:31 +0200 Subject: [PATCH 05/15] feat: Support generics on `messages` attribute in main `contract` macro call --- sylvia-derive/src/interfaces.rs | 74 +++++++++---- sylvia-derive/src/message.rs | 6 +- sylvia-derive/src/parser.rs | 34 +++++- sylvia/tests/generics.rs | 190 ++++++++++++++++++++++++++++++-- 4 files changed, 269 insertions(+), 35 deletions(-) diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs index f1d2a909..09559c70 100644 --- a/sylvia-derive/src/interfaces.rs +++ b/sylvia-derive/src/interfaces.rs @@ -15,13 +15,6 @@ pub struct Interfaces { } impl Interfaces { - fn merge_module_with_name(message_attr: &ContractMessageAttr, name: &syn::Ident) -> syn::Ident { - // ContractMessageAttr will fail to parse empty `#[messsages()]` attribute so we can safely unwrap here - let syn::PathSegment { ident, .. } = &message_attr.module.segments.last().unwrap(); - let module_name = ident.to_string().to_case(Case::UpperCamel); - syn::Ident::new(&format!("{}{}", module_name, name), name.span()) - } - pub fn new(source: &ItemImpl) -> Self { let interfaces: Vec<_> = source .attrs @@ -90,11 +83,19 @@ impl Interfaces { .iter() .map(|interface| { let ContractMessageAttr { - module, variant, .. + module, + variant, + generics, + .. } = interface; + let generics = if !generics.is_empty() { + quote! { < #generics > } + } else { + quote! {} + }; let interface_enum = - quote! { <#module ::InterfaceTypes as #sylvia ::types::InterfaceMessages> }; + quote! { <#module ::InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> }; if msg_ty == &MsgType::Query { quote! { #variant ( #interface_enum :: Query) } } else { @@ -104,28 +105,46 @@ impl Interfaces { .collect() } - pub fn emit_messages_call(&self, msg_name: &Ident) -> Vec { + pub fn emit_messages_call(&self, msg_ty: &MsgType) -> Vec { + let sylvia = crate_module(); + self.interfaces .iter() .map(|interface| { - let enum_name = Self::merge_module_with_name(interface, msg_name); - let module = &interface.module; - quote! { &#module :: #enum_name :: messages()} + let ContractMessageAttr { + module, generics, .. + } = interface; + let generics = if !generics.is_empty() { + quote! { < #generics > } + } else { + quote! {} + }; + let type_name = msg_ty.as_accessor_name(); + quote! { + &<#module :: InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> :: #type_name :: messages() + } }) .collect() } - pub fn emit_deserialization_attempts(&self, msg_name: &Ident) -> Vec { + pub fn emit_deserialization_attempts(&self, msg_ty: &MsgType) -> Vec { + let sylvia = crate_module(); + self.interfaces .iter() .map(|interface| { let ContractMessageAttr { - module, variant, .. + module, variant, generics, .. } = interface; - let enum_name = Self::merge_module_with_name(interface, msg_name); + let generics = if !generics.is_empty() { + quote! { < #generics > } + } else { + quote! {} + }; + let type_name = msg_ty.as_accessor_name(); quote! { - let msgs = &#module :: #enum_name ::messages(); + let msgs = &<#module :: InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> :: #type_name :: messages(); if msgs.into_iter().any(|msg| msg == &recv_msg_name) { match val.deserialize_into() { Ok(msg) => return Ok(Self:: #variant (msg)), @@ -137,13 +156,26 @@ impl Interfaces { .collect() } - pub fn emit_response_schemas_calls(&self, msg_name: &Ident) -> Vec { + pub fn emit_response_schemas_calls(&self, msg_ty: &MsgType) -> Vec { + let sylvia = crate_module(); + self.interfaces .iter() .map(|interface| { - let enum_name = Self::merge_module_with_name(interface, msg_name); - let module = &interface.module; - quote! { #module :: #enum_name :: response_schemas_impl()} + let ContractMessageAttr { + module, generics, .. + } = interface; + + let generics = if !generics.is_empty() { + quote! { < #generics > } + } else { + quote! {} + }; + + let type_name = msg_ty.as_accessor_name(); + quote! { + <#module :: InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> :: #type_name :: response_schemas_impl() + } }) .collect() } diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 3e46a990..4c5449eb 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -970,7 +970,7 @@ impl<'a> GlueMessage<'a> { let msg_name = quote! {#contract ( #name)}; let mut messages_call_on_all_variants: Vec = - interfaces.emit_messages_call(name); + interfaces.emit_messages_call(msg_ty); messages_call_on_all_variants.push(quote! {&#name :: messages()}); let variants_cnt = messages_call_on_all_variants.len(); @@ -1004,7 +1004,7 @@ impl<'a> GlueMessage<'a> { let dispatch_arm = quote! {#enum_name :: #contract (msg) => msg.dispatch(contract, ctx)}; - let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(name); + let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(msg_ty); #[cfg(not(tarpaulin_include))] let contract_deserialization_attempt = quote! { @@ -1020,7 +1020,7 @@ impl<'a> GlueMessage<'a> { let ctx_type = msg_ty.emit_ctx_type(&custom.query_or_default()); let ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), error); - let mut response_schemas_calls = interfaces.emit_response_schemas_calls(name); + let mut response_schemas_calls = interfaces.emit_response_schemas_calls(msg_ty); response_schemas_calls.push(quote! {#name :: response_schemas_impl()}); let response_schemas = match name.to_string().as_str() { diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs index d6586327..9d352743 100644 --- a/sylvia-derive/src/parser.rs +++ b/sylvia-derive/src/parser.rs @@ -1,14 +1,17 @@ use proc_macro2::{Punct, TokenStream}; use proc_macro_error::emit_error; use quote::quote; +use syn::fold::Fold; use syn::parse::{Error, Nothing, Parse, ParseBuffer, ParseStream, Parser}; +use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ - parenthesized, parse_quote, Attribute, Ident, ImplItem, ImplItemMethod, ItemImpl, ItemTrait, - Path, Result, Token, TraitItem, Type, + parenthesized, parse_quote, Attribute, GenericArgument, Ident, ImplItem, ImplItemMethod, + ItemImpl, ItemTrait, Path, PathArguments, Result, Token, TraitItem, Type, }; use crate::crate_module; +use crate::strip_generics::StripGenerics; /// Parser arguments for `contract` macro pub struct ContractArgs { @@ -248,6 +251,7 @@ pub struct ContractMessageAttr { pub module: Path, pub variant: Ident, pub customs: Customs, + pub generics: Punctuated, } fn interface_has_custom(content: ParseStream) -> Result { @@ -289,6 +293,26 @@ fn interface_has_custom(content: ParseStream) -> Result { Ok(customs) } +fn extract_generics_from_path(module: &mut Path) -> Punctuated { + let generics = module.segments.last().map(|segment| { + match segment.arguments.clone(){ + PathArguments::AngleBracketed(generics) => { + generics.args + }, + PathArguments::None => Default::default(), + PathArguments::Parenthesized(generics) => { + emit_error!( + generics.span(), "Found paranthesis wrapping generics in `messages` attribute."; + note = "Expected `messages` attribute to be in form `#[messages(Path as Type)]`" + ); + Default::default() + } + } + }).unwrap_or_default(); + + generics +} + #[cfg(not(tarpaulin_include))] // False negative. It is being called in closure impl Parse for ContractMessageAttr { @@ -296,7 +320,9 @@ impl Parse for ContractMessageAttr { let content; parenthesized!(content in input); - let module = content.parse()?; + let mut module = content.parse()?; + let generics = extract_generics_from_path(&mut module); + let module = StripGenerics.fold_path(module); let _: Token![as] = content.parse()?; let variant = content.parse()?; @@ -314,6 +340,7 @@ impl Parse for ContractMessageAttr { module, variant, customs, + generics, }) } } @@ -478,6 +505,7 @@ impl OverrideEntryPoint { entry_point, msg_name, msg_type, + .. } = self; let sylvia = crate_module(); diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs index bc9e982d..73f36fd4 100644 --- a/sylvia/tests/generics.rs +++ b/sylvia/tests/generics.rs @@ -13,7 +13,7 @@ pub mod cw1 { where for<'msg_de> Msg: CustomMsg + Deserialize<'msg_de>, Param: sylvia::types::CustomMsg, - for<'msg_de> QueryRet: CustomQuery + DeserializeOwned, + QueryRet: CustomQuery + DeserializeOwned, { type Error: From; @@ -29,23 +29,140 @@ pub mod cw1 { } } +pub mod whitelist { + use cosmwasm_std::{CosmosMsg, CustomMsg, CustomQuery, Response, StdError}; + + use serde::de::DeserializeOwned; + use serde::Deserialize; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::interface; + + #[interface(module=msg)] + pub trait Whitelist + where + for<'msg_de> Msg: CustomMsg + Deserialize<'msg_de>, + for<'msg_de> QueryRet: CustomQuery + DeserializeOwned, + { + type Error: From; + + #[msg(exec)] + fn update_admins( + &self, + ctx: ExecCtx, + msgs: Vec>, + ) -> Result; + + #[msg(query)] + fn admins_list(&self, ctx: QueryCtx) -> Result; + } +} + +pub mod non_generic { + use cosmwasm_std::{CosmosMsg, Empty, Response, StdError}; + + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::interface; + + #[interface(module=msg)] + pub trait NonGeneric { + type Error: From; + + #[msg(exec)] + fn non_generic_exec( + &self, + ctx: ExecCtx, + msgs: Vec>, + ) -> Result; + + #[msg(query)] + fn non_generic_query(&self, ctx: QueryCtx) -> Result; + } +} + pub mod cw1_contract { use cosmwasm_std::{Response, StdResult}; use sylvia::types::InstantiateCtx; use sylvia_derive::contract; + use crate::{ExternalMsg, ExternalQuery}; + pub struct Cw1Contract; #[contract] + #[messages(crate::cw1 as Cw1)] + #[messages(crate::whitelist as Whitelist: custom(msg))] + #[messages(crate::non_generic as NonGeneric: custom(msg))] + /// Required if interface returns generic `Response` + #[sv::custom(msg=ExternalMsg)] impl Cw1Contract { pub const fn new() -> Self { Self } #[msg(instantiate)] - pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult { + pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult> { + Ok(Response::new()) + } + } +} + +pub mod impl_non_generic { + use crate::cw1_contract::Cw1Contract; + use crate::non_generic::NonGeneric; + use cosmwasm_std::{CosmosMsg, Empty, Response, StdError}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; + + #[contract(module = crate::cw1_contract)] + #[messages(crate::non_generic as NonGeneric)] + #[sv::custom(msg=crate::ExternalMsg)] + impl NonGeneric for Cw1Contract { + type Error = StdError; + + #[msg(exec)] + fn non_generic_exec( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> Result { Ok(Response::new()) } + + #[msg(query)] + fn non_generic_query(&self, _ctx: QueryCtx) -> Result { + Ok(Response::default()) + } + } +} + +pub mod impl_whitelist { + use cosmwasm_std::{CosmosMsg, Response, StdError}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; + + use crate::cw1_contract::Cw1Contract; + use crate::whitelist::Whitelist; + use crate::{ExternalMsg, ExternalQuery}; + + #[contract(module = crate::cw1_contract)] + #[messages(crate::whitelist as Whitelist)] + #[sv::custom(msg=ExternalMsg)] + impl Whitelist for Cw1Contract { + type Error = StdError; + + #[msg(exec)] + fn update_admins( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> Result { + Ok(Response::new()) + } + + #[msg(query)] + fn admins_list(&self, _ctx: QueryCtx) -> Result { + Ok(ExternalQuery {}) + } } } @@ -58,6 +175,7 @@ pub mod impl_cw1 { #[contract(module = crate::cw1_contract)] #[messages(crate::cw1 as Cw1)] + #[sv::custom(msg=ExternalMsg)] impl Cw1 for Cw1Contract { type Error = StdError; @@ -88,31 +206,52 @@ impl cosmwasm_std::CustomMsg for ExternalMsg {} #[cw_serde] pub struct ExternalQuery; impl cosmwasm_std::CustomQuery for ExternalQuery {} - #[cfg(all(test, feature = "mt"))] mod tests { + use crate::cw1::{InterfaceTypes, Querier as Cw1Querier}; + use crate::cw1_contract::Cw1Contract; + use crate::impl_cw1::test_utils::Cw1; + use crate::impl_non_generic::test_utils::NonGeneric; + use crate::impl_whitelist::test_utils::Whitelist; + use crate::non_generic::Querier as NonGenericQuerier; + use crate::whitelist::Querier as WhitelistQuerier; + use crate::{ExternalMsg, ExternalQuery}; use cosmwasm_std::{testing::mock_dependencies, Addr, CosmosMsg, Empty, QuerierWrapper}; - - use crate::{cw1::Querier, ExternalMsg, ExternalQuery}; - - use crate::cw1::InterfaceTypes; + use sylvia::multitest::App; use sylvia::types::InterfaceMessages; + #[test] fn construct_messages() { let contract = Addr::unchecked("contract"); + // Direct message construction + // cw1 let _ = crate::cw1::QueryMsg::<_, Empty>::some_query(ExternalMsg {}); let _ = crate::cw1::ExecMsg::execute(vec![CosmosMsg::Custom(ExternalMsg {})]); let _ = crate::cw1::ExecMsg::execute(vec![CosmosMsg::Custom(Empty {})]); + // whitelist + let _ = crate::whitelist::QueryMsg::::admins_list(); + let _ = crate::whitelist::ExecMsg::update_admins(vec![CosmosMsg::Custom(ExternalMsg {})]); + + // non_generic + let _ = crate::non_generic::QueryMsg::non_generic_query(); + let _ = crate::non_generic::ExecMsg::non_generic_exec(vec![]); + // Generic Querier let deps = mock_dependencies(); let querier: QuerierWrapper = QuerierWrapper::new(&deps.querier); let cw1_querier = crate::cw1::BoundQuerier::borrowed(&contract, &querier); - let _: Result = Querier::some_query(&cw1_querier, ExternalMsg {}); + let _: Result = + crate::cw1::Querier::some_query(&cw1_querier, ExternalMsg {}); let _: Result = cw1_querier.some_query(ExternalMsg {}); + let contract_querier = crate::cw1_contract::BoundQuerier::borrowed(&contract, &querier); + let _: Result = contract_querier.some_query(ExternalMsg {}); + let _: Result = contract_querier.admins_list(); + let _ = contract_querier.non_generic_query(); + // Construct messages with Interface extension let _ = as InterfaceMessages>::Query::some_query( @@ -123,4 +262,39 @@ mod tests { CosmosMsg::Custom(ExternalMsg {}), ]); } + + #[test] + fn mt_helpers() { + let _ = Cw1Contract::new(); + let app = App::>::custom(|_, _, _| {}); + let code_id = crate::cw1_contract::multitest_utils::CodeId::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate() + .with_label("Cw1Contract") + .call(owner) + .unwrap(); + + // CustomMsg generic Interface + contract.cw1_proxy().some_query(ExternalMsg {}).unwrap(); + contract.cw1_proxy().execute(vec![]).call(owner).unwrap(); + + // Non-CustomMsg generic Interface + contract.whitelist_proxy().admins_list().unwrap(); + contract + .whitelist_proxy() + .update_admins(vec![]) + .call(owner) + .unwrap(); + + // Non-CustomMsg non-generic Interface + contract.non_generic_proxy().non_generic_query().unwrap(); + contract + .non_generic_proxy() + .non_generic_exec(vec![]) + .call(owner) + .unwrap(); + } } From 5a1d87cff8c075b5f25fea481a6f0cc740f61d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Thu, 12 Oct 2023 10:24:11 +0200 Subject: [PATCH 06/15] feat: Support generic contract for simple contract --- sylvia-derive/src/input.rs | 117 ++++++----- sylvia-derive/src/message.rs | 349 +++++++++++++++++++++------------ sylvia-derive/src/multitest.rs | 165 ++++++++++------ sylvia-derive/src/parser.rs | 65 ++---- sylvia/tests/generics.rs | 46 ++++- 5 files changed, 459 insertions(+), 283 deletions(-) diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 80b8929a..8f9f2400 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -1,6 +1,6 @@ use proc_macro2::{Span, TokenStream}; use proc_macro_error::emit_error; -use quote::quote; +use quote::{quote, ToTokens}; use syn::parse::{Parse, Parser}; use syn::spanned::Spanned; use syn::{ @@ -8,6 +8,7 @@ use syn::{ TraitItem, Type, }; +use crate::check_generics::GetPath; use crate::crate_module; use crate::interfaces::Interfaces; use crate::message::{ @@ -159,47 +160,17 @@ impl<'a> ImplInput<'a> { } pub fn process(&self) -> TokenStream { - let Self { - item, - generics, - error, - custom, - override_entry_points, - interfaces, - .. - } = self; - let is_trait = item.trait_.is_some(); - let multitest_helpers = if cfg!(feature = "mt") { - let interface_generics = self.extract_generic_argument(); - MultitestHelpers::new( - item, - is_trait, - error, - &interface_generics, - custom, - override_entry_points, - interfaces, - ) - .emit() - } else { - quote! {} - }; - - let where_clause = &item.generics.where_clause; - let variants = MsgVariants::new( - self.item.as_variants(), - MsgType::Query, - generics, - where_clause, - ); + let is_trait = self.item.trait_.is_some(); match is_trait { - true => self.process_interface(multitest_helpers), - false => self.process_contract(variants, multitest_helpers), + true => self.process_interface(), + false => self.process_contract(), } } - fn process_interface(&self, multitest_helpers: TokenStream) -> TokenStream { + fn process_interface(&self) -> TokenStream { + let interface_generics = self.extract_generic_argument(); + let multitest_helpers = self.emit_multitest_helpers(&interface_generics); let querier_bound_for_impl = self.emit_querier_for_bound_impl(); #[cfg(not(tarpaulin_include))] @@ -210,14 +181,19 @@ impl<'a> ImplInput<'a> { } } - fn process_contract( - &self, - variants: MsgVariants<'a, GenericParam>, - multitest_helpers: TokenStream, - ) -> TokenStream { - let messages = self.emit_messages(); - let remote = Remote::new(&self.interfaces).emit(); + fn process_contract(&self) -> TokenStream { + let Self { item, generics, .. } = self; + let multitest_helpers = self.emit_multitest_helpers(generics); + let where_clause = &item.generics.where_clause; + let variants = MsgVariants::new( + self.item.as_variants(), + MsgType::Query, + generics, + where_clause, + ); + let messages = self.emit_messages(&variants); + let remote = Remote::new(&self.interfaces).emit(); let querier = variants.emit_querier(); let querier_from_impl = self.interfaces.emit_querier_from_impl(); @@ -237,15 +213,23 @@ impl<'a> ImplInput<'a> { } } - fn emit_messages(&self) -> TokenStream { + fn emit_messages(&self, variants: &MsgVariants) -> TokenStream { let instantiate = self.emit_struct_msg(MsgType::Instantiate); let migrate = self.emit_struct_msg(MsgType::Migrate); let exec_impl = self.emit_enum_msg(&Ident::new("ExecMsg", Span::mixed_site()), MsgType::Exec); let query_impl = self.emit_enum_msg(&Ident::new("QueryMsg", Span::mixed_site()), MsgType::Query); - let exec = self.emit_glue_msg(&Ident::new("ExecMsg", Span::mixed_site()), MsgType::Exec); - let query = self.emit_glue_msg(&Ident::new("QueryMsg", Span::mixed_site()), MsgType::Query); + let exec = self.emit_glue_msg( + &Ident::new("ExecMsg", Span::mixed_site()), + MsgType::Exec, + variants, + ); + let query = self.emit_glue_msg( + &Ident::new("QueryMsg", Span::mixed_site()), + MsgType::Query, + variants, + ); #[cfg(not(tarpaulin_include))] { @@ -282,7 +266,12 @@ impl<'a> ImplInput<'a> { .emit() } - fn emit_glue_msg(&self, name: &Ident, msg_ty: MsgType) -> TokenStream { + fn emit_glue_msg( + &self, + name: &Ident, + msg_ty: MsgType, + variants: &MsgVariants, + ) -> TokenStream { GlueMessage::new( name, self.item, @@ -290,6 +279,7 @@ impl<'a> ImplInput<'a> { &self.error, &self.custom, &self.interfaces, + variants, ) .emit() } @@ -322,4 +312,35 @@ impl<'a> ImplInput<'a> { variants.emit_querier_for_bound_impl(trait_module, contract_module) } + + fn emit_multitest_helpers(&self, generics: &[&Generic]) -> TokenStream + where + Generic: ToTokens + PartialEq + GetPath, + { + let Self { + item, + error, + custom, + override_entry_points, + interfaces, + .. + } = self; + + let is_trait = self.item.trait_.is_some(); + + if cfg!(feature = "mt") { + MultitestHelpers::new( + item, + is_trait, + error, + generics, + custom, + override_entry_points, + interfaces, + ) + .emit() + } else { + quote! {} + } + } } diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 4c5449eb..4a0d363b 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -3,7 +3,7 @@ use crate::crate_module; use crate::interfaces::Interfaces; use crate::parser::{ parse_associated_custom_type, parse_struct_message, ContractErrorAttr, ContractMessageAttr, - Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints, + Custom, MsgAttr, MsgType, OverrideEntryPoints, }; use crate::strip_generics::StripGenerics; use crate::utils::{ @@ -19,7 +19,7 @@ use syn::parse::{Parse, Parser}; use syn::spanned::Spanned; use syn::visit::Visit; use syn::{ - parse_quote, Attribute, GenericParam, Ident, ImplItem, ItemImpl, ItemTrait, Pat, PatType, Path, + parse_quote, Attribute, GenericParam, Ident, ItemImpl, ItemTrait, Pat, PatType, Path, ReturnType, Signature, TraitItem, Type, WhereClause, WherePredicate, }; @@ -123,7 +123,7 @@ impl<'a> StructMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] #[serde(rename_all="snake_case")] - pub struct #name #generics #where_clause { + pub struct #name #generics { #(pub #fields,)* } @@ -242,9 +242,7 @@ impl<'a> EnumMessage<'a> { query_type, } = self; - let match_arms = variants - .iter() - .map(|variant| variant.emit_dispatch_leg(*msg_ty)); + let match_arms = variants.iter().map(|variant| variant.emit_dispatch_leg()); let mut msgs: Vec = variants .iter() .map(|var| var.name.to_string().to_case(Case::Snake)) @@ -351,7 +349,7 @@ impl<'a> EnumMessage<'a> { /// Representation of single enum message pub struct ContractEnumMessage<'a> { name: &'a Ident, - variants: Vec>, + variants: MsgVariants<'a, GenericParam>, msg_ty: MsgType, contract: &'a Type, error: &'a Type, @@ -362,40 +360,22 @@ impl<'a> ContractEnumMessage<'a> { pub fn new( name: &'a Ident, source: &'a ItemImpl, - ty: MsgType, + msg_ty: MsgType, generics: &'a [&'a GenericParam], error: &'a Type, custom: &'a Custom, ) -> Self { - let mut generics_checker = CheckGenerics::new(generics); - let variants: Vec<_> = source - .items - .iter() - .filter_map(|item| match item { - ImplItem::Method(method) => { - let msg_attr = method.attrs.iter().find(|attr| attr.path.is_ident("msg"))?; - let attr = match MsgAttr::parse.parse2(msg_attr.tokens.clone()) { - Ok(attr) => attr, - Err(err) => { - emit_error!(method.span(), err); - return None; - } - }; - - if attr == ty { - Some(MsgVariant::new(&method.sig, &mut generics_checker, attr)) - } else { - None - } - } - _ => None, - }) - .collect(); + let variants = MsgVariants::new( + source.as_variants(), + msg_ty, + generics, + &source.generics.where_clause, + ); Self { name, variants, - msg_ty: ty, + msg_ty, contract: &source.self_ty, error, custom, @@ -414,61 +394,45 @@ impl<'a> ContractEnumMessage<'a> { custom, } = self; - let match_arms = variants - .iter() - .map(|variant| variant.emit_dispatch_leg(*msg_ty)); - let mut msgs: Vec = variants - .iter() - .map(|var| var.name.to_string().to_case(Case::Snake)) - .collect(); - msgs.sort(); - let msgs_cnt = msgs.len(); - let variants_constructors = variants.iter().map(MsgVariant::emit_variants_constructors); - let variants = variants.iter().map(MsgVariant::emit); + let match_arms = variants.emit_dispatch_legs(); + let generic_name = variants.emit_generic_name(name); + let unused_generics = variants.unused_generics(); + let unused_generics = emit_bracketed_generics(unused_generics); + + let mut variant_names = variants.as_names_snake_cased(); + variant_names.sort(); + let variants_cnt = variant_names.len(); + let variants_constructors = variants.emit_constructors(); + let variants = variants.emit(); let ctx_type = msg_ty.emit_ctx_type(&custom.query_or_default()); - let contract = StripGenerics.fold_type((*contract).clone()); let ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), error); - #[cfg(not(tarpaulin_include))] - let enum_declaration = match name.to_string().as_str() { - "QueryMsg" => { - quote! { - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, cosmwasm_schema::QueryResponses)] - #[serde(rename_all="snake_case")] - pub enum #name { - #(#variants,)* - } - } - } - _ => { - quote! { - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] - #[serde(rename_all="snake_case")] - pub enum #name { - #(#variants,)* - } - } - } + let derive_query = match msg_ty { + MsgType::Query => quote! { #sylvia ::cw_schema::QueryResponses }, + _ => quote! {}, }; #[cfg(not(tarpaulin_include))] { quote! { - #enum_declaration + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, #derive_query )] + #[serde(rename_all="snake_case")] + pub enum #generic_name { + #(#variants,)* + } - impl #name { - pub fn dispatch(self, contract: &#contract, ctx: #ctx_type) -> #ret_type { + impl #generic_name { + pub fn dispatch #unused_generics (self, contract: &#contract, ctx: #ctx_type) -> #ret_type { use #name::*; match self { #(#match_arms,)* } } - pub const fn messages() -> [&'static str; #msgs_cnt] { - [#(#msgs,)*] + pub const fn messages() -> [&'static str; #variants_cnt] { + [#(#variant_names,)*] } #(#variants_constructors)* @@ -479,6 +443,7 @@ impl<'a> ContractEnumMessage<'a> { } /// Representation of whole message variant +#[derive(Debug)] pub struct MsgVariant<'a> { name: Ident, function_name: &'a Ident, @@ -564,13 +529,14 @@ impl<'a> MsgVariant<'a> { /// Emits match leg dispatching against this variant. Assumes enum variants are imported into the /// scope. Dispatching is performed by calling the function this variant is build from on the /// `contract` variable, with `ctx` as its first argument - both of them should be in scope. - pub fn emit_dispatch_leg(&self, msg_type: MsgType) -> TokenStream { + pub fn emit_dispatch_leg(&self) -> TokenStream { use MsgType::*; let Self { name, fields, function_name, + msg_type, .. } = self; @@ -683,12 +649,23 @@ impl<'a> MsgVariant<'a> { } } } + + pub fn as_fields_names(&self) -> Vec<&Ident> { + self.fields.iter().map(MsgField::name).collect() + } + + pub fn emit_fields(&self) -> Vec { + self.fields.iter().map(MsgField::emit).collect() + } } +#[derive(Debug)] pub struct MsgVariants<'a, Generic> { variants: Vec>, used_generics: Vec<&'a Generic>, + unused_generics: Vec<&'a Generic>, where_predicates: Vec<&'a WherePredicate>, + msg_ty: MsgType, } impl<'a, Generic> MsgVariants<'a, Generic> @@ -697,11 +674,11 @@ where { pub fn new( source: VariantDescs<'a>, - msg_type: MsgType, - generics: &'a [&'a Generic], + msg_ty: MsgType, + all_generics: &'a [&'a Generic], unfiltered_where_clause: &'a Option, ) -> Self { - let mut generics_checker = CheckGenerics::new(generics); + let mut generics_checker = CheckGenerics::new(all_generics); let variants: Vec<_> = source .filter_map(|variant_desc| { let msg_attr = variant_desc.attr_msg()?; @@ -713,7 +690,7 @@ where } }; - if attr.msg_type() != msg_type { + if attr.msg_type() != msg_ty { return None; } @@ -725,13 +702,15 @@ where }) .collect(); - let (used_generics, _) = generics_checker.used_unused(); - let where_predicates = filter_wheres(unfiltered_where_clause, generics, &used_generics); + let (used_generics, unused_generics) = generics_checker.used_unused(); + let where_predicates = filter_wheres(unfiltered_where_clause, all_generics, &used_generics); Self { variants, used_generics, + unused_generics, where_predicates, + msg_ty, } } @@ -748,6 +727,18 @@ where &self.variants } + pub fn used_generics(&self) -> &Vec<&'a Generic> { + &self.used_generics + } + + pub fn unused_generics(&self) -> &Vec<&'a Generic> { + &self.unused_generics + } + + pub fn where_predicates(&'a self) -> &'a [&'a WherePredicate] { + &self.where_predicates + } + pub fn emit_querier(&self) -> TokenStream { let sylvia = crate_module(); let Self { @@ -843,9 +834,100 @@ where } } } + + pub fn emit_multitest_default_dispatch(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { + msg_ty, + used_generics, + .. + } = self; + + let values = msg_ty.emit_ctx_values(); + let msg_name = msg_ty.emit_msg_name(used_generics.as_slice()); + + quote! { + #sylvia ::cw_std::from_slice::< #msg_name >(&msg)? + .dispatch(self, ( #values )) + .map_err(Into::into) + } + } + + pub fn emit_default_entry_point( + &self, + custom_msg: &Type, + custom_query: &Type, + name: &Type, + error: &Type, + ) -> TokenStream { + let Self { + used_generics, + msg_ty, + .. + } = self; + let sylvia = crate_module(); + + let resp_type = match msg_ty { + MsgType::Query => quote! { #sylvia ::cw_std::Binary }, + _ => quote! { #sylvia ::cw_std::Response < #custom_msg > }, + }; + let params = msg_ty.emit_ctx_params(custom_query); + let values = msg_ty.emit_ctx_values(); + let ep_name = msg_ty.emit_ep_name(); + let msg_name = msg_ty.emit_msg_name(used_generics); + + quote! { + #[#sylvia ::cw_std::entry_point] + pub fn #ep_name ( + #params , + msg: #msg_name, + ) -> Result<#resp_type, #error> { + msg.dispatch(&#name ::new() , ( #values )).map_err(Into::into) + } + } + } + + pub fn emit_dispatch_legs(&self) -> impl Iterator + '_ { + self.variants + .iter() + .map(|variant| variant.emit_dispatch_leg()) + } + + pub fn as_names_snake_cased(&self) -> Vec { + self.variants + .iter() + .map(|variant| variant.name.to_string().to_case(Case::Snake)) + .collect() + } + + pub fn emit_constructors(&self) -> impl Iterator + '_ { + self.variants + .iter() + .map(MsgVariant::emit_variants_constructors) + } + + pub fn emit_generic_name(&self, name: &Ident) -> TokenStream { + let generics = emit_bracketed_generics(&self.used_generics); + + #[cfg(not(tarpaulin_include))] + { + quote! { + #name #generics + } + } + } + + pub fn emit(&self) -> impl Iterator + '_ { + self.variants.iter().map(MsgVariant::emit) + } + + pub fn get_only_variant(&self) -> Option<&MsgVariant> { + self.variants.first() + } } /// Representation of single message variant field +#[derive(Debug)] pub struct MsgField<'a> { name: &'a Ident, ty: &'a Type, @@ -931,6 +1013,7 @@ pub struct GlueMessage<'a> { error: &'a Type, custom: &'a Custom<'a>, interfaces: &'a Interfaces, + variants: &'a MsgVariants<'a, GenericParam>, } impl<'a> GlueMessage<'a> { @@ -941,6 +1024,7 @@ impl<'a> GlueMessage<'a> { error: &'a Type, custom: &'a Custom, interfaces: &'a Interfaces, + variants: &'a MsgVariants<'a, GenericParam>, ) -> Self { GlueMessage { name, @@ -949,12 +1033,12 @@ impl<'a> GlueMessage<'a> { error, custom, interfaces, + variants, } } pub fn emit(&self) -> TokenStream { let sylvia = crate_module(); - let Self { name, contract, @@ -962,18 +1046,23 @@ impl<'a> GlueMessage<'a> { error, custom, interfaces, + variants, } = self; - let contract = StripGenerics.fold_type((*contract).clone()); + let contract_name = StripGenerics.fold_type((*contract).clone()); let enum_name = Ident::new(&format!("Contract{}", name), name.span()); + let used_generics = variants.used_generics(); + let used_generics = emit_bracketed_generics(used_generics); + let unused_generics = variants.unused_generics(); + let unused_generics = emit_bracketed_generics(unused_generics); + let where_clause = variants.where_clause(); let variants = interfaces.emit_glue_message_variants(msg_ty); - let msg_name = quote! {#contract ( #name)}; - let mut messages_call_on_all_variants: Vec = - interfaces.emit_messages_call(msg_ty); - messages_call_on_all_variants.push(quote! {&#name :: messages()}); + let contract_variant = quote! { #contract_name ( #name ) }; + let mut messages_call = interfaces.emit_messages_call(msg_ty); + messages_call.push(quote! { &#name :: messages() }); - let variants_cnt = messages_call_on_all_variants.len(); + let variants_cnt = messages_call.len(); let dispatch_arms = interfaces.interfaces().iter().map(|interface| { let ContractMessageAttr { @@ -1002,7 +1091,8 @@ impl<'a> GlueMessage<'a> { } }); - let dispatch_arm = quote! {#enum_name :: #contract (msg) => msg.dispatch(contract, ctx)}; + let dispatch_arm = + quote! {#enum_name :: #contract_name (msg) => msg.dispatch(contract, ctx)}; let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(msg_ty); @@ -1011,8 +1101,8 @@ impl<'a> GlueMessage<'a> { let msgs = &#name :: messages(); if msgs.into_iter().any(|msg| msg == &recv_msg_name) { match val.deserialize_into() { - Ok(msg) => return Ok(Self:: #contract (msg)), - Err(err) => return Err(D::Error::custom(err)).map(Self:: #contract) + Ok(msg) => return Ok(Self:: #contract_name (msg)), + Err(err) => return Err(D::Error::custom(err)).map(Self:: #contract_name ) }; } }; @@ -1049,19 +1139,19 @@ impl<'a> GlueMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] #[serde(rename_all="snake_case", untagged)] - pub enum #enum_name { + pub enum #enum_name #used_generics { #(#variants,)* - #msg_name + #contract_variant } - impl #enum_name { - pub fn dispatch( + impl #used_generics #enum_name #used_generics { + pub fn dispatch #unused_generics #where_clause ( self, contract: &#contract, ctx: #ctx_type, ) -> #ret_type { const _: () = { - let msgs: [&[&str]; #variants_cnt] = [#(#messages_call_on_all_variants),*]; + let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*]; #sylvia ::utils::assert_no_intersection(msgs); }; @@ -1097,7 +1187,7 @@ impl<'a> GlueMessage<'a> { #contract_deserialization_attempt } - let msgs: [&[&str]; #variants_cnt] = [#(#messages_call_on_all_variants),*]; + let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*]; let mut err_msg = msgs.into_iter().flatten().fold( // It might be better to forward the error or serialization, but we just // deserialized it from JSON, not reason to expect failure here. @@ -1182,12 +1272,13 @@ impl<'a> InterfaceMessages<'a> { } pub struct EntryPoints<'a> { + source: &'a ItemImpl, name: Type, error: Type, custom: Custom<'a>, override_entry_points: OverrideEntryPoints, - has_migrate: bool, - reply: Option, + generics: Vec<&'a GenericParam>, + where_clause: &'a Option, } impl<'a> EntryPoints<'a> { @@ -1211,56 +1302,71 @@ impl<'a> EntryPoints<'a> { ) .unwrap_or_else(|| parse_quote! { #sylvia ::cw_std::StdError }); - let has_migrate = - !MsgVariants::::new(source.as_variants(), MsgType::Migrate, &[], &None) - .variants() - .is_empty(); - - let reply = - MsgVariants::::new(source.as_variants(), MsgType::Reply, &[], &None) - .variants() - .iter() - .map(|variant| variant.function_name.clone()) - .next(); + let generics: Vec<_> = source.generics.params.iter().collect(); + let where_clause = &source.generics.where_clause; let custom = Custom::new(&source.attrs); Self { + source, name, error, custom, override_entry_points, - has_migrate, - reply, + generics, + where_clause, } } pub fn emit(&self) -> TokenStream { let Self { + source, name, error, custom, override_entry_points, - has_migrate, - reply, + generics, + where_clause, } = self; let sylvia = crate_module(); let custom_msg = custom.msg_or_default(); let custom_query = custom.query_or_default(); + let instantiate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Instantiate, + generics, + where_clause, + ); + let exec_variants = + MsgVariants::new(source.as_variants(), MsgType::Exec, generics, where_clause); + let query_variants = + MsgVariants::new(source.as_variants(), MsgType::Query, generics, where_clause); + let migrate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Migrate, + generics, + where_clause, + ); + let reply = + MsgVariants::::new(source.as_variants(), MsgType::Reply, &[], &None) + .variants() + .iter() + .map(|variant| variant.function_name.clone()) + .next(); + #[cfg(not(tarpaulin_include))] { - let entry_points = [MsgType::Instantiate, MsgType::Exec, MsgType::Query] + let entry_points = [instantiate_variants, exec_variants, query_variants] .into_iter() .map( - |msg_type| match override_entry_points.get_entry_point(msg_type) { + |variants| match override_entry_points.get_entry_point(variants.msg_ty) { Some(_) => quote! {}, - None => OverrideEntryPoint::emit_default_entry_point( + None => variants.emit_default_entry_point( &custom_msg, &custom_query, name, error, - msg_type, ), }, ); @@ -1269,14 +1375,9 @@ impl<'a> EntryPoints<'a> { .get_entry_point(MsgType::Migrate) .is_none(); - let migrate = if migrate_not_overridden && *has_migrate { - OverrideEntryPoint::emit_default_entry_point( - &custom_msg, - &custom_query, - name, - error, - MsgType::Migrate, - ) + let migrate = if migrate_not_overridden && migrate_variants.get_only_variant().is_some() + { + migrate_variants.emit_default_entry_point(&custom_msg, &custom_query, name, error) } else { quote! {} }; diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs index 2600f92a..4389104b 100644 --- a/sylvia-derive/src/multitest.rs +++ b/sylvia-derive/src/multitest.rs @@ -5,14 +5,34 @@ use syn::parse::{Parse, Parser}; use syn::spanned::Spanned; use syn::{parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Pat, PatType, Path, Type}; -use crate::check_generics::{CheckGenerics, GetPath}; +use crate::check_generics::GetPath; use crate::crate_module; use crate::interfaces::Interfaces; -use crate::message::MsgField; -use crate::parser::{ - parse_struct_message, Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints, -}; -use crate::utils::{emit_bracketed_generics, extract_return_type, process_fields}; +use crate::message::{MsgVariant, MsgVariants}; +use crate::parser::{Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints}; +use crate::utils::{emit_bracketed_generics, extract_return_type}; +use crate::variant_descs::AsVariantDescs; + +fn interface_name(source: &ItemImpl) -> &Ident { + let trait_name = &source.trait_; + let Some(trait_name) = trait_name else { + unreachable!() + }; + let (_, Path { segments, .. }, _) = &trait_name; + assert!(!segments.is_empty()); + + &segments[0].ident +} + +fn extract_contract_name(contract: &Type) -> &Ident { + let Type::Path(type_path) = contract else { + unreachable!() + }; + let segments = &type_path.path.segments; + assert!(!segments.is_empty()); + let segment = &segments[0]; + &segment.ident +} struct MessageSignature<'a> { pub name: &'a Ident, @@ -27,7 +47,6 @@ pub struct MultitestHelpers<'a, Generics> { error_type: TokenStream, contract: &'a Type, is_trait: bool, - is_migrate: bool, reply: Option, source: &'a ItemImpl, generics: &'a [&'a Generics], @@ -36,27 +55,10 @@ pub struct MultitestHelpers<'a, Generics> { custom: &'a Custom<'a>, override_entry_points: &'a OverrideEntryPoints, interfaces: &'a Interfaces, -} - -fn interface_name(source: &ItemImpl) -> &Ident { - let trait_name = &source.trait_; - let Some(trait_name) = trait_name else { - unreachable!() - }; - let (_, Path { segments, .. }, _) = &trait_name; - assert!(!segments.is_empty()); - - &segments[0].ident -} - -fn extract_contract_name(contract: &Type) -> &Ident { - let Type::Path(type_path) = contract else { - unreachable!() - }; - let segments = &type_path.path.segments; - assert!(!segments.is_empty()); - let segment = &segments[0]; - &segment.ident + instantiate_variants: MsgVariants<'a, Generics>, + exec_variants: MsgVariants<'a, Generics>, + query_variants: MsgVariants<'a, Generics>, + migrate_variants: MsgVariants<'a, Generics>, } impl<'a, Generics> MultitestHelpers<'a, Generics> @@ -72,10 +74,27 @@ where override_entry_points: &'a OverrideEntryPoints, interfaces: &'a Interfaces, ) -> Self { - let mut is_migrate = false; let mut reply = None; let sylvia = crate_module(); + let where_clause = &source.generics.where_clause; + let instantiate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Instantiate, + generics, + where_clause, + ); + let exec_variants = + MsgVariants::new(source.as_variants(), MsgType::Exec, generics, where_clause); + let query_variants = + MsgVariants::new(source.as_variants(), MsgType::Query, generics, where_clause); + let migrate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Migrate, + generics, + where_clause, + ); + let messages: Vec<_> = source .items .iter() @@ -91,12 +110,10 @@ where }; let msg_ty = attr.msg_type(); - if msg_ty == MsgType::Migrate { - is_migrate = true; - } else if msg_ty == MsgType::Reply { + if msg_ty == MsgType::Reply { reply = Some(method.sig.ident.clone()); return None; - } else if msg_ty != MsgType::Query && msg_ty != MsgType::Exec { + } else if ![MsgType::Query, MsgType::Exec, MsgType::Migrate].contains(&msg_ty) { return None; } @@ -201,7 +218,6 @@ where error_type, contract, is_trait, - is_migrate, reply, source, generics, @@ -210,6 +226,10 @@ where custom, override_entry_points, interfaces, + instantiate_variants, + exec_variants, + query_variants, + migrate_variants, } } @@ -514,13 +534,15 @@ where } fn generate_contract_helpers(&self) -> TokenStream { + let sylvia = crate_module(); let Self { + source, error_type, is_trait, - source, generics, contract_name, proxy_name, + instantiate_variants, .. } = self; @@ -528,18 +550,34 @@ where return quote! {}; } - let sylvia = crate_module(); + let fields_names = instantiate_variants + .get_only_variant() + .map(MsgVariant::as_fields_names) + .unwrap_or(vec![]); - let mut generics_checker = CheckGenerics::new(generics); + let fields = instantiate_variants + .get_only_variant() + .map(MsgVariant::emit_fields) + .unwrap_or(vec![]); - let parsed = parse_struct_message(source, MsgType::Instantiate); - let Some((method, _)) = parsed else { - return quote! {}; + let used_generics = instantiate_variants.used_generics(); + let bracketed_used_generics = emit_bracketed_generics(used_generics); + let bracketed_generics = emit_bracketed_generics(generics); + let full_where_clause = &source.generics.where_clause; + + let where_predicates = instantiate_variants.where_predicates(); + let where_clause = instantiate_variants.where_clause(); + let contract = if !generics.is_empty() { + quote! { #contract_name ::< #(#generics,)* > } + } else { + quote! { #contract_name } }; - let instantiate_fields = process_fields(&method.sig, &mut generics_checker); - let fields_names: Vec<_> = instantiate_fields.iter().map(MsgField::name).collect(); - let fields = instantiate_fields.iter().map(MsgField::emit); + let instantiate_msg = if !used_generics.is_empty() { + quote! { InstantiateMsg::< #(#used_generics,)* > } + } else { + quote! { InstantiateMsg } + }; let impl_contract = self.generate_impl_contract(); @@ -582,10 +620,10 @@ where IbcT: #sylvia ::cw_multi_test::Ibc, GovT: #sylvia ::cw_multi_test::Gov, { - pub fn store_code(app: &'app #sylvia ::multitest::App< #mt_app >) -> Self { + pub fn store_code #bracketed_generics (app: &'app #sylvia ::multitest::App< #mt_app >) -> Self #full_where_clause { let code_id = app .app_mut() - .store_code(Box::new(#contract_name ::new())); + .store_code(Box::new(#contract ::new())); Self { code_id, app } } @@ -593,11 +631,11 @@ where self.code_id } - pub fn instantiate( + pub fn instantiate #bracketed_used_generics ( &self,#(#fields,)* - ) -> InstantiateProxy<'_, 'app, #mt_app > { - let msg = InstantiateMsg {#(#fields_names,)*}; - InstantiateProxy { + ) -> InstantiateProxy<'_, 'app, #mt_app, #(#used_generics,)* > #where_clause { + let msg = #instantiate_msg {#(#fields_names,)*}; + InstantiateProxy::<_, #(#used_generics,)* > { code_id: self, funds: &[], label: "Contract", @@ -607,17 +645,18 @@ where } } - pub struct InstantiateProxy<'a, 'app, MtApp> { + pub struct InstantiateProxy<'a, 'app, MtApp, #(#used_generics,)* > { code_id: &'a CodeId <'app, MtApp>, funds: &'a [#sylvia ::cw_std::Coin], label: &'a str, admin: Option, - msg: InstantiateMsg, + msg: InstantiateMsg #bracketed_used_generics, } - impl<'a, 'app, MtApp> InstantiateProxy<'a, 'app, MtApp> + impl<'a, 'app, MtApp, #(#used_generics,)* > InstantiateProxy<'a, 'app, MtApp, #(#used_generics,)* > where MtApp: Executor< #custom_msg >, + #(#where_predicates,)* { pub fn with_funds(self, funds: &'a [#sylvia ::cw_std::Coin]) -> Self { Self { funds, ..self } @@ -657,29 +696,35 @@ where fn generate_impl_contract(&self) -> TokenStream { let Self { + source, contract, custom, override_entry_points, + generics, + instantiate_variants, + exec_variants, + query_variants, + migrate_variants, .. } = self; let sylvia = crate_module(); + let bracketed_generics = emit_bracketed_generics(generics); + let full_where_clause = &source.generics.where_clause; let instantiate_body = override_entry_points .get_entry_point(MsgType::Instantiate) .map(OverrideEntryPoint::emit_multitest_dispatch) - .unwrap_or_else(|| { - OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Instantiate) - }); + .unwrap_or_else(|| instantiate_variants.emit_multitest_default_dispatch()); let exec_body = override_entry_points .get_entry_point(MsgType::Exec) .map(OverrideEntryPoint::emit_multitest_dispatch) - .unwrap_or_else(|| OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Exec)); + .unwrap_or_else(|| exec_variants.emit_multitest_default_dispatch()); let query_body = override_entry_points .get_entry_point(MsgType::Query) .map(OverrideEntryPoint::emit_multitest_dispatch) - .unwrap_or_else(|| OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Query)); + .unwrap_or_else(|| query_variants.emit_multitest_default_dispatch()); let sudo_body = override_entry_points .get_entry_point(MsgType::Sudo) @@ -692,8 +737,8 @@ where let migrate_body = match override_entry_points.get_entry_point(MsgType::Migrate) { Some(entry_point) => entry_point.emit_multitest_dispatch(), - None if self.is_migrate => { - OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Migrate) + None if migrate_variants.get_only_variant().is_some() => { + migrate_variants.emit_multitest_default_dispatch() } None => quote! { #sylvia ::anyhow::bail!("migrate not implemented for contract") @@ -723,7 +768,7 @@ where #[cfg(not(tarpaulin_include))] { quote! { - impl #sylvia ::cw_multi_test::Contract<#custom_msg, #custom_query> for #contract { + impl #bracketed_generics #sylvia ::cw_multi_test::Contract<#custom_msg, #custom_query> for #contract #full_where_clause { fn execute( &self, deps: #sylvia ::cw_std::DepsMut< #custom_query >, diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs index 9d352743..db6e875b 100644 --- a/sylvia-derive/src/parser.rs +++ b/sylvia-derive/src/parser.rs @@ -1,6 +1,6 @@ use proc_macro2::{Punct, TokenStream}; use proc_macro_error::emit_error; -use quote::quote; +use quote::{quote, ToTokens}; use syn::fold::Fold; use syn::parse::{Error, Nothing, Parse, ParseBuffer, ParseStream, Parser}; use syn::punctuated::Punctuated; @@ -145,13 +145,21 @@ impl MsgType { } } - pub fn emit_msg_name(&self) -> Type { + pub fn emit_msg_name(&self, generics: &[&Generic]) -> Type + where + Generic: ToTokens, + { + let generics = if !generics.is_empty() { + quote! { ::< #(#generics,)* > } + } else { + quote! {} + }; match self { - MsgType::Exec => parse_quote! { ContractExecMsg }, - MsgType::Query => parse_quote! { ContractQueryMsg }, - MsgType::Instantiate => parse_quote! { InstantiateMsg }, - MsgType::Migrate => parse_quote! { MigrateMsg }, - MsgType::Reply => parse_quote! { ReplyMsg }, + MsgType::Exec => parse_quote! { ContractExecMsg #generics }, + MsgType::Query => parse_quote! { ContractQueryMsg #generics }, + MsgType::Instantiate => parse_quote! { InstantiateMsg #generics }, + MsgType::Migrate => parse_quote! { MigrateMsg #generics }, + MsgType::Reply => parse_quote! { ReplyMsg #generics }, MsgType::Sudo => todo!(), } } @@ -516,49 +524,6 @@ impl OverrideEntryPoint { .map_err(Into::into) } } - - pub fn emit_multitest_default_dispatch(ty: MsgType) -> TokenStream { - let sylvia = crate_module(); - - let values = ty.emit_ctx_values(); - let msg_name = ty.emit_msg_name(); - - quote! { - #sylvia ::cw_std::from_slice::< #msg_name >(&msg)? - .dispatch(self, ( #values )) - .map_err(Into::into) - } - } - - #[cfg(not(tarpaulin_include))] - pub fn emit_default_entry_point( - custom_msg: &Type, - custom_query: &Type, - name: &Type, - error: &Type, - msg_type: MsgType, - ) -> TokenStream { - let sylvia = crate_module(); - - let resp_type = match msg_type { - MsgType::Query => quote! { #sylvia ::cw_std::Binary }, - _ => quote! { #sylvia ::cw_std::Response < #custom_msg > }, - }; - let params = msg_type.emit_ctx_params(custom_query); - let values = msg_type.emit_ctx_values(); - let ep_name = msg_type.emit_ep_name(); - let msg_name = msg_type.emit_msg_name(); - - quote! { - #[#sylvia ::cw_std::entry_point] - pub fn #ep_name ( - #params , - msg: #msg_name, - ) -> Result<#resp_type, #error> { - msg.dispatch(&#name ::new() , ( #values )).map_err(Into::into) - } - } - } } #[derive(Debug)] diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs index 73f36fd4..a386124e 100644 --- a/sylvia/tests/generics.rs +++ b/sylvia/tests/generics.rs @@ -41,7 +41,7 @@ pub mod whitelist { pub trait Whitelist where for<'msg_de> Msg: CustomMsg + Deserialize<'msg_de>, - for<'msg_de> QueryRet: CustomQuery + DeserializeOwned, + QueryRet: CustomQuery + DeserializeOwned, { type Error: From; @@ -79,6 +79,32 @@ pub mod non_generic { } } +pub mod generic_contract { + use cosmwasm_std::{CustomQuery, Response, StdResult}; + use serde::de::DeserializeOwned; + use serde::Deserialize; + use sylvia::types::{CustomMsg, InstantiateCtx}; + use sylvia_derive::contract; + + pub struct GenericContract(std::marker::PhantomData<(Msg, QueryRet)>); + + #[contract] + impl GenericContract + where + for<'msg_de> Msg: CustomMsg + Deserialize<'msg_de> + 'msg_de, + for<'a> QueryRet: CustomQuery + DeserializeOwned + 'a, + { + pub const fn new() -> Self { + Self(std::marker::PhantomData) + } + + #[msg(instantiate)] + pub fn instantiate(&self, _ctx: InstantiateCtx, _msg: Msg) -> StdResult { + Ok(Response::new()) + } + } +} + pub mod cw1_contract { use cosmwasm_std::{Response, StdResult}; use sylvia::types::InstantiateCtx; @@ -206,6 +232,7 @@ impl cosmwasm_std::CustomMsg for ExternalMsg {} #[cw_serde] pub struct ExternalQuery; impl cosmwasm_std::CustomQuery for ExternalQuery {} + #[cfg(all(test, feature = "mt"))] mod tests { use crate::cw1::{InterfaceTypes, Querier as Cw1Querier}; @@ -297,4 +324,21 @@ mod tests { .call(owner) .unwrap(); } + + #[test] + fn generic_contract() { + let app = App::default(); + let code_id = crate::generic_contract::multitest_utils::CodeId::store_code::< + ExternalMsg, + ExternalQuery, + >(&app); + + let owner = "owner"; + + code_id + .instantiate(ExternalMsg {}) + .with_label("GenericContract") + .call(owner) + .unwrap(); + } } From 1384d8e5245b67b805fa9b16fecf5936f1ae5108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 11 Oct 2023 10:50:11 +0200 Subject: [PATCH 07/15] feat: Multiple generic interfaces --- sylvia/tests/generics.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs index a386124e..5cb57c98 100644 --- a/sylvia/tests/generics.rs +++ b/sylvia/tests/generics.rs @@ -232,7 +232,6 @@ impl cosmwasm_std::CustomMsg for ExternalMsg {} #[cw_serde] pub struct ExternalQuery; impl cosmwasm_std::CustomQuery for ExternalQuery {} - #[cfg(all(test, feature = "mt"))] mod tests { use crate::cw1::{InterfaceTypes, Querier as Cw1Querier}; From ef30ccb9b391bc0e4e7006c16cf09954cd483171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Thu, 12 Oct 2023 10:24:11 +0200 Subject: [PATCH 08/15] feat: Support generic contract for simple contract --- sylvia-derive/src/message.rs | 194 +++++++++++++++++++++++ sylvia-derive/src/multitest.rs | 276 ++++++++------------------------- sylvia/tests/generics.rs | 1 + 3 files changed, 257 insertions(+), 214 deletions(-) diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 4a0d363b..443b11c2 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -650,6 +650,138 @@ impl<'a> MsgVariant<'a> { } } + pub fn emit_multitest_proxy_methods( + &self, + msg_ty: &MsgType, + custom_msg: &Type, + mt_app: &Type, + error_type: &Type, + ) -> TokenStream { + let sylvia = crate_module(); + let Self { + name, + fields, + return_type, + .. + } = self; + + let params = fields.iter().map(|field| field.emit_method_field()); + let arguments = fields.iter().map(MsgField::name); + let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); + + match msg_ty { + MsgType::Exec => quote! { + #[track_caller] + pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, ExecMsg, #mt_app, #custom_msg> { + let msg = ExecMsg:: #name ( #(#arguments),* ); + + #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) + } + }, + MsgType::Migrate => quote! { + #[track_caller] + pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::MigrateProxy::<#error_type, MigrateMsg, #mt_app, #custom_msg> { + let msg = MigrateMsg::new( #(#arguments),* ); + + #sylvia ::multitest::MigrateProxy::new(&self.contract_addr, msg, &self.app) + } + }, + MsgType::Query => quote! { + pub fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { + let msg = QueryMsg:: #name ( #(#arguments),* ); + + (*self.app) + .app() + .wrap() + .query_wasm_smart(self.contract_addr.clone(), &msg) + .map_err(Into::into) + } + }, + _ => quote! {}, + } + } + + pub fn emit_interface_multitest_proxy_methods( + &self, + msg_ty: &MsgType, + custom_msg: &Type, + mt_app: &Type, + error_type: &Type, + generics: &[&Generics], + module: &TokenStream, + ) -> TokenStream + where + Generics: ToTokens, + { + let sylvia = crate_module(); + let Self { + name, + fields, + return_type, + .. + } = self; + + let params = fields.iter().map(|field| field.emit_method_field()); + let arguments = fields.iter().map(MsgField::name); + let bracketed_generics = emit_bracketed_generics(generics); + let interface_enum = quote! { < #module InterfaceTypes #bracketed_generics as #sylvia ::types::InterfaceMessages> }; + let type_name = msg_ty.as_accessor_name(); + let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); + + match msg_ty { + MsgType::Exec => quote! { + #[track_caller] + fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, #mt_app, #custom_msg> { + let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* ); + + #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) + } + }, + MsgType::Query => quote! { + fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { + let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* ); + + (*self.app) + .app() + .wrap() + .query_wasm_smart(self.contract_addr.clone(), &msg) + .map_err(Into::into) + } + }, + _ => quote! {}, + } + } + + pub fn emit_proxy_methods_declarations( + &self, + msg_ty: &MsgType, + custom_msg: &Type, + error_type: &Type, + interface_enum: &TokenStream, + ) -> TokenStream { + let sylvia = crate_module(); + let Self { + name, + fields, + return_type, + .. + } = self; + + let params = fields.iter().map(|field| field.emit_method_field()); + let type_name = msg_ty.as_accessor_name(); + let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); + + match msg_ty { + MsgType::Exec => quote! { + fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, MtApp, #custom_msg>; + }, + MsgType::Query => quote! { + fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type>; + }, + _ => quote! {}, + } + } + pub fn as_fields_names(&self) -> Vec<&Ident> { self.fields.iter().map(MsgField::name).collect() } @@ -657,6 +789,10 @@ impl<'a> MsgVariant<'a> { pub fn emit_fields(&self) -> Vec { self.fields.iter().map(MsgField::emit).collect() } + + pub fn name(&self) -> &Ident { + &self.name + } } #[derive(Debug)] @@ -886,6 +1022,64 @@ where } } } + pub fn emit_multitest_proxy_methods( + &self, + custom_msg: &Type, + mt_app: &Type, + error_type: &Type, + ) -> Vec { + self.variants + .iter() + .map(|variant| { + variant.emit_multitest_proxy_methods(&self.msg_ty, custom_msg, mt_app, error_type) + }) + .collect() + } + + pub fn emit_interface_multitest_proxy_methods( + &self, + custom_msg: &Type, + mt_app: &Type, + error_type: &Type, + generics: &[&Generics], + module: &TokenStream, + ) -> Vec + where + Generics: ToTokens, + { + self.variants + .iter() + .map(|variant| { + variant.emit_interface_multitest_proxy_methods( + &self.msg_ty, + custom_msg, + mt_app, + error_type, + generics, + module, + ) + }) + .collect() + } + + pub fn emit_proxy_methods_declarations( + &self, + custom_msg: &Type, + error_type: &Type, + interface_enum: &TokenStream, + ) -> Vec { + self.variants + .iter() + .map(|variant| { + variant.emit_proxy_methods_declarations( + &self.msg_ty, + custom_msg, + error_type, + interface_enum, + ) + }) + .collect() + } pub fn emit_dispatch_legs(&self) -> impl Iterator + '_ { self.variants diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs index 4389104b..dbec15a1 100644 --- a/sylvia-derive/src/multitest.rs +++ b/sylvia-derive/src/multitest.rs @@ -1,16 +1,14 @@ +use convert_case::{Case, Casing}; use proc_macro2::{Ident, TokenStream}; -use proc_macro_error::emit_error; use quote::{quote, ToTokens}; -use syn::parse::{Parse, Parser}; -use syn::spanned::Spanned; -use syn::{parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Pat, PatType, Path, Type}; +use syn::{parse_quote, ImplItem, ItemImpl, ItemTrait, Path, Type}; use crate::check_generics::GetPath; use crate::crate_module; use crate::interfaces::Interfaces; use crate::message::{MsgVariant, MsgVariants}; -use crate::parser::{Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints}; -use crate::utils::{emit_bracketed_generics, extract_return_type}; +use crate::parser::{Custom, MsgType, OverrideEntryPoint, OverrideEntryPoints}; +use crate::utils::emit_bracketed_generics; use crate::variant_descs::AsVariantDescs; fn interface_name(source: &ItemImpl) -> &Ident { @@ -34,20 +32,10 @@ fn extract_contract_name(contract: &Type) -> &Ident { &segment.ident } -struct MessageSignature<'a> { - pub name: &'a Ident, - pub params: Vec, - pub arguments: Vec<&'a Ident>, - pub msg_ty: MsgType, - pub return_type: TokenStream, -} - pub struct MultitestHelpers<'a, Generics> { - messages: Vec>, - error_type: TokenStream, + error_type: Type, contract: &'a Type, is_trait: bool, - reply: Option, source: &'a ItemImpl, generics: &'a [&'a Generics], contract_name: &'a Ident, @@ -59,6 +47,7 @@ pub struct MultitestHelpers<'a, Generics> { exec_variants: MsgVariants<'a, Generics>, query_variants: MsgVariants<'a, Generics>, migrate_variants: MsgVariants<'a, Generics>, + reply_variants: MsgVariants<'a, Generics>, } impl<'a, Generics> MultitestHelpers<'a, Generics> @@ -74,9 +63,6 @@ where override_entry_points: &'a OverrideEntryPoints, interfaces: &'a Interfaces, ) -> Self { - let mut reply = None; - let sylvia = crate_module(); - let where_clause = &source.generics.where_clause; let instantiate_variants = MsgVariants::new( source.as_variants(), @@ -94,88 +80,10 @@ where generics, where_clause, ); + let reply_variants = + MsgVariants::new(source.as_variants(), MsgType::Reply, generics, where_clause); - let messages: Vec<_> = source - .items - .iter() - .filter_map(|item| match item { - ImplItem::Method(method) => { - let msg_attr = method.attrs.iter().find(|attr| attr.path.is_ident("msg"))?; - let attr = match MsgAttr::parse.parse2(msg_attr.tokens.clone()) { - Ok(attr) => attr, - Err(err) => { - emit_error!(method.span(), err); - return None; - } - }; - let msg_ty = attr.msg_type(); - - if msg_ty == MsgType::Reply { - reply = Some(method.sig.ident.clone()); - return None; - } else if ![MsgType::Query, MsgType::Exec, MsgType::Migrate].contains(&msg_ty) { - return None; - } - - let sig = &method.sig; - let return_type = if let MsgAttr::Query { resp_type } = attr { - match resp_type { - Some(resp_type) => quote! {#resp_type}, - None => { - let return_type = extract_return_type(&sig.output); - quote! {#return_type} - } - } - } else { - quote! { #sylvia ::cw_multi_test::AppResponse } - }; - - let name = &sig.ident; - let params: Vec<_> = sig - .inputs - .iter() - .skip(2) - .filter_map(|arg| match arg { - FnArg::Typed(ty) => { - let name = match ty.pat.as_ref() { - Pat::Ident(ident) => &ident.ident, - _ => return None, - }; - let ty = &ty.ty; - Some(quote! {#name : #ty}) - } - _ => None, - }) - .collect(); - let arguments: Vec<_> = sig - .inputs - .iter() - .skip(2) - .filter_map(|arg| match arg { - FnArg::Typed(item) => { - let PatType { pat, .. } = item; - let Pat::Ident(ident) = pat.as_ref() else { - unreachable!() - }; - Some(&ident.ident) - } - _ => None, - }) - .collect(); - - Some(MessageSignature { - name, - params, - arguments, - msg_ty, - return_type, - }) - } - _ => None, - }) - .collect(); - - let error_type = if is_trait { + let error_type: Type = if is_trait { let error_type: Vec<_> = source .items .iter() @@ -198,9 +106,9 @@ where assert!(!error_type.is_empty()); let error_type = error_type[0]; - quote! {#error_type} + parse_quote! {#error_type} } else { - quote! {#contract_error} + parse_quote! {#contract_error} }; let contract = &source.self_ty; @@ -214,11 +122,9 @@ where }; Self { - messages, error_type, contract, is_trait, - reply, source, generics, contract_name, @@ -230,17 +136,20 @@ where exec_variants, query_variants, migrate_variants, + reply_variants, } } pub fn emit(&self) -> TokenStream { let Self { - messages, error_type, proxy_name, is_trait, custom, interfaces, + exec_variants, + query_variants, + migrate_variants, .. } = self; let sylvia = crate_module(); @@ -265,49 +174,12 @@ where > }; - #[cfg(not(tarpaulin_include))] - let messages = messages.iter().map(|msg| { - let MessageSignature { - name, - params, - arguments, - msg_ty, - return_type, - } = msg; - if msg_ty == &MsgType::Exec { - quote! { - #[track_caller] - pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, ExecMsg, #mt_app, #custom_msg> { - let msg = ExecMsg:: #name ( #(#arguments),* ); - - #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) - } - } - } else if msg_ty == &MsgType::Migrate { - quote! { - #[track_caller] - pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::MigrateProxy::<#error_type, MigrateMsg, #mt_app, #custom_msg> { - let msg = MigrateMsg::new( #(#arguments),* ); - - #sylvia ::multitest::MigrateProxy::new(&self.contract_addr, msg, &self.app) - } - } - } else if msg_ty == &MsgType::Query { - quote! { - pub fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { - let msg = QueryMsg:: #name ( #(#arguments),* ); - - (*self.app) - .app() - .wrap() - .query_wasm_smart(self.contract_addr.clone(), &msg) - .map_err(Into::into) - } - } - } else { - quote! {} - } - }); + let exec_methods = + exec_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type); + let query_methods = + query_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type); + let migrate_methods = + migrate_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type); let contract_block = self.generate_contract_helpers(); @@ -353,9 +225,10 @@ where #proxy_name{ contract_addr, app } } - #(#messages)* - - #(#proxy_accessors)* + #( #exec_methods )* + #( #migrate_methods )* + #( #query_methods )* + #( #proxy_accessors )* } impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> @@ -397,11 +270,12 @@ where fn impl_trait_on_proxy(&self) -> TokenStream { let Self { - messages, error_type, custom, interfaces, generics, + exec_variants, + query_variants, .. } = self; @@ -422,7 +296,7 @@ where let custom_msg = custom.msg_or_default(); #[cfg(not(tarpaulin_include))] - let mt_app = quote! { + let mt_app = parse_quote! { #sylvia ::cw_multi_test::App< BankT, ApiT, @@ -439,60 +313,27 @@ where let bracketed_generics = emit_bracketed_generics(generics); let interface_enum = quote! { < #module InterfaceTypes #bracketed_generics as #sylvia ::types::InterfaceMessages> }; - #[cfg(not(tarpaulin_include))] - let methods_definitions = messages.iter().map(|msg| { - let MessageSignature { - name, - params, - arguments, - msg_ty, - return_type, - } = msg; - let type_name = msg_ty.as_accessor_name(); - if msg_ty == &MsgType::Exec { - quote! { - #[track_caller] - fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, #mt_app, #custom_msg> { - let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* ); - - #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) - } - } - } else { - quote! { - fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { - let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* ); - - (*self.app) - .app() - .wrap() - .query_wasm_smart(self.contract_addr.clone(), &msg) - .map_err(Into::into) - } - } - } - }); - - #[cfg(not(tarpaulin_include))] - let methods_declarations = messages.iter().map(|msg| { - let MessageSignature { - name, - params, - msg_ty, - return_type, - .. - } = msg; - let type_name = msg_ty.as_accessor_name(); - if msg_ty == &MsgType::Exec { - quote! { - fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, MtApp, #custom_msg>; - } - } else { - quote! { - fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type>; - } - } - }); + let exec_methods = exec_variants.emit_interface_multitest_proxy_methods( + &custom_msg, + &mt_app, + error_type, + generics, + &module, + ); + let query_methods = query_variants.emit_interface_multitest_proxy_methods( + &custom_msg, + &mt_app, + error_type, + generics, + &module, + ); + let exec_methods_declarations = + exec_variants.emit_proxy_methods_declarations(&custom_msg, error_type, &interface_enum); + let query_methods_declarations = query_variants.emit_proxy_methods_declarations( + &custom_msg, + error_type, + &interface_enum, + ); #[cfg(not(tarpaulin_include))] { @@ -501,7 +342,8 @@ where use super::*; pub trait #trait_name { - #(#methods_declarations)* + #(#query_methods_declarations)* + #(#exec_methods_declarations)* } impl #trait_name< #mt_app > for #module trait_utils:: #proxy_name<'_, #mt_app > @@ -525,8 +367,8 @@ where CustomT::QueryT: #sylvia:: cw_std::CustomQuery + #sylvia ::serde::de::DeserializeOwned + 'static, #mt_app : #sylvia ::cw_multi_test::Executor< #custom_msg > { - - #(#methods_definitions)* + #(#query_methods)* + #(#exec_methods)* } } } @@ -705,6 +547,7 @@ where exec_variants, query_variants, migrate_variants, + reply_variants, .. } = self; let sylvia = crate_module(); @@ -747,12 +590,17 @@ where let reply_body = match override_entry_points.get_entry_point(MsgType::Reply) { Some(entry_point) => entry_point.emit_multitest_dispatch(), - None => self - .reply + None => reply_variants + .get_only_variant() .as_ref() .map(|reply| { + let reply_name = reply.name(); + let reply_name = Ident::new( + &reply_name.to_string().to_case(Case::Snake), + reply_name.span(), + ); quote! { - self. #reply((deps, env).into(), msg).map_err(Into::into) + self. #reply_name ((deps, env).into(), msg).map_err(Into::into) } }) .unwrap_or_else(|| { diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs index 5cb57c98..a386124e 100644 --- a/sylvia/tests/generics.rs +++ b/sylvia/tests/generics.rs @@ -232,6 +232,7 @@ impl cosmwasm_std::CustomMsg for ExternalMsg {} #[cw_serde] pub struct ExternalQuery; impl cosmwasm_std::CustomQuery for ExternalQuery {} + #[cfg(all(test, feature = "mt"))] mod tests { use crate::cw1::{InterfaceTypes, Querier as Cw1Querier}; From 33e02364a32f74dbd0b874c58da59ca128c5cdea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 18 Oct 2023 12:29:45 +0200 Subject: [PATCH 09/15] feat: Support generics on every message type --- sylvia-derive/src/input.rs | 54 ++++-------- sylvia-derive/src/message.rs | 155 +++++++++++++++++++-------------- sylvia-derive/src/multitest.rs | 69 +++++++++------ sylvia-derive/src/parser.rs | 24 ++--- sylvia/tests/generics.rs | 72 ++++++++++++--- 5 files changed, 216 insertions(+), 158 deletions(-) diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 8f9f2400..74061b64 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -185,16 +185,16 @@ impl<'a> ImplInput<'a> { let Self { item, generics, .. } = self; let multitest_helpers = self.emit_multitest_helpers(generics); let where_clause = &item.generics.where_clause; - let variants = MsgVariants::new( + + let querier = MsgVariants::new( self.item.as_variants(), MsgType::Query, generics, where_clause, - ); - - let messages = self.emit_messages(&variants); + ) + .emit_querier(); + let messages = self.emit_messages(); let remote = Remote::new(&self.interfaces).emit(); - let querier = variants.emit_querier(); let querier_from_impl = self.interfaces.emit_querier_from_impl(); #[cfg(not(tarpaulin_include))] @@ -213,23 +213,13 @@ impl<'a> ImplInput<'a> { } } - fn emit_messages(&self, variants: &MsgVariants) -> TokenStream { + fn emit_messages(&self) -> TokenStream { let instantiate = self.emit_struct_msg(MsgType::Instantiate); let migrate = self.emit_struct_msg(MsgType::Migrate); - let exec_impl = - self.emit_enum_msg(&Ident::new("ExecMsg", Span::mixed_site()), MsgType::Exec); - let query_impl = - self.emit_enum_msg(&Ident::new("QueryMsg", Span::mixed_site()), MsgType::Query); - let exec = self.emit_glue_msg( - &Ident::new("ExecMsg", Span::mixed_site()), - MsgType::Exec, - variants, - ); - let query = self.emit_glue_msg( - &Ident::new("QueryMsg", Span::mixed_site()), - MsgType::Query, - variants, - ); + let exec_impl = self.emit_enum_msg(MsgType::Exec); + let query_impl = self.emit_enum_msg(MsgType::Query); + let exec = self.emit_glue_msg(MsgType::Exec); + let query = self.emit_glue_msg(MsgType::Query); #[cfg(not(tarpaulin_include))] { @@ -254,26 +244,16 @@ impl<'a> ImplInput<'a> { .map_or(quote! {}, |msg| msg.emit()) } - fn emit_enum_msg(&self, name: &Ident, msg_ty: MsgType) -> TokenStream { - ContractEnumMessage::new( - name, - self.item, - msg_ty, - &self.generics, - &self.error, - &self.custom, - ) - .emit() + fn emit_enum_msg(&self, msg_ty: MsgType) -> TokenStream { + ContractEnumMessage::new(self.item, msg_ty, &self.generics, &self.error, &self.custom) + .emit() } - fn emit_glue_msg( - &self, - name: &Ident, - msg_ty: MsgType, - variants: &MsgVariants, - ) -> TokenStream { + fn emit_glue_msg(&self, msg_ty: MsgType) -> TokenStream { + let Self { generics, item, .. } = self; + let where_clause = &item.generics.where_clause; + let variants = MsgVariants::new(item.as_variants(), msg_ty, generics, where_clause); GlueMessage::new( - name, self.item, msg_ty, &self.error, diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 443b11c2..a067a7c3 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -348,37 +348,32 @@ impl<'a> EnumMessage<'a> { /// Representation of single enum message pub struct ContractEnumMessage<'a> { - name: &'a Ident, variants: MsgVariants<'a, GenericParam>, msg_ty: MsgType, contract: &'a Type, error: &'a Type, custom: &'a Custom<'a>, + where_clause: &'a Option, } impl<'a> ContractEnumMessage<'a> { pub fn new( - name: &'a Ident, source: &'a ItemImpl, msg_ty: MsgType, generics: &'a [&'a GenericParam], error: &'a Type, custom: &'a Custom, ) -> Self { - let variants = MsgVariants::new( - source.as_variants(), - msg_ty, - generics, - &source.generics.where_clause, - ); + let where_clause = &source.generics.where_clause; + let variants = MsgVariants::new(source.as_variants(), msg_ty, generics, where_clause); Self { - name, variants, msg_ty, contract: &source.self_ty, error, custom, + where_clause, } } @@ -386,18 +381,21 @@ impl<'a> ContractEnumMessage<'a> { let sylvia = crate_module(); let Self { - name, variants, msg_ty, contract, error, custom, + where_clause, + .. } = self; + let enum_name = msg_ty.emit_msg_name(false); let match_arms = variants.emit_dispatch_legs(); - let generic_name = variants.emit_generic_name(name); let unused_generics = variants.unused_generics(); let unused_generics = emit_bracketed_generics(unused_generics); + let used_generics = variants.used_generics(); + let used_generics = emit_bracketed_generics(used_generics); let mut variant_names = variants.as_names_snake_cased(); variant_names.sort(); @@ -419,13 +417,13 @@ impl<'a> ContractEnumMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, #derive_query )] #[serde(rename_all="snake_case")] - pub enum #generic_name { + pub enum #enum_name #used_generics { #(#variants,)* } - impl #generic_name { - pub fn dispatch #unused_generics (self, contract: &#contract, ctx: #ctx_type) -> #ret_type { - use #name::*; + impl #used_generics #enum_name #used_generics { + pub fn dispatch #unused_generics (self, contract: &#contract, ctx: #ctx_type) -> #ret_type #where_clause { + use #enum_name::*; match self { #(#match_arms,)* @@ -650,13 +648,17 @@ impl<'a> MsgVariant<'a> { } } - pub fn emit_multitest_proxy_methods( + pub fn emit_multitest_proxy_methods( &self, msg_ty: &MsgType, custom_msg: &Type, mt_app: &Type, error_type: &Type, - ) -> TokenStream { + generics: &[&Generic], + ) -> TokenStream + where + Generic: ToTokens, + { let sylvia = crate_module(); let Self { name, @@ -668,27 +670,33 @@ impl<'a> MsgVariant<'a> { let params = fields.iter().map(|field| field.emit_method_field()); let arguments = fields.iter().map(MsgField::name); let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); + let enum_name = msg_ty.emit_msg_name(false); + let enum_name: Type = if !generics.is_empty() { + parse_quote! { #enum_name ::< #(#generics,)* > } + } else { + parse_quote! { #enum_name } + }; match msg_ty { MsgType::Exec => quote! { #[track_caller] - pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, ExecMsg, #mt_app, #custom_msg> { - let msg = ExecMsg:: #name ( #(#arguments),* ); + pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #enum_name, #mt_app, #custom_msg> { + let msg = #enum_name :: #name ( #(#arguments),* ); #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) } }, MsgType::Migrate => quote! { #[track_caller] - pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::MigrateProxy::<#error_type, MigrateMsg, #mt_app, #custom_msg> { - let msg = MigrateMsg::new( #(#arguments),* ); + pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::MigrateProxy::<#error_type, #enum_name, #mt_app, #custom_msg> { + let msg = #enum_name ::new( #(#arguments),* ); #sylvia ::multitest::MigrateProxy::new(&self.contract_addr, msg, &self.app) } }, MsgType::Query => quote! { pub fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { - let msg = QueryMsg:: #name ( #(#arguments),* ); + let msg = #enum_name :: #name ( #(#arguments),* ); (*self.app) .app() @@ -871,8 +879,13 @@ where &self.unused_generics } - pub fn where_predicates(&'a self) -> &'a [&'a WherePredicate] { - &self.where_predicates + pub fn as_where_clause(&'a self) -> Option { + let where_predicates = &self.where_predicates; + if !where_predicates.is_empty() { + Some(parse_quote!( where #(#where_predicates,)* )) + } else { + None + } } pub fn emit_querier(&self) -> TokenStream { @@ -980,10 +993,11 @@ where } = self; let values = msg_ty.emit_ctx_values(); - let msg_name = msg_ty.emit_msg_name(used_generics.as_slice()); + let msg_name = msg_ty.emit_msg_name(true); + let bracketed_generics = emit_bracketed_generics(used_generics); quote! { - #sylvia ::cw_std::from_slice::< #msg_name >(&msg)? + #sylvia ::cw_std::from_slice::< #msg_name #bracketed_generics >(&msg)? .dispatch(self, ( #values )) .map_err(Into::into) } @@ -1010,13 +1024,14 @@ where let params = msg_ty.emit_ctx_params(custom_query); let values = msg_ty.emit_ctx_values(); let ep_name = msg_ty.emit_ep_name(); - let msg_name = msg_ty.emit_msg_name(used_generics); + let msg_name = msg_ty.emit_msg_name(true); + let bracketed_generics = emit_bracketed_generics(used_generics); quote! { #[#sylvia ::cw_std::entry_point] pub fn #ep_name ( #params , - msg: #msg_name, + msg: #msg_name #bracketed_generics, ) -> Result<#resp_type, #error> { msg.dispatch(&#name ::new() , ( #values )).map_err(Into::into) } @@ -1031,7 +1046,13 @@ where self.variants .iter() .map(|variant| { - variant.emit_multitest_proxy_methods(&self.msg_ty, custom_msg, mt_app, error_type) + variant.emit_multitest_proxy_methods( + &self.msg_ty, + custom_msg, + mt_app, + error_type, + &self.used_generics, + ) }) .collect() } @@ -1100,17 +1121,6 @@ where .map(MsgVariant::emit_variants_constructors) } - pub fn emit_generic_name(&self, name: &Ident) -> TokenStream { - let generics = emit_bracketed_generics(&self.used_generics); - - #[cfg(not(tarpaulin_include))] - { - quote! { - #name #generics - } - } - } - pub fn emit(&self) -> impl Iterator + '_ { self.variants.iter().map(MsgVariant::emit) } @@ -1201,27 +1211,26 @@ impl<'a> MsgField<'a> { /// Glue message is the message composing Exec/Query messages from several traits #[derive(Debug)] pub struct GlueMessage<'a> { - name: &'a Ident, + source: &'a ItemImpl, contract: &'a Type, msg_ty: MsgType, error: &'a Type, custom: &'a Custom<'a>, interfaces: &'a Interfaces, - variants: &'a MsgVariants<'a, GenericParam>, + variants: MsgVariants<'a, GenericParam>, } impl<'a> GlueMessage<'a> { pub fn new( - name: &'a Ident, source: &'a ItemImpl, msg_ty: MsgType, error: &'a Type, custom: &'a Custom, interfaces: &'a Interfaces, - variants: &'a MsgVariants<'a, GenericParam>, + variants: MsgVariants<'a, GenericParam>, ) -> Self { GlueMessage { - name, + source, contract: &source.self_ty, msg_ty, error, @@ -1234,7 +1243,7 @@ impl<'a> GlueMessage<'a> { pub fn emit(&self) -> TokenStream { let sylvia = crate_module(); let Self { - name, + source, contract, msg_ty, error, @@ -1242,19 +1251,28 @@ impl<'a> GlueMessage<'a> { interfaces, variants, } = self; - let contract_name = StripGenerics.fold_type((*contract).clone()); - let enum_name = Ident::new(&format!("Contract{}", name), name.span()); + let used_generics = variants.used_generics(); - let used_generics = emit_bracketed_generics(used_generics); let unused_generics = variants.unused_generics(); + let where_clause = variants.as_where_clause(); + let full_where_clause = &source.generics.where_clause; + + let contract_enum_name = msg_ty.emit_msg_name(true); + let enum_name = msg_ty.emit_msg_name(false); + let contract_name = StripGenerics.fold_type((*contract).clone()); let unused_generics = emit_bracketed_generics(unused_generics); - let where_clause = variants.where_clause(); + let bracketed_used_generics = emit_bracketed_generics(used_generics); let variants = interfaces.emit_glue_message_variants(msg_ty); - let contract_variant = quote! { #contract_name ( #name ) }; + let contract_variant = quote! { #contract_name ( #enum_name #bracketed_used_generics ) }; let mut messages_call = interfaces.emit_messages_call(msg_ty); - messages_call.push(quote! { &#name :: messages() }); + let prefixed_used_generics = if !used_generics.is_empty() { + quote! { :: #bracketed_used_generics } + } else { + quote! {} + }; + messages_call.push(quote! { &#enum_name #prefixed_used_generics :: messages() }); let variants_cnt = messages_call.len(); @@ -1277,22 +1295,22 @@ impl<'a> GlueMessage<'a> { match (msg_ty, customs.has_msg) { (MsgType::Exec, true) => quote! { - #enum_name:: #variant(msg) => #sylvia ::into_response::IntoResponse::into_response(msg.dispatch(contract, Into::into( #ctx ))?) + #contract_enum_name:: #variant(msg) => #sylvia ::into_response::IntoResponse::into_response(msg.dispatch(contract, Into::into( #ctx ))?) }, _ => quote! { - #enum_name :: #variant(msg) => msg.dispatch(contract, Into::into( #ctx )) + #contract_enum_name :: #variant(msg) => msg.dispatch(contract, Into::into( #ctx )) }, } }); let dispatch_arm = - quote! {#enum_name :: #contract_name (msg) => msg.dispatch(contract, ctx)}; + quote! {#contract_enum_name :: #contract_name (msg) => msg.dispatch(contract, ctx)}; let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(msg_ty); #[cfg(not(tarpaulin_include))] let contract_deserialization_attempt = quote! { - let msgs = &#name :: messages(); + let msgs = &#enum_name #prefixed_used_generics :: messages(); if msgs.into_iter().any(|msg| msg == &recv_msg_name) { match val.deserialize_into() { Ok(msg) => return Ok(Self:: #contract_name (msg)), @@ -1305,15 +1323,16 @@ impl<'a> GlueMessage<'a> { let ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), error); let mut response_schemas_calls = interfaces.emit_response_schemas_calls(msg_ty); - response_schemas_calls.push(quote! {#name :: response_schemas_impl()}); + response_schemas_calls + .push(quote! {#enum_name #prefixed_used_generics :: response_schemas_impl()}); - let response_schemas = match name.to_string().as_str() { - "QueryMsg" => { + let response_schemas = match msg_ty { + MsgType::Query => { #[cfg(not(tarpaulin_include))] { quote! { #[cfg(not(target_arch = "wasm32"))] - impl #sylvia ::cw_schema::QueryResponses for #enum_name { + impl #bracketed_used_generics #sylvia ::cw_schema::QueryResponses for #contract_enum_name #bracketed_used_generics #where_clause { fn response_schemas_impl() -> std::collections::BTreeMap { let responses = [#(#response_schemas_calls),*]; responses.into_iter().flatten().collect() @@ -1333,21 +1352,23 @@ impl<'a> GlueMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] #[serde(rename_all="snake_case", untagged)] - pub enum #enum_name #used_generics { + pub enum #contract_enum_name #bracketed_used_generics { #(#variants,)* #contract_variant } - impl #used_generics #enum_name #used_generics { - pub fn dispatch #unused_generics #where_clause ( + impl #bracketed_used_generics #contract_enum_name #bracketed_used_generics { + pub fn dispatch #unused_generics ( self, contract: &#contract, ctx: #ctx_type, - ) -> #ret_type { - const _: () = { + ) -> #ret_type #full_where_clause { + const fn assert_no_intersection #bracketed_used_generics () #where_clause { let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*]; #sylvia ::utils::assert_no_intersection(msgs); - }; + } + + assert_no_intersection #prefixed_used_generics (); match self { #(#dispatch_arms,)* @@ -1358,7 +1379,7 @@ impl<'a> GlueMessage<'a> { #response_schemas - impl<'de> serde::Deserialize<'de> for #enum_name { + impl<'de, #(#used_generics,)* > serde::Deserialize<'de> for #contract_enum_name #bracketed_used_generics #where_clause { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs index dbec15a1..f958cd5c 100644 --- a/sylvia-derive/src/multitest.rs +++ b/sylvia-derive/src/multitest.rs @@ -38,6 +38,7 @@ pub struct MultitestHelpers<'a, Generics> { is_trait: bool, source: &'a ItemImpl, generics: &'a [&'a Generics], + where_clause: &'a Option, contract_name: &'a Ident, proxy_name: Ident, custom: &'a Custom<'a>, @@ -127,6 +128,7 @@ where is_trait, source, generics, + where_clause, contract_name, proxy_name, custom, @@ -150,6 +152,8 @@ where exec_variants, query_variants, migrate_variants, + generics, + where_clause, .. } = self; let sylvia = crate_module(); @@ -180,6 +184,9 @@ where query_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type); let migrate_methods = migrate_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type); + let where_predicates = where_clause + .as_ref() + .map(|where_clause| &where_clause.predicates); let contract_block = self.generate_contract_helpers(); @@ -195,13 +202,14 @@ where #[derive(Derivative)] #[derivative(Debug)] - pub struct #proxy_name <'app, MtApp> { + pub struct #proxy_name <'app, MtApp, #(#generics,)* > { pub contract_addr: #sylvia ::cw_std::Addr, #[derivative(Debug="ignore")] pub app: &'app #sylvia ::multitest::App, + _phantom: std::marker::PhantomData<( #(#generics,)* )>, } - impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> #proxy_name <'app, #mt_app > + impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > #proxy_name <'app, #mt_app, #(#generics,)* > where CustomT: #sylvia ::cw_multi_test::Module, CustomT::ExecT: std::fmt::Debug @@ -219,10 +227,11 @@ where DistrT: #sylvia ::cw_multi_test::Distribution, IbcT: #sylvia ::cw_multi_test::Ibc, GovT: #sylvia ::cw_multi_test::Gov, - #mt_app : Executor< #custom_msg > + #mt_app : Executor< #custom_msg >, + #where_predicates { pub fn new(contract_addr: #sylvia ::cw_std::Addr, app: &'app #sylvia ::multitest::App< #mt_app >) -> Self { - #proxy_name{ contract_addr, app } + #proxy_name { contract_addr, app, _phantom: std::marker::PhantomData::default() } } #( #exec_methods )* @@ -231,12 +240,12 @@ where #( #proxy_accessors )* } - impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> + impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > From<( #sylvia ::cw_std::Addr, &'app #sylvia ::multitest::App<#mt_app>, )> - for #proxy_name <'app, #mt_app > + for #proxy_name <'app, #mt_app, #(#generics,)* > where CustomT: #sylvia ::cw_multi_test::Module, CustomT::ExecT: std::fmt::Debug @@ -255,9 +264,10 @@ where IbcT: #sylvia ::cw_multi_test::Ibc, GovT: #sylvia ::cw_multi_test::Gov, #mt_app : Executor< #custom_msg >, + #where_predicates { fn from(input: (#sylvia ::cw_std::Addr, &'app #sylvia ::multitest::App< #mt_app >)) - -> #proxy_name<'app, #mt_app > { + -> #proxy_name<'app, #mt_app, #(#generics,)* > { #proxy_name::new(input.0, input.1) } } @@ -378,10 +388,10 @@ where fn generate_contract_helpers(&self) -> TokenStream { let sylvia = crate_module(); let Self { - source, error_type, is_trait, generics, + where_clause, contract_name, proxy_name, instantiate_variants, @@ -404,11 +414,10 @@ where let used_generics = instantiate_variants.used_generics(); let bracketed_used_generics = emit_bracketed_generics(used_generics); - let bracketed_generics = emit_bracketed_generics(generics); - let full_where_clause = &source.generics.where_clause; - let where_predicates = instantiate_variants.where_predicates(); - let where_clause = instantiate_variants.where_clause(); + let where_predicates = where_clause + .as_ref() + .map(|where_clause| &where_clause.predicates); let contract = if !generics.is_empty() { quote! { #contract_name ::< #(#generics,)* > } } else { @@ -446,12 +455,14 @@ where quote! { #impl_contract - pub struct CodeId<'app, MtApp> { + pub struct CodeId<'app, MtApp, #(#generics,)* > { code_id: u64, app: &'app #sylvia ::multitest::App, + _phantom: std::marker::PhantomData<( #(#generics,)* )>, + } - impl<'app, BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT> CodeId<'app, #mt_app> + impl<'app, BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > CodeId<'app, #mt_app, #(#generics,)* > where BankT: #sylvia ::cw_multi_test::Bank, ApiT: #sylvia ::cw_std::Api, @@ -461,23 +472,24 @@ where DistrT: #sylvia ::cw_multi_test::Distribution, IbcT: #sylvia ::cw_multi_test::Ibc, GovT: #sylvia ::cw_multi_test::Gov, + #where_predicates { - pub fn store_code #bracketed_generics (app: &'app #sylvia ::multitest::App< #mt_app >) -> Self #full_where_clause { + pub fn store_code(app: &'app #sylvia ::multitest::App< #mt_app >) -> Self { let code_id = app .app_mut() .store_code(Box::new(#contract ::new())); - Self { code_id, app } + Self { code_id, app, _phantom: std::marker::PhantomData::default() } } pub fn code_id(&self) -> u64 { self.code_id } - pub fn instantiate #bracketed_used_generics ( + pub fn instantiate( &self,#(#fields,)* - ) -> InstantiateProxy<'_, 'app, #mt_app, #(#used_generics,)* > #where_clause { + ) -> InstantiateProxy<'_, 'app, #mt_app, #(#generics,)* > { let msg = #instantiate_msg {#(#fields_names,)*}; - InstantiateProxy::<_, #(#used_generics,)* > { + InstantiateProxy::<_, #(#generics,)* > { code_id: self, funds: &[], label: "Contract", @@ -487,24 +499,24 @@ where } } - pub struct InstantiateProxy<'a, 'app, MtApp, #(#used_generics,)* > { - code_id: &'a CodeId <'app, MtApp>, - funds: &'a [#sylvia ::cw_std::Coin], - label: &'a str, + pub struct InstantiateProxy<'proxy, 'app, MtApp, #(#generics,)* > { + code_id: &'proxy CodeId <'app, MtApp, #(#generics,)* >, + funds: &'proxy [#sylvia ::cw_std::Coin], + label: &'proxy str, admin: Option, msg: InstantiateMsg #bracketed_used_generics, } - impl<'a, 'app, MtApp, #(#used_generics,)* > InstantiateProxy<'a, 'app, MtApp, #(#used_generics,)* > + impl<'proxy, 'app, MtApp, #(#generics,)* > InstantiateProxy<'proxy, 'app, MtApp, #(#generics,)* > where MtApp: Executor< #custom_msg >, - #(#where_predicates,)* + #where_predicates { - pub fn with_funds(self, funds: &'a [#sylvia ::cw_std::Coin]) -> Self { + pub fn with_funds(self, funds: &'proxy [#sylvia ::cw_std::Coin]) -> Self { Self { funds, ..self } } - pub fn with_label(self, label: &'a str) -> Self { + pub fn with_label(self, label: &'proxy str) -> Self { Self { label, ..self } } @@ -514,7 +526,7 @@ where } #[track_caller] - pub fn call(self, sender: &str) -> Result<#proxy_name<'app, MtApp>, #error_type> { + pub fn call(self, sender: &str) -> Result<#proxy_name<'app, MtApp, #(#generics,)* >, #error_type> { (*self.code_id.app) .app_mut() .instantiate_contract( @@ -529,6 +541,7 @@ where .map(|addr| #proxy_name { contract_addr: addr, app: self.code_id.app, + _phantom: std::marker::PhantomData::default(), }) } } diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs index db6e875b..8e0b4368 100644 --- a/sylvia-derive/src/parser.rs +++ b/sylvia-derive/src/parser.rs @@ -1,6 +1,6 @@ use proc_macro2::{Punct, TokenStream}; use proc_macro_error::emit_error; -use quote::{quote, ToTokens}; +use quote::quote; use syn::fold::Fold; use syn::parse::{Error, Nothing, Parse, ParseBuffer, ParseStream, Parser}; use syn::punctuated::Punctuated; @@ -145,21 +145,15 @@ impl MsgType { } } - pub fn emit_msg_name(&self, generics: &[&Generic]) -> Type - where - Generic: ToTokens, - { - let generics = if !generics.is_empty() { - quote! { ::< #(#generics,)* > } - } else { - quote! {} - }; + pub fn emit_msg_name(&self, is_wrapper: bool) -> Type { match self { - MsgType::Exec => parse_quote! { ContractExecMsg #generics }, - MsgType::Query => parse_quote! { ContractQueryMsg #generics }, - MsgType::Instantiate => parse_quote! { InstantiateMsg #generics }, - MsgType::Migrate => parse_quote! { MigrateMsg #generics }, - MsgType::Reply => parse_quote! { ReplyMsg #generics }, + MsgType::Exec if is_wrapper => parse_quote! { ContractExecMsg }, + MsgType::Query if is_wrapper => parse_quote! { ContractQueryMsg }, + MsgType::Exec => parse_quote! { ExecMsg }, + MsgType::Query => parse_quote! { QueryMsg }, + MsgType::Instantiate => parse_quote! { InstantiateMsg }, + MsgType::Migrate => parse_quote! { MigrateMsg }, + MsgType::Reply => parse_quote! { ReplyMsg }, MsgType::Sudo => todo!(), } } diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs index a386124e..5b153111 100644 --- a/sylvia/tests/generics.rs +++ b/sylvia/tests/generics.rs @@ -80,26 +80,63 @@ pub mod non_generic { } pub mod generic_contract { - use cosmwasm_std::{CustomQuery, Response, StdResult}; + use cosmwasm_std::{Reply, Response, StdResult}; use serde::de::DeserializeOwned; use serde::Deserialize; - use sylvia::types::{CustomMsg, InstantiateCtx}; + use sylvia::types::{CustomMsg, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, ReplyCtx}; use sylvia_derive::contract; - pub struct GenericContract(std::marker::PhantomData<(Msg, QueryRet)>); + pub struct GenericContract( + std::marker::PhantomData<( + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + )>, + ); #[contract] - impl GenericContract + impl + GenericContract where - for<'msg_de> Msg: CustomMsg + Deserialize<'msg_de> + 'msg_de, - for<'a> QueryRet: CustomQuery + DeserializeOwned + 'a, + for<'msg_de> InstantiateParam: CustomMsg + Deserialize<'msg_de> + 'msg_de, + for<'exec> ExecParam: CustomMsg + DeserializeOwned + 'exec, + for<'exec> QueryParam: CustomMsg + DeserializeOwned + 'exec, + for<'exec> MigrateParam: CustomMsg + DeserializeOwned + 'exec, + for<'ret> RetType: CustomMsg + DeserializeOwned + 'ret, { pub const fn new() -> Self { Self(std::marker::PhantomData) } #[msg(instantiate)] - pub fn instantiate(&self, _ctx: InstantiateCtx, _msg: Msg) -> StdResult { + pub fn instantiate( + &self, + _ctx: InstantiateCtx, + _msg: InstantiateParam, + ) -> StdResult { + Ok(Response::new()) + } + + #[msg(exec)] + pub fn execute(&self, _ctx: ExecCtx, _msg: ExecParam) -> StdResult { + Ok(Response::new()) + } + + #[msg(query)] + pub fn query(&self, _ctx: QueryCtx, _msg: QueryParam) -> StdResult { + Ok(Response::new()) + } + + #[msg(migrate)] + pub fn migrate(&self, _ctx: MigrateCtx, _msg: MigrateParam) -> StdResult { + Ok(Response::new()) + } + + #[allow(dead_code)] + #[msg(reply)] + fn reply(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult { Ok(Response::new()) } } @@ -327,18 +364,31 @@ mod tests { #[test] fn generic_contract() { + use crate::generic_contract::multitest_utils::CodeId; let app = App::default(); - let code_id = crate::generic_contract::multitest_utils::CodeId::store_code::< + let code_id: CodeId< + cw_multi_test::BasicApp, + ExternalMsg, ExternalMsg, - ExternalQuery, - >(&app); + ExternalMsg, + crate::ExternalMsg, + crate::ExternalMsg, + > = CodeId::store_code(&app); let owner = "owner"; - code_id + let contract = code_id .instantiate(ExternalMsg {}) .with_label("GenericContract") + .with_admin(owner) .call(owner) .unwrap(); + + contract.execute(ExternalMsg).call(owner).unwrap(); + contract.query(ExternalMsg).unwrap(); + contract + .migrate(ExternalMsg) + .call(owner, code_id.code_id()) + .unwrap(); } } From a1bdc2a6201f3f3831b77aeb1167138905b18a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Mon, 23 Oct 2023 13:38:54 +0200 Subject: [PATCH 10/15] feat: Move `messages` method out of EnumMsg --- .github/workflows/ci.yml | 26 ++ examples/Cargo.lock | 58 +++ examples/Cargo.toml | 28 +- .../contracts/generic_contract/.cargo/config | 6 + .../contracts/generic_contract/Cargo.toml | 34 ++ .../generic_contract/src/bin/schema.rs | 13 + .../generic_contract/src/contract.rs | 114 +++++ .../src/custom_and_generic.rs | 77 ++++ .../contracts/generic_contract/src/cw1.rs | 70 ++++ .../contracts/generic_contract/src/generic.rs | 80 ++++ .../contracts/generic_contract/src/lib.rs | 4 + .../generic_iface_on_contract/.cargo/config | 6 + .../generic_iface_on_contract/Cargo.toml | 34 ++ .../src/bin/schema.rs | 12 + .../generic_iface_on_contract/src/contract.rs | 81 ++++ .../src/custom_and_generic.rs | 31 ++ .../generic_iface_on_contract/src/cw1.rs | 26 ++ .../generic_iface_on_contract/src/generic.rs | 35 ++ .../generic_iface_on_contract/src/lib.rs | 4 + .../interfaces/custom-and-generic/Cargo.toml | 22 + .../interfaces/custom-and-generic/src/lib.rs | 69 +++ examples/interfaces/cw4/src/lib.rs | 4 +- examples/interfaces/generic/Cargo.toml | 22 + examples/interfaces/generic/src/lib.rs | 61 +++ sylvia-derive/src/interfaces.rs | 31 +- sylvia-derive/src/message.rs | 32 +- sylvia-derive/src/multitest.rs | 14 +- sylvia-derive/src/parser.rs | 14 +- sylvia/src/types.rs | 6 + sylvia/tests/generics.rs | 394 ------------------ 30 files changed, 952 insertions(+), 456 deletions(-) create mode 100644 examples/contracts/generic_contract/.cargo/config create mode 100644 examples/contracts/generic_contract/Cargo.toml create mode 100644 examples/contracts/generic_contract/src/bin/schema.rs create mode 100644 examples/contracts/generic_contract/src/contract.rs create mode 100644 examples/contracts/generic_contract/src/custom_and_generic.rs create mode 100644 examples/contracts/generic_contract/src/cw1.rs create mode 100644 examples/contracts/generic_contract/src/generic.rs create mode 100644 examples/contracts/generic_contract/src/lib.rs create mode 100644 examples/contracts/generic_iface_on_contract/.cargo/config create mode 100644 examples/contracts/generic_iface_on_contract/Cargo.toml create mode 100644 examples/contracts/generic_iface_on_contract/src/bin/schema.rs create mode 100644 examples/contracts/generic_iface_on_contract/src/contract.rs create mode 100644 examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs create mode 100644 examples/contracts/generic_iface_on_contract/src/cw1.rs create mode 100644 examples/contracts/generic_iface_on_contract/src/generic.rs create mode 100644 examples/contracts/generic_iface_on_contract/src/lib.rs create mode 100644 examples/interfaces/custom-and-generic/Cargo.toml create mode 100644 examples/interfaces/custom-and-generic/src/lib.rs create mode 100644 examples/interfaces/generic/Cargo.toml create mode 100644 examples/interfaces/generic/src/lib.rs delete mode 100644 sylvia/tests/generics.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31b3ba7e..8eb303fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,6 +66,7 @@ jobs: run: cargo clippy --all-targets -- -D warnings - name: Fmt check project run: cargo fmt --check + - name: Test examples working-directory: examples run: cargo test --locked @@ -75,6 +76,7 @@ jobs: - name: Fmt check examples working-directory: examples run: cargo fmt --check + - name: Build cw20-base example working-directory: examples/contracts/cw20-base run: cargo build --release --target wasm32-unknown-unknown --locked --lib @@ -90,10 +92,18 @@ jobs: - name: Build custom working-directory: examples/contracts/custom run: cargo build --release --target wasm32-unknown-unknown --locked --lib + - name: Build generic_contract + working-directory: examples/contracts/generic_contract + run: cargo build --release --target wasm32-unknown-unknown --locked --lib + - name: Build generic_iface_on_contract + working-directory: examples/contracts/generic_iface_on_contract + run: cargo build --release --target wasm32-unknown-unknown --locked --lib + - name: Install cosmwasm-check run: cargo install cosmwasm-check --force - name: Check contracts run: find examples/target/wasm32-unknown-unknown/release/ -type f -name "*.wasm" -exec cosmwasm-check {} \; + - name: Cw1-whitelist schema working-directory: examples/contracts/cw1-whitelist/ run: cargo schema @@ -109,6 +119,13 @@ jobs: - name: Custom schema working-directory: examples/contracts/custom run: cargo schema + - name: Generic_contract schema + working-directory: examples/contracts/generic_contract + run: cargo schema + - name: generic_iface_on_contract schema + working-directory: examples/contracts/generic_iface_on_contract + run: cargo schema + - name: Cw1-whitelist ts-codegen working-directory: examples/contracts/cw1-whitelist/ run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name cw1-whitelist --no-bundle @@ -124,6 +141,13 @@ jobs: - name: Custom ts-codegen working-directory: examples/contracts/custom/ run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name custom --no-bundle + - name: Generic_contract ts-codegen + working-directory: examples/contracts/generic_contract/ + run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name custom --no-bundle + - name: Generic_iface_on_contract ts-codegen + working-directory: examples/contracts/generic_iface_on_contract + run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name custom --no-bundle + - name: Archive schema artifats uses: actions/upload-artifact@v3 with: @@ -134,6 +158,8 @@ jobs: examples/contracts/cw20-base/schema/cw20-base.json examples/contracts/entry-points-overriding/schema/entry-points-overriding.json examples/contracts/custom/schema/custom.json + examples/contracts/generic_contract/schema/generic_contract.json + examples/contracts/generic_iface_on_contract/schema/generic_iface_on_contract.json coverage: name: Code coverage diff --git a/examples/Cargo.lock b/examples/Cargo.lock index ae267fe5..74e1dbe3 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -242,6 +242,18 @@ dependencies = [ "sylvia", ] +[[package]] +name = "custom-and-generic" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "serde", + "sylvia", +] + [[package]] name = "cw-multi-test" version = "0.16.5" @@ -550,6 +562,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "generic" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "serde", + "sylvia", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -560,6 +584,40 @@ dependencies = [ "version_check", ] +[[package]] +name = "generic_contract" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "custom-and-generic", + "cw-multi-test", + "cw-storage-plus", + "cw-utils", + "cw1", + "generic", + "serde", + "sylvia", +] + +[[package]] +name = "generic_iface_on_contract" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "custom-and-generic", + "cw-multi-test", + "cw-storage-plus", + "cw-utils", + "cw1", + "generic", + "serde", + "sylvia", +] + [[package]] name = "getrandom" version = "0.2.10" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index a8a0e3ad..f5b26b33 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,18 +1,22 @@ [workspace] members = [ - # Contract intefaces - "interfaces/cw1", - "interfaces/cw4", - "interfaces/cw20-allowances", - "interfaces/cw20-minting", - "interfaces/cw20-marketing", + # Contract intefaces + "interfaces/cw1", + "interfaces/cw4", + "interfaces/cw20-allowances", + "interfaces/cw20-minting", + "interfaces/cw20-marketing", + "interfaces/custom-and-generic", + "interfaces/generic", - # Contracts - "contracts/cw1-whitelist", - "contracts/cw1-subkeys", - "contracts/cw20-base", - "contracts/entry-points-overriding", - "contracts/custom", + # Contracts + "contracts/cw1-whitelist", + "contracts/cw1-subkeys", + "contracts/cw20-base", + "contracts/entry-points-overriding", + "contracts/custom", + "contracts/generic_contract", + "contracts/generic_iface_on_contract", ] resolver = "2" diff --git a/examples/contracts/generic_contract/.cargo/config b/examples/contracts/generic_contract/.cargo/config new file mode 100644 index 00000000..d8ab80fe --- /dev/null +++ b/examples/contracts/generic_contract/.cargo/config @@ -0,0 +1,6 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown --lib" +wasm-debug = "build --target wasm32-unknown-unknown --lib" +unit-test = "test --lib" +integration-test = "test --test integration" +schema = "run --bin schema" diff --git a/examples/contracts/generic_contract/Cargo.toml b/examples/contracts/generic_contract/Cargo.toml new file mode 100644 index 00000000..af6f57dd --- /dev/null +++ b/examples/contracts/generic_contract/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "generic_contract" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Example of generic contract" +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +library = [] +tests = ["library", "cw-multi-test", "anyhow"] + +[dependencies] +anyhow = { version = "1.0", optional = true } +cosmwasm-schema = "1.2" +cosmwasm-std = { version = "1.3", features = ["staking"] } +cw-multi-test = { version = "0.16", optional = true } +cw-storage-plus = "1.0" +cw-utils = "1.0" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } +cw1 = { path = "../../interfaces/cw1" } +generic = { path = "../../interfaces/generic" } +custom-and-generic = { path = "../../interfaces/custom-and-generic" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" +sylvia = { path = "../../../sylvia", features = ["mt"] } diff --git a/examples/contracts/generic_contract/src/bin/schema.rs b/examples/contracts/generic_contract/src/bin/schema.rs new file mode 100644 index 00000000..f84d1bb3 --- /dev/null +++ b/examples/contracts/generic_contract/src/bin/schema.rs @@ -0,0 +1,13 @@ +use cosmwasm_schema::write_api; + +#[cfg(not(tarpaulin_include))] +fn main() { + use generic_contract::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; + use sylvia::types::SvCustomMsg; + + write_api! { + instantiate: InstantiateMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, + } +} diff --git a/examples/contracts/generic_contract/src/contract.rs b/examples/contracts/generic_contract/src/contract.rs new file mode 100644 index 00000000..e253e06e --- /dev/null +++ b/examples/contracts/generic_contract/src/contract.rs @@ -0,0 +1,114 @@ +use cosmwasm_std::{Reply, Response, StdResult}; +use serde::de::DeserializeOwned; +use serde::Deserialize; +use sylvia::types::{ + CustomMsg, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, ReplyCtx, SvCustomMsg, +}; +use sylvia::{contract, schemars}; + +pub struct GenericContract( + std::marker::PhantomData<( + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + )>, +); + +#[contract] +#[messages(cw1 as Cw1: custom(msg))] +#[messages(generic as Generic: custom(msg))] +#[messages(custom_and_generic as CustomAndGeneric)] +#[sv::custom(msg=SvCustomMsg)] +impl + GenericContract +where + for<'msg_de> InstantiateParam: CustomMsg + Deserialize<'msg_de> + 'msg_de, + ExecParam: CustomMsg + DeserializeOwned + 'static, + QueryParam: CustomMsg + DeserializeOwned + 'static, + MigrateParam: CustomMsg + DeserializeOwned + 'static, + RetType: CustomMsg + DeserializeOwned + 'static, +{ + pub const fn new() -> Self { + Self(std::marker::PhantomData) + } + + #[msg(instantiate)] + pub fn instantiate( + &self, + _ctx: InstantiateCtx, + _msg: InstantiateParam, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(exec)] + pub fn contract_execute( + &self, + _ctx: ExecCtx, + _msg: ExecParam, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(query)] + pub fn contract_query( + &self, + _ctx: QueryCtx, + _msg: QueryParam, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(migrate)] + pub fn migrate( + &self, + _ctx: MigrateCtx, + _msg: MigrateParam, + ) -> StdResult> { + Ok(Response::new()) + } + + #[allow(dead_code)] + #[msg(reply)] + fn reply(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult> { + Ok(Response::new()) + } +} + +#[cfg(test)] +mod tests { + use super::multitest_utils::CodeId; + use sylvia::multitest::App; + use sylvia::types::SvCustomMsg; + + #[test] + fn generic_contract() { + let app = App::>::custom(|_, _, _| {}); + let code_id: CodeId< + SvCustomMsg, + SvCustomMsg, + SvCustomMsg, + super::SvCustomMsg, + super::SvCustomMsg, + _, + > = CodeId::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate(SvCustomMsg {}) + .with_label("GenericContract") + .with_admin(owner) + .call(owner) + .unwrap(); + + contract.contract_execute(SvCustomMsg).call(owner).unwrap(); + contract.contract_query(SvCustomMsg).unwrap(); + contract + .migrate(SvCustomMsg) + .call(owner, code_id.code_id()) + .unwrap(); + } +} diff --git a/examples/contracts/generic_contract/src/custom_and_generic.rs b/examples/contracts/generic_contract/src/custom_and_generic.rs new file mode 100644 index 00000000..98926abc --- /dev/null +++ b/examples/contracts/generic_contract/src/custom_and_generic.rs @@ -0,0 +1,77 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use custom_and_generic::CustomAndGeneric; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; + +#[contract(module = crate::contract)] +#[messages(custom_and_generic as CustomAndGeneric)] +#[sv::custom(msg=sylvia::types::SvCustomMsg)] +impl + CustomAndGeneric + for crate::contract::GenericContract< + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + > +{ + type Error = StdError; + + #[msg(exec)] + fn custom_generic_execute( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(query)] + fn custom_generic_query( + &self, + _ctx: QueryCtx, + _msg: sylvia::types::SvCustomMsg, + ) -> StdResult { + Ok(SvCustomMsg {}) + } +} + +#[cfg(test)] +mod tests { + use super::test_utils::CustomAndGeneric; + use crate::contract::multitest_utils::CodeId; + use sylvia::{multitest::App, types::SvCustomMsg}; + + #[test] + fn proxy_methods() { + let app = App::>::custom(|_, _, _| {}); + let code_id = CodeId::< + SvCustomMsg, + sylvia::types::SvCustomMsg, + SvCustomMsg, + SvCustomMsg, + sylvia::types::SvCustomMsg, + _, + >::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate(SvCustomMsg {}) + .with_label("GenericContract") + .with_admin(owner) + .call(owner) + .unwrap(); + + contract + .custom_and_generic_proxy() + .custom_generic_execute(vec![]) + .call(owner) + .unwrap(); + contract + .custom_and_generic_proxy() + .custom_generic_query(SvCustomMsg {}) + .unwrap(); + } +} diff --git a/examples/contracts/generic_contract/src/cw1.rs b/examples/contracts/generic_contract/src/cw1.rs new file mode 100644 index 00000000..48bd0870 --- /dev/null +++ b/examples/contracts/generic_contract/src/cw1.rs @@ -0,0 +1,70 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use cw1::{CanExecuteResp, Cw1}; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx}; + +#[contract(module = crate::contract)] +#[messages(cw1 as Cw1)] +#[sv::custom(msg=sylvia::types::SvCustomMsg)] +impl Cw1 + for crate::contract::GenericContract< + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + > +{ + type Error = StdError; + + #[msg(exec)] + fn execute(&self, _ctx: ExecCtx, _msgs: Vec) -> StdResult { + Ok(Response::new()) + } + + #[msg(query)] + fn can_execute( + &self, + _ctx: QueryCtx, + _sender: String, + _msg: CosmosMsg, + ) -> StdResult { + Ok(CanExecuteResp::default()) + } +} + +#[cfg(test)] +mod tests { + use super::test_utils::Cw1; + use crate::contract::multitest_utils::CodeId; + use cosmwasm_std::{CosmosMsg, Empty}; + use sylvia::{multitest::App, types::SvCustomMsg}; + + #[test] + fn proxy_methods() { + let app = App::>::custom(|_, _, _| {}); + let code_id = CodeId::< + SvCustomMsg, + sylvia::types::SvCustomMsg, + SvCustomMsg, + SvCustomMsg, + sylvia::types::SvCustomMsg, + _, + >::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate(SvCustomMsg {}) + .with_label("GenericContract") + .with_admin(owner) + .call(owner) + .unwrap(); + + contract.cw1_proxy().execute(vec![]).call(owner).unwrap(); + contract + .cw1_proxy() + .can_execute("sender".to_owned(), CosmosMsg::Custom(Empty {})) + .unwrap(); + } +} diff --git a/examples/contracts/generic_contract/src/generic.rs b/examples/contracts/generic_contract/src/generic.rs new file mode 100644 index 00000000..aef1ac96 --- /dev/null +++ b/examples/contracts/generic_contract/src/generic.rs @@ -0,0 +1,80 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use generic::Generic; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; + +#[contract(module = crate::contract)] +#[messages(generic as Generic)] +#[sv::custom(msg=SvCustomMsg)] +impl + Generic + for crate::contract::GenericContract< + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + > +{ + type Error = StdError; + + #[msg(exec)] + fn generic_exec( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> StdResult { + Ok(Response::new()) + } + + // Sylvia will fail if single type is used to match against two different generics + // It's because we have to map unique generics used as they can be used multiple times. + // If for some reason like here one type would be used in place of two generics either full + // path or some alias has to be used. + #[msg(query)] + fn generic_query( + &self, + _ctx: QueryCtx, + _msg: sylvia::types::SvCustomMsg, + ) -> StdResult { + Ok(SvCustomMsg {}) + } +} + +#[cfg(test)] +mod tests { + use super::test_utils::Generic; + use crate::contract::multitest_utils::CodeId; + use cosmwasm_std::CosmosMsg; + use sylvia::multitest::App; + use sylvia::types::SvCustomMsg; + + #[test] + fn proxy_methods() { + let app = App::>::custom(|_, _, _| {}); + let code_id: CodeId< + SvCustomMsg, + sylvia::types::SvCustomMsg, + SvCustomMsg, + SvCustomMsg, + sylvia::types::SvCustomMsg, + _, + > = CodeId::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate(SvCustomMsg {}) + .with_label("GenericContract") + .with_admin(owner) + .call(owner) + .unwrap(); + + contract + .generic_proxy() + .generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]) + .call(owner) + .unwrap(); + contract.generic_proxy().generic_query(SvCustomMsg).unwrap(); + } +} diff --git a/examples/contracts/generic_contract/src/lib.rs b/examples/contracts/generic_contract/src/lib.rs new file mode 100644 index 00000000..ea8b3738 --- /dev/null +++ b/examples/contracts/generic_contract/src/lib.rs @@ -0,0 +1,4 @@ +pub mod contract; +pub mod custom_and_generic; +pub mod cw1; +pub mod generic; diff --git a/examples/contracts/generic_iface_on_contract/.cargo/config b/examples/contracts/generic_iface_on_contract/.cargo/config new file mode 100644 index 00000000..d8ab80fe --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/.cargo/config @@ -0,0 +1,6 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown --lib" +wasm-debug = "build --target wasm32-unknown-unknown --lib" +unit-test = "test --lib" +integration-test = "test --test integration" +schema = "run --bin schema" diff --git a/examples/contracts/generic_iface_on_contract/Cargo.toml b/examples/contracts/generic_iface_on_contract/Cargo.toml new file mode 100644 index 00000000..99ad5aa1 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "generic_iface_on_contract" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Generic interfaces implemented on non generic contract" +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +library = [] +tests = ["library", "cw-multi-test", "anyhow"] + +[dependencies] +anyhow = { version = "1.0", optional = true } +cosmwasm-schema = "1.2" +cosmwasm-std = { version = "1.3", features = ["staking"] } +cw-multi-test = { version = "0.16", optional = true } +cw-storage-plus = "1.0" +cw-utils = "1.0" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } +cw1 = { path = "../../interfaces/cw1" } +generic = { path = "../../interfaces/generic" } +custom-and-generic = { path = "../../interfaces/custom-and-generic" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" +sylvia = { path = "../../../sylvia", features = ["mt"] } diff --git a/examples/contracts/generic_iface_on_contract/src/bin/schema.rs b/examples/contracts/generic_iface_on_contract/src/bin/schema.rs new file mode 100644 index 00000000..8a7c48e5 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/bin/schema.rs @@ -0,0 +1,12 @@ +use cosmwasm_schema::write_api; + +#[cfg(not(tarpaulin_include))] +fn main() { + use generic_iface_on_contract::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; + + write_api! { + instantiate: InstantiateMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/contract.rs b/examples/contracts/generic_iface_on_contract/src/contract.rs new file mode 100644 index 00000000..f35d0d69 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/contract.rs @@ -0,0 +1,81 @@ +use cosmwasm_std::{Response, StdResult}; +use sylvia::types::{InstantiateCtx, SvCustomMsg}; +use sylvia::{contract, schemars}; + +pub struct NonGenericContract; + +#[contract] +#[messages(generic as Generic: custom(msg))] +#[messages(custom_and_generic as CustomAndGeneric)] +#[messages(cw1 as Cw1: custom(msg))] +/// Required if interface returns generic `Response` +#[sv::custom(msg=SvCustomMsg)] +impl NonGenericContract { + pub const fn new() -> Self { + Self + } + + #[msg(instantiate)] + pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult> { + Ok(Response::new()) + } +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::{CosmosMsg, Empty}; + use sylvia::{multitest::App, types::SvCustomMsg}; + + use super::NonGenericContract; + use crate::custom_and_generic::test_utils::CustomAndGeneric; + use crate::cw1::test_utils::Cw1; + use crate::generic::test_utils::Generic; + + #[test] + fn mt_helpers() { + let _ = NonGenericContract::new(); + let app = App::>::custom(|_, _, _| {}); + let code_id = super::multitest_utils::CodeId::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate() + .with_label("Cw1Contract") + .call(owner) + .unwrap(); + + // Non custom non generic interface + contract + .cw1_proxy() + .can_execute("sender".to_owned(), CosmosMsg::Custom(Empty {})) + .unwrap(); + contract + .cw1_proxy() + .execute(vec![CosmosMsg::Custom(Empty {})]) + .call(owner) + .unwrap(); + + // Non-Custom generic Interface + contract + .generic_proxy() + .generic_query(SvCustomMsg {}) + .unwrap(); + contract + .generic_proxy() + .generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]) + .call(owner) + .unwrap(); + + // Custom generic Interface + contract + .custom_and_generic_proxy() + .custom_generic_query(SvCustomMsg {}) + .unwrap(); + contract + .custom_and_generic_proxy() + .custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]) + .call(owner) + .unwrap(); + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs b/examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs new file mode 100644 index 00000000..90052b86 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs @@ -0,0 +1,31 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use custom_and_generic::CustomAndGeneric; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; + +#[contract(module = crate::contract)] +#[messages(custom_and_generic as CustomAndGeneric)] +#[sv::custom(msg=sylvia::types::SvCustomMsg)] +impl CustomAndGeneric + for crate::contract::NonGenericContract +{ + type Error = StdError; + + #[msg(exec)] + fn custom_generic_execute( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(query)] + fn custom_generic_query( + &self, + _ctx: QueryCtx, + _msg: sylvia::types::SvCustomMsg, + ) -> StdResult { + Ok(SvCustomMsg {}) + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/cw1.rs b/examples/contracts/generic_iface_on_contract/src/cw1.rs new file mode 100644 index 00000000..8d45bc0d --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/cw1.rs @@ -0,0 +1,26 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use cw1::{CanExecuteResp, Cw1}; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx}; + +#[contract(module = crate::contract)] +#[messages(cw1 as Cw1)] +#[sv::custom(msg=sylvia::types::SvCustomMsg)] +impl Cw1 for crate::contract::NonGenericContract { + type Error = StdError; + + #[msg(exec)] + fn execute(&self, _ctx: ExecCtx, _msgs: Vec) -> StdResult { + Ok(Response::new()) + } + + #[msg(query)] + fn can_execute( + &self, + _ctx: QueryCtx, + _sender: String, + _msg: CosmosMsg, + ) -> StdResult { + Ok(CanExecuteResp::default()) + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/generic.rs b/examples/contracts/generic_iface_on_contract/src/generic.rs new file mode 100644 index 00000000..d13827a0 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/generic.rs @@ -0,0 +1,35 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use generic::Generic; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; + +#[contract(module = crate::contract)] +#[messages(generic as Generic)] +#[sv::custom(msg = SvCustomMsg)] +impl Generic + for crate::contract::NonGenericContract +{ + type Error = StdError; + + #[msg(exec)] + fn generic_exec( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> StdResult { + Ok(Response::new()) + } + + // Sylvia will fail if single type is used to match against two different generics + // It's because we have to map unique generics used as they can be used multiple times. + // If for some reason like here one type would be used in place of two generics either full + // path or some alias has to be used. + #[msg(query)] + fn generic_query( + &self, + _ctx: QueryCtx, + _msg: sylvia::types::SvCustomMsg, + ) -> StdResult { + Ok(SvCustomMsg {}) + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/lib.rs b/examples/contracts/generic_iface_on_contract/src/lib.rs new file mode 100644 index 00000000..ea8b3738 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/lib.rs @@ -0,0 +1,4 @@ +pub mod contract; +pub mod custom_and_generic; +pub mod cw1; +pub mod generic; diff --git a/examples/interfaces/custom-and-generic/Cargo.toml b/examples/interfaces/custom-and-generic/Cargo.toml new file mode 100644 index 00000000..c1f38f99 --- /dev/null +++ b/examples/interfaces/custom-and-generic/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "custom-and-generic" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Interface with custom msg and generic support." +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[features] +mt = ["sylvia/mt"] + +[dependencies] +cosmwasm-std = { version = "1.3", features = ["staking"] } +cosmwasm-schema = "1.2" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" diff --git a/examples/interfaces/custom-and-generic/src/lib.rs b/examples/interfaces/custom-and-generic/src/lib.rs new file mode 100644 index 00000000..134eb77e --- /dev/null +++ b/examples/interfaces/custom-and-generic/src/lib.rs @@ -0,0 +1,69 @@ +use cosmwasm_std::{CosmosMsg, CustomMsg, Response, StdError}; + +use serde::de::DeserializeOwned; +use serde::Deserialize; +use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::{interface, schemars}; + +#[interface] +#[sv::custom(msg=RetType)] +pub trait CustomAndGeneric +where + for<'msg_de> ExecParam: CustomMsg + Deserialize<'msg_de>, + QueryParam: sylvia::types::CustomMsg, + RetType: CustomMsg + DeserializeOwned, +{ + type Error: From; + + #[msg(exec)] + fn custom_generic_execute( + &self, + ctx: ExecCtx, + msgs: Vec>, + ) -> Result, Self::Error>; + + #[msg(query)] + fn custom_generic_query( + &self, + ctx: QueryCtx, + param: QueryParam, + ) -> Result; +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::{Addr, CosmosMsg, Empty, QuerierWrapper}; + use sylvia::types::{InterfaceMessages, SvCustomMsg}; + + use crate::Querier; + + #[test] + fn construct_messages() { + let contract = Addr::unchecked("contract"); + + // Direct message construction + let _ = super::QueryMsg::<_, Empty>::custom_generic_query(SvCustomMsg {}); + let _ = super::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]); + let _ = super::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]); + + // Querier + let deps = mock_dependencies(); + let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier); + + let querier = super::BoundQuerier::borrowed(&contract, &querier_wrapper); + let _: Result = + super::Querier::custom_generic_query(&querier, SvCustomMsg {}); + let _: Result = querier.custom_generic_query(SvCustomMsg {}); + + // Construct messages with Interface extension + let _ = + as InterfaceMessages>::Query::custom_generic_query( + SvCustomMsg {}, + ); + let _= + as InterfaceMessages>::Exec::custom_generic_execute( + vec![ CosmosMsg::Custom(SvCustomMsg{}), + ]); + } +} diff --git a/examples/interfaces/cw4/src/lib.rs b/examples/interfaces/cw4/src/lib.rs index cc730746..4b27dcde 100644 --- a/examples/interfaces/cw4/src/lib.rs +++ b/examples/interfaces/cw4/src/lib.rs @@ -84,7 +84,7 @@ mod tests { #[test] fn exec_msgs() { assert_eq!( - Cw4ExecMsg::messages(), + execute_messages(), ["add_hook", "remove_hook", "update_admin", "update_members"] ); } @@ -92,7 +92,7 @@ mod tests { #[test] fn query_msgs() { assert_eq!( - Cw4QueryMsg::messages(), + query_messages(), ["admin", "hooks", "list_members", "member", "total_weight"] ); } diff --git a/examples/interfaces/generic/Cargo.toml b/examples/interfaces/generic/Cargo.toml new file mode 100644 index 00000000..c5832b18 --- /dev/null +++ b/examples/interfaces/generic/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "generic" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Interface with generic support." +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[features] +mt = ["sylvia/mt"] + +[dependencies] +cosmwasm-std = { version = "1.3", features = ["staking"] } +cosmwasm-schema = "1.2" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" diff --git a/examples/interfaces/generic/src/lib.rs b/examples/interfaces/generic/src/lib.rs new file mode 100644 index 00000000..042db389 --- /dev/null +++ b/examples/interfaces/generic/src/lib.rs @@ -0,0 +1,61 @@ +use cosmwasm_std::{CosmosMsg, CustomMsg, Response, StdError}; + +use serde::{de::DeserializeOwned, Deserialize}; +use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::{interface, schemars}; + +#[interface] +pub trait Generic +where + for<'msg_de> ExecParam: CustomMsg + Deserialize<'msg_de>, + QueryParam: sylvia::types::CustomMsg, + RetType: CustomMsg + DeserializeOwned, +{ + type Error: From; + + #[msg(exec)] + fn generic_exec( + &self, + ctx: ExecCtx, + msgs: Vec>, + ) -> Result; + + #[msg(query)] + fn generic_query(&self, ctx: QueryCtx, param: QueryParam) -> Result; +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::{testing::mock_dependencies, Addr, CosmosMsg, Empty, QuerierWrapper}; + use sylvia::types::{InterfaceMessages, SvCustomMsg}; + + use crate::Querier; + + #[test] + fn construct_messages() { + let contract = Addr::unchecked("contract"); + + // Direct message construction + let _ = super::QueryMsg::<_, Empty>::generic_query(SvCustomMsg {}); + let _ = super::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]); + let _ = super::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]); + + // Querier + let deps = mock_dependencies(); + let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier); + + let querier = super::BoundQuerier::borrowed(&contract, &querier_wrapper); + let _: Result = super::Querier::generic_query(&querier, SvCustomMsg {}); + let _: Result = querier.generic_query(SvCustomMsg {}); + + // Construct messages with Interface extension + let _ = + as InterfaceMessages>::Query::generic_query( + SvCustomMsg {}, + ); + let _= + as InterfaceMessages>::Exec::generic_exec(vec![ + CosmosMsg::Custom(SvCustomMsg{}), + ]); + } +} diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs index 09559c70..877edebf 100644 --- a/sylvia-derive/src/interfaces.rs +++ b/sylvia-derive/src/interfaces.rs @@ -106,45 +106,32 @@ impl Interfaces { } pub fn emit_messages_call(&self, msg_ty: &MsgType) -> Vec { - let sylvia = crate_module(); - self.interfaces .iter() .map(|interface| { - let ContractMessageAttr { - module, generics, .. - } = interface; - let generics = if !generics.is_empty() { - quote! { < #generics > } - } else { - quote! {} - }; - let type_name = msg_ty.as_accessor_name(); + let ContractMessageAttr { module, .. } = interface; + + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), module.span()); quote! { - &<#module :: InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> :: #type_name :: messages() + &#module :: #messages_fn_name() } }) .collect() } pub fn emit_deserialization_attempts(&self, msg_ty: &MsgType) -> Vec { - let sylvia = crate_module(); - self.interfaces .iter() .map(|interface| { let ContractMessageAttr { - module, variant, generics, .. + module, variant, .. } = interface; - let generics = if !generics.is_empty() { - quote! { < #generics > } - } else { - quote! {} - }; + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), module.span()); - let type_name = msg_ty.as_accessor_name(); quote! { - let msgs = &<#module :: InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> :: #type_name :: messages(); + let msgs = &#module :: #messages_fn_name(); if msgs.into_iter().any(|msg| msg == &recv_msg_name) { match val.deserialize_into() { Ok(msg) => return Ok(Self:: #variant (msg)), diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index a067a7c3..4af19680 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -293,6 +293,9 @@ impl<'a> EnumMessage<'a> { let unique_enum_name = Ident::new(&format!("{}{}", trait_name, name), name.span()); + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), name.span()); + #[cfg(not(tarpaulin_include))] let enum_declaration = match name.to_string().as_str() { "QueryMsg" => { @@ -336,11 +339,12 @@ impl<'a> EnumMessage<'a> { #match_arms } } - pub const fn messages() -> [&'static str; #msgs_cnt] { - [#(#msgs,)*] - } #(#variants_constructors)* } + + pub const fn #messages_fn_name () -> [&'static str; #msgs_cnt] { + [#(#msgs,)*] + } } } } @@ -411,6 +415,9 @@ impl<'a> ContractEnumMessage<'a> { _ => quote! {}, }; + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span()); + #[cfg(not(tarpaulin_include))] { quote! { @@ -429,12 +436,13 @@ impl<'a> ContractEnumMessage<'a> { #(#match_arms,)* } } - pub const fn messages() -> [&'static str; #variants_cnt] { - [#(#variant_names,)*] - } #(#variants_constructors)* } + + pub const fn #messages_fn_name () -> [&'static str; #variants_cnt] { + [#(#variant_names,)*] + } } } } @@ -1265,6 +1273,8 @@ impl<'a> GlueMessage<'a> { let variants = interfaces.emit_glue_message_variants(msg_ty); + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span()); let contract_variant = quote! { #contract_name ( #enum_name #bracketed_used_generics ) }; let mut messages_call = interfaces.emit_messages_call(msg_ty); let prefixed_used_generics = if !used_generics.is_empty() { @@ -1272,7 +1282,7 @@ impl<'a> GlueMessage<'a> { } else { quote! {} }; - messages_call.push(quote! { &#enum_name #prefixed_used_generics :: messages() }); + messages_call.push(quote! { &#messages_fn_name() }); let variants_cnt = messages_call.len(); @@ -1310,7 +1320,7 @@ impl<'a> GlueMessage<'a> { #[cfg(not(tarpaulin_include))] let contract_deserialization_attempt = quote! { - let msgs = &#enum_name #prefixed_used_generics :: messages(); + let msgs = &#messages_fn_name(); if msgs.into_iter().any(|msg| msg == &recv_msg_name) { match val.deserialize_into() { Ok(msg) => return Ok(Self:: #contract_name (msg)), @@ -1363,12 +1373,10 @@ impl<'a> GlueMessage<'a> { contract: &#contract, ctx: #ctx_type, ) -> #ret_type #full_where_clause { - const fn assert_no_intersection #bracketed_used_generics () #where_clause { + const _: () = { let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*]; #sylvia ::utils::assert_no_intersection(msgs); - } - - assert_no_intersection #prefixed_used_generics (); + }; match self { #(#dispatch_arms,)* diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs index f958cd5c..b92cac65 100644 --- a/sylvia-derive/src/multitest.rs +++ b/sylvia-derive/src/multitest.rs @@ -455,14 +455,14 @@ where quote! { #impl_contract - pub struct CodeId<'app, MtApp, #(#generics,)* > { + pub struct CodeId<'app, #(#generics,)* MtApp> { code_id: u64, app: &'app #sylvia ::multitest::App, _phantom: std::marker::PhantomData<( #(#generics,)* )>, } - impl<'app, BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > CodeId<'app, #mt_app, #(#generics,)* > + impl<'app, BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > CodeId<'app, #(#generics,)* #mt_app > where BankT: #sylvia ::cw_multi_test::Bank, ApiT: #sylvia ::cw_std::Api, @@ -487,9 +487,9 @@ where pub fn instantiate( &self,#(#fields,)* - ) -> InstantiateProxy<'_, 'app, #mt_app, #(#generics,)* > { + ) -> InstantiateProxy<'_, 'app, #(#generics,)* #mt_app > { let msg = #instantiate_msg {#(#fields_names,)*}; - InstantiateProxy::<_, #(#generics,)* > { + InstantiateProxy::< #(#generics,)* _> { code_id: self, funds: &[], label: "Contract", @@ -499,15 +499,15 @@ where } } - pub struct InstantiateProxy<'proxy, 'app, MtApp, #(#generics,)* > { - code_id: &'proxy CodeId <'app, MtApp, #(#generics,)* >, + pub struct InstantiateProxy<'proxy, 'app, #(#generics,)* MtApp> { + code_id: &'proxy CodeId <'app, #(#generics,)* MtApp>, funds: &'proxy [#sylvia ::cw_std::Coin], label: &'proxy str, admin: Option, msg: InstantiateMsg #bracketed_used_generics, } - impl<'proxy, 'app, MtApp, #(#generics,)* > InstantiateProxy<'proxy, 'app, MtApp, #(#generics,)* > + impl<'proxy, 'app, #(#generics,)* MtApp> InstantiateProxy<'proxy, 'app, #(#generics,)* MtApp> where MtApp: Executor< #custom_msg >, #where_predicates diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs index 8e0b4368..709ed404 100644 --- a/sylvia-derive/src/parser.rs +++ b/sylvia-derive/src/parser.rs @@ -105,16 +105,16 @@ impl MsgType { } #[cfg(not(tarpaulin_include))] - pub fn emit_ep_name(self) -> TokenStream { + pub fn emit_ep_name(self) -> Ident { use MsgType::*; match self { - Exec => quote! { execute }, - Instantiate => quote! { instantiate }, - Migrate => quote! { migrate }, - Sudo => quote! { sudo }, - Reply => quote! { reply }, - Query => quote! { query }, + Exec => parse_quote! { execute }, + Instantiate => parse_quote! { instantiate }, + Migrate => parse_quote! { migrate }, + Sudo => parse_quote! { sudo }, + Reply => parse_quote! { reply }, + Query => parse_quote! { query }, } } diff --git a/sylvia/src/types.rs b/sylvia/src/types.rs index c70ed7b3..059ede46 100644 --- a/sylvia/src/types.rs +++ b/sylvia/src/types.rs @@ -1,3 +1,4 @@ +use cosmwasm_schema::cw_serde; use cosmwasm_std::{CustomQuery, Deps, DepsMut, Empty, Env, MessageInfo}; use serde::de::DeserializeOwned; @@ -99,6 +100,11 @@ pub trait CustomMsg: cosmwasm_std::CustomMsg + DeserializeOwned {} impl CustomMsg for T where T: cosmwasm_std::CustomMsg + DeserializeOwned {} +#[cw_serde] +pub struct SvCustomMsg; + +impl cosmwasm_std::CustomMsg for SvCustomMsg {} + pub trait InterfaceMessages { type Exec; type Query; diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs deleted file mode 100644 index 5b153111..00000000 --- a/sylvia/tests/generics.rs +++ /dev/null @@ -1,394 +0,0 @@ -use cosmwasm_schema::cw_serde; - -pub mod cw1 { - use cosmwasm_std::{CosmosMsg, CustomMsg, CustomQuery, Response, StdError}; - - use serde::{de::DeserializeOwned, Deserialize}; - use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia_derive::interface; - - #[interface(module=msg)] - #[sv::custom(msg=Msg)] - pub trait Cw1 - where - for<'msg_de> Msg: CustomMsg + Deserialize<'msg_de>, - Param: sylvia::types::CustomMsg, - QueryRet: CustomQuery + DeserializeOwned, - { - type Error: From; - - #[msg(exec)] - fn execute( - &self, - ctx: ExecCtx, - msgs: Vec>, - ) -> Result, Self::Error>; - - #[msg(query)] - fn some_query(&self, ctx: QueryCtx, param: Param) -> Result; - } -} - -pub mod whitelist { - use cosmwasm_std::{CosmosMsg, CustomMsg, CustomQuery, Response, StdError}; - - use serde::de::DeserializeOwned; - use serde::Deserialize; - use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia_derive::interface; - - #[interface(module=msg)] - pub trait Whitelist - where - for<'msg_de> Msg: CustomMsg + Deserialize<'msg_de>, - QueryRet: CustomQuery + DeserializeOwned, - { - type Error: From; - - #[msg(exec)] - fn update_admins( - &self, - ctx: ExecCtx, - msgs: Vec>, - ) -> Result; - - #[msg(query)] - fn admins_list(&self, ctx: QueryCtx) -> Result; - } -} - -pub mod non_generic { - use cosmwasm_std::{CosmosMsg, Empty, Response, StdError}; - - use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia_derive::interface; - - #[interface(module=msg)] - pub trait NonGeneric { - type Error: From; - - #[msg(exec)] - fn non_generic_exec( - &self, - ctx: ExecCtx, - msgs: Vec>, - ) -> Result; - - #[msg(query)] - fn non_generic_query(&self, ctx: QueryCtx) -> Result; - } -} - -pub mod generic_contract { - use cosmwasm_std::{Reply, Response, StdResult}; - use serde::de::DeserializeOwned; - use serde::Deserialize; - use sylvia::types::{CustomMsg, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, ReplyCtx}; - use sylvia_derive::contract; - - pub struct GenericContract( - std::marker::PhantomData<( - InstantiateParam, - ExecParam, - QueryParam, - MigrateParam, - RetType, - )>, - ); - - #[contract] - impl - GenericContract - where - for<'msg_de> InstantiateParam: CustomMsg + Deserialize<'msg_de> + 'msg_de, - for<'exec> ExecParam: CustomMsg + DeserializeOwned + 'exec, - for<'exec> QueryParam: CustomMsg + DeserializeOwned + 'exec, - for<'exec> MigrateParam: CustomMsg + DeserializeOwned + 'exec, - for<'ret> RetType: CustomMsg + DeserializeOwned + 'ret, - { - pub const fn new() -> Self { - Self(std::marker::PhantomData) - } - - #[msg(instantiate)] - pub fn instantiate( - &self, - _ctx: InstantiateCtx, - _msg: InstantiateParam, - ) -> StdResult { - Ok(Response::new()) - } - - #[msg(exec)] - pub fn execute(&self, _ctx: ExecCtx, _msg: ExecParam) -> StdResult { - Ok(Response::new()) - } - - #[msg(query)] - pub fn query(&self, _ctx: QueryCtx, _msg: QueryParam) -> StdResult { - Ok(Response::new()) - } - - #[msg(migrate)] - pub fn migrate(&self, _ctx: MigrateCtx, _msg: MigrateParam) -> StdResult { - Ok(Response::new()) - } - - #[allow(dead_code)] - #[msg(reply)] - fn reply(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult { - Ok(Response::new()) - } - } -} - -pub mod cw1_contract { - use cosmwasm_std::{Response, StdResult}; - use sylvia::types::InstantiateCtx; - use sylvia_derive::contract; - - use crate::{ExternalMsg, ExternalQuery}; - - pub struct Cw1Contract; - - #[contract] - #[messages(crate::cw1 as Cw1)] - #[messages(crate::whitelist as Whitelist: custom(msg))] - #[messages(crate::non_generic as NonGeneric: custom(msg))] - /// Required if interface returns generic `Response` - #[sv::custom(msg=ExternalMsg)] - impl Cw1Contract { - pub const fn new() -> Self { - Self - } - - #[msg(instantiate)] - pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult> { - Ok(Response::new()) - } - } -} - -pub mod impl_non_generic { - use crate::cw1_contract::Cw1Contract; - use crate::non_generic::NonGeneric; - use cosmwasm_std::{CosmosMsg, Empty, Response, StdError}; - use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia_derive::contract; - - #[contract(module = crate::cw1_contract)] - #[messages(crate::non_generic as NonGeneric)] - #[sv::custom(msg=crate::ExternalMsg)] - impl NonGeneric for Cw1Contract { - type Error = StdError; - - #[msg(exec)] - fn non_generic_exec( - &self, - _ctx: ExecCtx, - _msgs: Vec>, - ) -> Result { - Ok(Response::new()) - } - - #[msg(query)] - fn non_generic_query(&self, _ctx: QueryCtx) -> Result { - Ok(Response::default()) - } - } -} - -pub mod impl_whitelist { - use cosmwasm_std::{CosmosMsg, Response, StdError}; - use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia_derive::contract; - - use crate::cw1_contract::Cw1Contract; - use crate::whitelist::Whitelist; - use crate::{ExternalMsg, ExternalQuery}; - - #[contract(module = crate::cw1_contract)] - #[messages(crate::whitelist as Whitelist)] - #[sv::custom(msg=ExternalMsg)] - impl Whitelist for Cw1Contract { - type Error = StdError; - - #[msg(exec)] - fn update_admins( - &self, - _ctx: ExecCtx, - _msgs: Vec>, - ) -> Result { - Ok(Response::new()) - } - - #[msg(query)] - fn admins_list(&self, _ctx: QueryCtx) -> Result { - Ok(ExternalQuery {}) - } - } -} - -pub mod impl_cw1 { - use cosmwasm_std::{CosmosMsg, Response, StdError}; - use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia_derive::contract; - - use crate::{cw1::Cw1, cw1_contract::Cw1Contract, ExternalMsg}; - - #[contract(module = crate::cw1_contract)] - #[messages(crate::cw1 as Cw1)] - #[sv::custom(msg=ExternalMsg)] - impl Cw1 for Cw1Contract { - type Error = StdError; - - #[msg(exec)] - fn execute( - &self, - _ctx: ExecCtx, - _msgs: Vec>, - ) -> Result, Self::Error> { - Ok(Response::new()) - } - - #[msg(query)] - fn some_query( - &self, - _ctx: QueryCtx, - _param: crate::ExternalMsg, - ) -> Result { - Ok(crate::ExternalQuery {}) - } - } -} - -#[cw_serde] -pub struct ExternalMsg; -impl cosmwasm_std::CustomMsg for ExternalMsg {} - -#[cw_serde] -pub struct ExternalQuery; -impl cosmwasm_std::CustomQuery for ExternalQuery {} - -#[cfg(all(test, feature = "mt"))] -mod tests { - use crate::cw1::{InterfaceTypes, Querier as Cw1Querier}; - use crate::cw1_contract::Cw1Contract; - use crate::impl_cw1::test_utils::Cw1; - use crate::impl_non_generic::test_utils::NonGeneric; - use crate::impl_whitelist::test_utils::Whitelist; - use crate::non_generic::Querier as NonGenericQuerier; - use crate::whitelist::Querier as WhitelistQuerier; - use crate::{ExternalMsg, ExternalQuery}; - use cosmwasm_std::{testing::mock_dependencies, Addr, CosmosMsg, Empty, QuerierWrapper}; - use sylvia::multitest::App; - use sylvia::types::InterfaceMessages; - - #[test] - fn construct_messages() { - let contract = Addr::unchecked("contract"); - - // Direct message construction - // cw1 - let _ = crate::cw1::QueryMsg::<_, Empty>::some_query(ExternalMsg {}); - let _ = crate::cw1::ExecMsg::execute(vec![CosmosMsg::Custom(ExternalMsg {})]); - let _ = crate::cw1::ExecMsg::execute(vec![CosmosMsg::Custom(Empty {})]); - - // whitelist - let _ = crate::whitelist::QueryMsg::::admins_list(); - let _ = crate::whitelist::ExecMsg::update_admins(vec![CosmosMsg::Custom(ExternalMsg {})]); - - // non_generic - let _ = crate::non_generic::QueryMsg::non_generic_query(); - let _ = crate::non_generic::ExecMsg::non_generic_exec(vec![]); - - // Generic Querier - let deps = mock_dependencies(); - let querier: QuerierWrapper = QuerierWrapper::new(&deps.querier); - - let cw1_querier = crate::cw1::BoundQuerier::borrowed(&contract, &querier); - let _: Result = - crate::cw1::Querier::some_query(&cw1_querier, ExternalMsg {}); - let _: Result = cw1_querier.some_query(ExternalMsg {}); - - let contract_querier = crate::cw1_contract::BoundQuerier::borrowed(&contract, &querier); - let _: Result = contract_querier.some_query(ExternalMsg {}); - let _: Result = contract_querier.admins_list(); - let _ = contract_querier.non_generic_query(); - - // Construct messages with Interface extension - let _ = - as InterfaceMessages>::Query::some_query( - ExternalMsg {}, - ); - let _= - as InterfaceMessages>::Exec::execute(vec![ - CosmosMsg::Custom(ExternalMsg {}), - ]); - } - - #[test] - fn mt_helpers() { - let _ = Cw1Contract::new(); - let app = App::>::custom(|_, _, _| {}); - let code_id = crate::cw1_contract::multitest_utils::CodeId::store_code(&app); - - let owner = "owner"; - - let contract = code_id - .instantiate() - .with_label("Cw1Contract") - .call(owner) - .unwrap(); - - // CustomMsg generic Interface - contract.cw1_proxy().some_query(ExternalMsg {}).unwrap(); - contract.cw1_proxy().execute(vec![]).call(owner).unwrap(); - - // Non-CustomMsg generic Interface - contract.whitelist_proxy().admins_list().unwrap(); - contract - .whitelist_proxy() - .update_admins(vec![]) - .call(owner) - .unwrap(); - - // Non-CustomMsg non-generic Interface - contract.non_generic_proxy().non_generic_query().unwrap(); - contract - .non_generic_proxy() - .non_generic_exec(vec![]) - .call(owner) - .unwrap(); - } - - #[test] - fn generic_contract() { - use crate::generic_contract::multitest_utils::CodeId; - let app = App::default(); - let code_id: CodeId< - cw_multi_test::BasicApp, - ExternalMsg, - ExternalMsg, - ExternalMsg, - crate::ExternalMsg, - crate::ExternalMsg, - > = CodeId::store_code(&app); - - let owner = "owner"; - - let contract = code_id - .instantiate(ExternalMsg {}) - .with_label("GenericContract") - .with_admin(owner) - .call(owner) - .unwrap(); - - contract.execute(ExternalMsg).call(owner).unwrap(); - contract.query(ExternalMsg).unwrap(); - contract - .migrate(ExternalMsg) - .call(owner, code_id.code_id()) - .unwrap(); - } -} From 3b606003ad9e428044cda9389036f556fe18ec39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 25 Oct 2023 10:10:59 +0200 Subject: [PATCH 11/15] feat: Hide generated interface types in `sv` module --- examples/Cargo.lock | 14 ++++ examples/Cargo.toml | 1 + examples/contracts/cw1-subkeys/Cargo.toml | 1 + .../contracts/cw1-subkeys/src/contract.rs | 1 - .../contracts/cw1-subkeys/src/whitelist.rs | 4 +- examples/contracts/cw1-whitelist/Cargo.toml | 1 + .../contracts/cw1-whitelist/src/contract.rs | 25 +++--- examples/contracts/cw1-whitelist/src/lib.rs | 1 - .../contracts/cw1-whitelist/src/multitest.rs | 5 +- .../contracts/cw1-whitelist/src/whitelist.rs | 20 +---- .../interfaces/custom-and-generic/src/lib.rs | 16 ++-- examples/interfaces/cw1/src/lib.rs | 10 +-- examples/interfaces/cw4/src/lib.rs | 2 +- examples/interfaces/generic/src/lib.rs | 16 ++-- examples/interfaces/whitelist/Cargo.toml | 22 ++++++ examples/interfaces/whitelist/src/lib.rs | 76 +++++++++++++++++++ .../whitelist}/src/responses.rs | 0 sylvia-derive/src/input.rs | 13 ++-- sylvia-derive/src/interfaces.rs | 14 ++-- sylvia-derive/src/message.rs | 13 +++- sylvia-derive/src/multitest.rs | 10 ++- sylvia-derive/src/remote.rs | 4 +- sylvia/examples/basic.rs | 19 +++-- sylvia/tests/custom_msg.rs | 55 +++++++++++--- sylvia/tests/custom_query.rs | 59 +++++++++++--- sylvia/tests/dispatching.rs | 10 +-- sylvia/tests/messages_generation.rs | 16 ++-- sylvia/tests/querier.rs | 11 +-- sylvia/tests/query_returns.rs | 2 +- sylvia/tests/remote.rs | 8 +- 30 files changed, 315 insertions(+), 134 deletions(-) create mode 100644 examples/interfaces/whitelist/Cargo.toml create mode 100644 examples/interfaces/whitelist/src/lib.rs rename examples/{contracts/cw1-whitelist => interfaces/whitelist}/src/responses.rs (100%) diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 74e1dbe3..a606c727 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -328,6 +328,7 @@ dependencies = [ "serde", "sylvia", "thiserror", + "whitelist", ] [[package]] @@ -346,6 +347,7 @@ dependencies = [ "serde", "sylvia", "thiserror", + "whitelist", ] [[package]] @@ -1218,6 +1220,18 @@ version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +[[package]] +name = "whitelist" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "serde", + "sylvia", +] + [[package]] name = "winnow" version = "0.4.6" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index f5b26b33..0becc451 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -2,6 +2,7 @@ members = [ # Contract intefaces "interfaces/cw1", + "interfaces/whitelist", "interfaces/cw4", "interfaces/cw20-allowances", "interfaces/cw20-minting", diff --git a/examples/contracts/cw1-subkeys/Cargo.toml b/examples/contracts/cw1-subkeys/Cargo.toml index 12dc5f70..41f8b087 100644 --- a/examples/contracts/cw1-subkeys/Cargo.toml +++ b/examples/contracts/cw1-subkeys/Cargo.toml @@ -18,6 +18,7 @@ cw-multi-test = { version = "0.16.2", optional = true } cw-storage-plus = "1.0" cw-utils = "1.0" cw1 = { path = "../../interfaces/cw1" } +whitelist = { path = "../../interfaces/whitelist" } cw1-whitelist = { path = "../cw1-whitelist", features = ["library"] } cw2 = "1.0" getrandom = { version = "0.2.8", features = ["js"] } diff --git a/examples/contracts/cw1-subkeys/src/contract.rs b/examples/contracts/cw1-subkeys/src/contract.rs index cd86a338..5bd9b49e 100644 --- a/examples/contracts/cw1-subkeys/src/contract.rs +++ b/examples/contracts/cw1-subkeys/src/contract.rs @@ -3,7 +3,6 @@ use cosmwasm_std::{ StakingMsg, StdResult, }; use cw1_whitelist::contract::Cw1WhitelistContract; -use cw1_whitelist::whitelist; use cw2::set_contract_version; use cw_storage_plus::{Bound, Map}; use cw_utils::Expiration; diff --git a/examples/contracts/cw1-subkeys/src/whitelist.rs b/examples/contracts/cw1-subkeys/src/whitelist.rs index c36556a4..eb29e84c 100644 --- a/examples/contracts/cw1-subkeys/src/whitelist.rs +++ b/examples/contracts/cw1-subkeys/src/whitelist.rs @@ -1,8 +1,8 @@ use cosmwasm_std::{Response, StdResult}; -use cw1_whitelist::responses::AdminListResponse; -use cw1_whitelist::whitelist::{self, Whitelist}; use sylvia::contract; use sylvia::types::{ExecCtx, QueryCtx}; +use whitelist::responses::AdminListResponse; +use whitelist::Whitelist; use crate::contract::Cw1SubkeysContract; use crate::error::ContractError; diff --git a/examples/contracts/cw1-whitelist/Cargo.toml b/examples/contracts/cw1-whitelist/Cargo.toml index c307894e..6718fbec 100644 --- a/examples/contracts/cw1-whitelist/Cargo.toml +++ b/examples/contracts/cw1-whitelist/Cargo.toml @@ -22,6 +22,7 @@ cosmwasm-schema = "1.2" serde = { version = "1.0", default-features = false, features = ["derive"] } sylvia = { path = "../../../sylvia" } cw1 = { path = "../../interfaces/cw1" } +whitelist = { path = "../../interfaces/whitelist" } cw-storage-plus = "1.0" thiserror = { version = "1.0" } cw2 = "1.0" diff --git a/examples/contracts/cw1-whitelist/src/contract.rs b/examples/contracts/cw1-whitelist/src/contract.rs index f27bdd82..9f5ae735 100644 --- a/examples/contracts/cw1-whitelist/src/contract.rs +++ b/examples/contracts/cw1-whitelist/src/contract.rs @@ -1,5 +1,4 @@ use crate::error::ContractError; -use crate::whitelist; use cosmwasm_std::{Addr, Deps, Empty, Response}; use cw2::set_contract_version; @@ -57,11 +56,11 @@ impl Cw1WhitelistContract<'_> { #[cfg(test)] mod tests { use super::*; - use crate::responses::AdminListResponse; - use crate::whitelist::{self, Whitelist}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{coin, coins, to_binary, BankMsg, CosmosMsg, StakingMsg, SubMsg, WasmMsg}; use cw1::Cw1; + use whitelist::responses::AdminListResponse; + use whitelist::Whitelist; #[test] fn instantiate_and_modify_config() { @@ -182,7 +181,7 @@ mod tests { ) .unwrap(); - let freeze = whitelist::ExecMsg::Freeze {}; + let freeze = whitelist::sv::ExecMsg::Freeze {}; let msgs = vec![ BankMsg::Send { to_address: bob.to_string(), @@ -289,15 +288,13 @@ mod tests { } mod msgs { - use super::*; - use cosmwasm_std::{from_binary, from_slice, to_binary, BankMsg}; use crate::contract::{ContractExecMsg, ContractQueryMsg}; #[test] fn freeze() { - let original = whitelist::ExecMsg::Freeze {}; + let original = whitelist::sv::ExecMsg::Freeze {}; let serialized = to_binary(&original).unwrap(); let deserialized = from_binary(&serialized).unwrap(); @@ -309,14 +306,14 @@ mod tests { let deserialized = from_slice(json).unwrap(); assert_eq!( - ContractExecMsg::Whitelist(whitelist::ExecMsg::Freeze {}), + ContractExecMsg::Whitelist(whitelist::sv::ExecMsg::Freeze {}), deserialized ); } #[test] fn update_admins() { - let original = whitelist::ExecMsg::UpdateAdmins { + let original = whitelist::sv::ExecMsg::UpdateAdmins { admins: vec!["admin1".to_owned(), "admin2".to_owned()], }; let serialized = to_binary(&original).unwrap(); @@ -332,7 +329,7 @@ mod tests { let deserialized = from_slice(json).unwrap(); assert_eq!( - ContractExecMsg::Whitelist(whitelist::ExecMsg::UpdateAdmins { + ContractExecMsg::Whitelist(whitelist::sv::ExecMsg::UpdateAdmins { admins: vec!["admin1".to_owned(), "admin3".to_owned()] }), deserialized @@ -341,7 +338,7 @@ mod tests { #[test] fn admin_list() { - let original = whitelist::QueryMsg::AdminList {}; + let original = whitelist::sv::QueryMsg::AdminList {}; let serialized = to_binary(&original).unwrap(); let deserialized = from_binary(&serialized).unwrap(); @@ -353,14 +350,14 @@ mod tests { let deserialized = from_slice(json).unwrap(); assert_eq!( - ContractQueryMsg::Whitelist(whitelist::QueryMsg::AdminList {}), + ContractQueryMsg::Whitelist(whitelist::sv::QueryMsg::AdminList {}), deserialized ); } #[test] fn execute() { - let original = cw1::ExecMsg::Execute { + let original = cw1::sv::ExecMsg::Execute { msgs: vec![BankMsg::Send { to_address: "admin1".to_owned(), amount: vec![], @@ -374,7 +371,7 @@ mod tests { #[test] fn can_execute() { - let original = cw1::QueryMsg::CanExecute { + let original = cw1::sv::QueryMsg::CanExecute { sender: "admin".to_owned(), msg: BankMsg::Send { to_address: "admin1".to_owned(), diff --git a/examples/contracts/cw1-whitelist/src/lib.rs b/examples/contracts/cw1-whitelist/src/lib.rs index cc023ca4..52af57a5 100644 --- a/examples/contracts/cw1-whitelist/src/lib.rs +++ b/examples/contracts/cw1-whitelist/src/lib.rs @@ -3,5 +3,4 @@ mod cw1; pub mod error; #[cfg(any(test, feature = "tests"))] pub mod multitest; -pub mod responses; pub mod whitelist; diff --git a/examples/contracts/cw1-whitelist/src/multitest.rs b/examples/contracts/cw1-whitelist/src/multitest.rs index 9859b6cf..c45e7c04 100644 --- a/examples/contracts/cw1-whitelist/src/multitest.rs +++ b/examples/contracts/cw1-whitelist/src/multitest.rs @@ -1,12 +1,11 @@ #[cfg(test)] mod test { use cosmwasm_std::{to_binary, WasmMsg}; + use whitelist::responses::AdminListResponse; use crate::contract::multitest_utils::CodeId; use crate::cw1::test_utils::Cw1; use crate::error::ContractError; - use crate::responses::AdminListResponse; - use crate::whitelist; use crate::whitelist::test_utils::Whitelist; use assert_matches::assert_matches; use sylvia::multitest::App; @@ -30,7 +29,7 @@ mod test { .call(owner) .unwrap(); - let freeze = whitelist::ExecMsg::Freeze {}; + let freeze = whitelist::sv::ExecMsg::Freeze {}; let freeze = WasmMsg::Execute { contract_addr: second_contract.contract_addr.to_string(), msg: to_binary(&freeze).unwrap(), diff --git a/examples/contracts/cw1-whitelist/src/whitelist.rs b/examples/contracts/cw1-whitelist/src/whitelist.rs index 68e2a79e..156a4af8 100644 --- a/examples/contracts/cw1-whitelist/src/whitelist.rs +++ b/examples/contracts/cw1-whitelist/src/whitelist.rs @@ -1,26 +1,14 @@ use cosmwasm_std::{Empty, Order, Response, StdResult}; +use sylvia::contract; use sylvia::types::{ExecCtx, QueryCtx}; -use sylvia::{contract, interface, schemars}; +use whitelist::responses::AdminListResponse; +use whitelist::Whitelist; use crate::contract::Cw1WhitelistContract; use crate::error::ContractError; -use crate::responses::AdminListResponse; - -#[interface] -pub trait Whitelist { - type Error: From; - - #[msg(exec)] - fn freeze(&self, ctx: ExecCtx) -> Result; - - #[msg(exec)] - fn update_admins(&self, ctx: ExecCtx, admins: Vec) -> Result; - - #[msg(query)] - fn admin_list(&self, ctx: QueryCtx) -> StdResult; -} #[contract(module=crate::contract)] +#[messages(whitelist as Whitelist)] impl Whitelist for Cw1WhitelistContract<'_> { type Error = ContractError; diff --git a/examples/interfaces/custom-and-generic/src/lib.rs b/examples/interfaces/custom-and-generic/src/lib.rs index 134eb77e..80eb0cfd 100644 --- a/examples/interfaces/custom-and-generic/src/lib.rs +++ b/examples/interfaces/custom-and-generic/src/lib.rs @@ -36,33 +36,33 @@ mod tests { use cosmwasm_std::{Addr, CosmosMsg, Empty, QuerierWrapper}; use sylvia::types::{InterfaceMessages, SvCustomMsg}; - use crate::Querier; + use crate::sv::Querier; #[test] fn construct_messages() { let contract = Addr::unchecked("contract"); // Direct message construction - let _ = super::QueryMsg::<_, Empty>::custom_generic_query(SvCustomMsg {}); - let _ = super::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]); - let _ = super::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]); + let _ = super::sv::QueryMsg::<_, Empty>::custom_generic_query(SvCustomMsg {}); + let _ = super::sv::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]); + let _ = super::sv::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]); // Querier let deps = mock_dependencies(); let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier); - let querier = super::BoundQuerier::borrowed(&contract, &querier_wrapper); + let querier = super::sv::BoundQuerier::borrowed(&contract, &querier_wrapper); let _: Result = - super::Querier::custom_generic_query(&querier, SvCustomMsg {}); + super::sv::Querier::custom_generic_query(&querier, SvCustomMsg {}); let _: Result = querier.custom_generic_query(SvCustomMsg {}); // Construct messages with Interface extension let _ = - as InterfaceMessages>::Query::custom_generic_query( + as InterfaceMessages>::Query::custom_generic_query( SvCustomMsg {}, ); let _= - as InterfaceMessages>::Exec::custom_generic_execute( + as InterfaceMessages>::Exec::custom_generic_execute( vec![ CosmosMsg::Custom(SvCustomMsg{}), ]); } diff --git a/examples/interfaces/cw1/src/lib.rs b/examples/interfaces/cw1/src/lib.rs index 3e12c2e2..46d204b1 100644 --- a/examples/interfaces/cw1/src/lib.rs +++ b/examples/interfaces/cw1/src/lib.rs @@ -36,11 +36,9 @@ pub trait Cw1 { mod tests { use cosmwasm_std::{coins, from_binary, from_slice, to_binary, BankMsg}; - use super::*; - #[test] fn execute() { - let original = ExecMsg::Execute { + let original = super::sv::ExecMsg::Execute { msgs: vec![BankMsg::Send { to_address: "receiver".to_owned(), amount: coins(10, "atom"), @@ -57,12 +55,12 @@ mod tests { #[test] fn execute_from_slice() { let deserialized = from_slice(br#"{"execute": { "msgs": [] }}"#).unwrap(); - assert_eq!(ExecMsg::Execute { msgs: vec![] }, deserialized); + assert_eq!(super::sv::ExecMsg::Execute { msgs: vec![] }, deserialized); } #[test] fn query() { - let original = QueryMsg::CanExecute { + let original = super::sv::QueryMsg::CanExecute { sender: "sender".to_owned(), msg: BankMsg::Send { to_address: "receiver".to_owned(), @@ -99,7 +97,7 @@ mod tests { ) .unwrap(); assert_eq!( - QueryMsg::CanExecute { + super::sv::QueryMsg::CanExecute { sender: "address".to_owned(), msg: BankMsg::Send { to_address: "receiver".to_owned(), diff --git a/examples/interfaces/cw4/src/lib.rs b/examples/interfaces/cw4/src/lib.rs index 4b27dcde..0bb2551a 100644 --- a/examples/interfaces/cw4/src/lib.rs +++ b/examples/interfaces/cw4/src/lib.rs @@ -39,7 +39,7 @@ pub trait Cw4 { mod tests { use cosmwasm_std::{from_binary, from_slice, to_binary}; - use super::*; + use super::sv::*; #[test] fn execute() { diff --git a/examples/interfaces/generic/src/lib.rs b/examples/interfaces/generic/src/lib.rs index 042db389..827067ea 100644 --- a/examples/interfaces/generic/src/lib.rs +++ b/examples/interfaces/generic/src/lib.rs @@ -29,32 +29,32 @@ mod tests { use cosmwasm_std::{testing::mock_dependencies, Addr, CosmosMsg, Empty, QuerierWrapper}; use sylvia::types::{InterfaceMessages, SvCustomMsg}; - use crate::Querier; + use crate::sv::Querier; #[test] fn construct_messages() { let contract = Addr::unchecked("contract"); // Direct message construction - let _ = super::QueryMsg::<_, Empty>::generic_query(SvCustomMsg {}); - let _ = super::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]); - let _ = super::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]); + let _ = super::sv::QueryMsg::<_, Empty>::generic_query(SvCustomMsg {}); + let _ = super::sv::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]); + let _ = super::sv::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]); // Querier let deps = mock_dependencies(); let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier); - let querier = super::BoundQuerier::borrowed(&contract, &querier_wrapper); - let _: Result = super::Querier::generic_query(&querier, SvCustomMsg {}); + let querier = super::sv::BoundQuerier::borrowed(&contract, &querier_wrapper); + let _: Result = super::sv::Querier::generic_query(&querier, SvCustomMsg {}); let _: Result = querier.generic_query(SvCustomMsg {}); // Construct messages with Interface extension let _ = - as InterfaceMessages>::Query::generic_query( + as InterfaceMessages>::Query::generic_query( SvCustomMsg {}, ); let _= - as InterfaceMessages>::Exec::generic_exec(vec![ + as InterfaceMessages>::Exec::generic_exec(vec![ CosmosMsg::Custom(SvCustomMsg{}), ]); } diff --git a/examples/interfaces/whitelist/Cargo.toml b/examples/interfaces/whitelist/Cargo.toml new file mode 100644 index 00000000..5ec641e6 --- /dev/null +++ b/examples/interfaces/whitelist/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "whitelist" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Coverage of whitelist sylvia message generation." +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[features] +mt = ["sylvia/mt"] + +[dependencies] +cosmwasm-std = { version = "1.3", features = ["staking"] } +cosmwasm-schema = "1.2" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" diff --git a/examples/interfaces/whitelist/src/lib.rs b/examples/interfaces/whitelist/src/lib.rs new file mode 100644 index 00000000..34daafe2 --- /dev/null +++ b/examples/interfaces/whitelist/src/lib.rs @@ -0,0 +1,76 @@ +use cosmwasm_std::{Response, StdResult}; +use responses::AdminListResponse; +use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::{interface, schemars}; + +pub mod responses; + +#[interface] +pub trait Whitelist { + type Error: From; + + #[msg(exec)] + fn freeze(&self, ctx: ExecCtx) -> Result; + + #[msg(exec)] + fn update_admins(&self, ctx: ExecCtx, admins: Vec) -> Result; + + #[msg(query)] + fn admin_list(&self, ctx: QueryCtx) -> StdResult; +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::{from_binary, from_slice, to_binary}; + + use super::*; + + #[test] + fn exec_from_binary() { + let original = sv::ExecMsg::Freeze {}; + + let serialized = to_binary(&original).unwrap(); + let deserialized = from_binary(&serialized).unwrap(); + + assert_eq!(original, deserialized); + + let original = sv::ExecMsg::UpdateAdmins { + admins: vec!["new_admin".to_owned()], + }; + + let serialized = to_binary(&original).unwrap(); + let deserialized = from_binary(&serialized).unwrap(); + + assert_eq!(original, deserialized); + } + + #[test] + fn exec_from_slice() { + let deserialized = from_slice(br#"{"freeze": { }}"#).unwrap(); + assert_eq!(sv::ExecMsg::Freeze {}, deserialized); + + let deserialized = + from_slice(br#"{"update_admins": { "admins": ["new_admin"] }}"#).unwrap(); + assert_eq!( + sv::ExecMsg::UpdateAdmins { + admins: vec!["new_admin".to_owned()] + }, + deserialized + ); + } + + #[test] + fn query_from_binary() { + let original = sv::QueryMsg::AdminList {}; + let serialized = to_binary(&original).unwrap(); + let deserialized = from_binary(&serialized).unwrap(); + + assert_eq!(original, deserialized); + } + + #[test] + fn query_from_slice() { + let deserialized = from_slice(br#"{"admin_list": {}}"#).unwrap(); + assert_eq!(sv::QueryMsg::AdminList {}, deserialized); + } +} diff --git a/examples/contracts/cw1-whitelist/src/responses.rs b/examples/interfaces/whitelist/src/responses.rs similarity index 100% rename from examples/contracts/cw1-whitelist/src/responses.rs rename to examples/interfaces/whitelist/src/responses.rs diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 74061b64..7e0ae593 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -82,15 +82,18 @@ impl<'a> TraitInput<'a> { #[cfg(not(tarpaulin_include))] { quote! { - #messages + pub mod sv { + use super::*; + #messages - #multitest_helpers + #multitest_helpers - #remote + #remote - #querier + #querier - #interface_messages + #interface_messages + } } } } diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs index 877edebf..61455b1e 100644 --- a/sylvia-derive/src/interfaces.rs +++ b/sylvia-derive/src/interfaces.rs @@ -46,7 +46,7 @@ impl Interfaces { self.as_modules() .map(|module| { quote! { - impl<'a, C: #sylvia ::cw_std::CustomQuery> From<&'a BoundQuerier<'a, C>> for #module ::BoundQuerier<'a, C> { + impl<'a, C: #sylvia ::cw_std::CustomQuery> From<&'a BoundQuerier<'a, C>> for #module ::sv::BoundQuerier<'a, C> { fn from(querier: &'a BoundQuerier<'a, C>) -> Self { Self::borrowed(querier.contract(), querier.querier()) } @@ -68,8 +68,8 @@ impl Interfaces { ); quote! { - pub fn #method_name (&self) -> #module ::trait_utils:: #proxy_name <'app, #mt_app> { - #module ::trait_utils:: #proxy_name ::new(self.contract_addr.clone(), self.app) + pub fn #method_name (&self) -> #module ::sv::trait_utils:: #proxy_name <'app, #mt_app> { + #module ::sv::trait_utils:: #proxy_name ::new(self.contract_addr.clone(), self.app) } } }) @@ -95,7 +95,7 @@ impl Interfaces { }; let interface_enum = - quote! { <#module ::InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> }; + quote! { <#module ::sv::InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> }; if msg_ty == &MsgType::Query { quote! { #variant ( #interface_enum :: Query) } } else { @@ -114,7 +114,7 @@ impl Interfaces { let ep_name = msg_ty.emit_ep_name(); let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), module.span()); quote! { - &#module :: #messages_fn_name() + &#module ::sv:: #messages_fn_name() } }) .collect() @@ -131,7 +131,7 @@ impl Interfaces { let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), module.span()); quote! { - let msgs = &#module :: #messages_fn_name(); + let msgs = &#module ::sv:: #messages_fn_name(); if msgs.into_iter().any(|msg| msg == &recv_msg_name) { match val.deserialize_into() { Ok(msg) => return Ok(Self:: #variant (msg)), @@ -161,7 +161,7 @@ impl Interfaces { let type_name = msg_ty.as_accessor_name(); quote! { - <#module :: InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> :: #type_name :: response_schemas_impl() + <#module ::sv::InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> :: #type_name :: response_schemas_impl() } }) .collect() diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 4af19680..9c299103 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -615,8 +615,13 @@ impl<'a> MsgVariant<'a> { let parameters = fields.iter().map(MsgField::emit_method_field); let fields_names = fields.iter().map(MsgField::name); let variant_name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); + + // This method is called for `impl Contract` and `impl Trait for Contract` + // In case of first one, `trait_module` will always be `None`. + // In case of the second one, `module` on interface is not an `Option` so it will always be + // `Some` or the compilation will fail earlier. let msg = trait_module - .map(|module| quote! { #module ::QueryMsg }) + .map(|module| quote! { #module ::sv::QueryMsg }) .unwrap_or_else(|| quote! { QueryMsg }); let msg = if !unbonded_generics.is_empty() { @@ -740,7 +745,7 @@ impl<'a> MsgVariant<'a> { let params = fields.iter().map(|field| field.emit_method_field()); let arguments = fields.iter().map(MsgField::name); let bracketed_generics = emit_bracketed_generics(generics); - let interface_enum = quote! { < #module InterfaceTypes #bracketed_generics as #sylvia ::types::InterfaceMessages> }; + let interface_enum = quote! { < #module sv::InterfaceTypes #bracketed_generics as #sylvia ::types::InterfaceMessages> }; let type_name = msg_ty.as_accessor_name(); let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); @@ -970,8 +975,8 @@ where .map(|variant| variant.emit_querier_impl(trait_module, used_generics)); let querier = trait_module - .map(|module| quote! { #module ::Querier }) - .unwrap_or_else(|| quote! { Querier }); + .map(|module| quote! { #module ::sv::Querier }) + .unwrap_or_else(|| quote! { sv::Querier }); let bound_querier = contract_module .map(|module| quote! { #module ::BoundQuerier}) .unwrap_or_else(|| quote! { BoundQuerier }); diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs index b92cac65..3ea3ad8e 100644 --- a/sylvia-derive/src/multitest.rs +++ b/sylvia-derive/src/multitest.rs @@ -17,9 +17,11 @@ fn interface_name(source: &ItemImpl) -> &Ident { unreachable!() }; let (_, Path { segments, .. }, _) = &trait_name; - assert!(!segments.is_empty()); - &segments[0].ident + match segments.last() { + Some(segment) => &segment.ident, + None => unreachable!(), + } } fn extract_contract_name(contract: &Type) -> &Ident { @@ -321,7 +323,7 @@ where }; let bracketed_generics = emit_bracketed_generics(generics); - let interface_enum = quote! { < #module InterfaceTypes #bracketed_generics as #sylvia ::types::InterfaceMessages> }; + let interface_enum = quote! { < #module sv::InterfaceTypes #bracketed_generics as #sylvia ::types::InterfaceMessages> }; let exec_methods = exec_variants.emit_interface_multitest_proxy_methods( &custom_msg, @@ -356,7 +358,7 @@ where #(#exec_methods_declarations)* } - impl #trait_name< #mt_app > for #module trait_utils:: #proxy_name<'_, #mt_app > + impl #trait_name< #mt_app > for #module sv::trait_utils:: #proxy_name<'_, #mt_app > where CustomT: #sylvia ::cw_multi_test::Module, WasmT: #sylvia ::cw_multi_test::Wasm, diff --git a/sylvia-derive/src/remote.rs b/sylvia-derive/src/remote.rs index 4d673f1f..bfe63420 100644 --- a/sylvia-derive/src/remote.rs +++ b/sylvia-derive/src/remote.rs @@ -21,9 +21,9 @@ impl<'a> Remote<'a> { let ContractMessageAttr { module, .. } = interface; quote! { - impl<'a> From<&'a Remote<'a>> for #module ::Remote<'a> { + impl<'a> From<&'a Remote<'a>> for #module ::sv::Remote<'a> { fn from(remote: &'a Remote) -> Self { - #module ::Remote::borrowed(remote.as_ref()) + #module ::sv::Remote::borrowed(remote.as_ref()) } } } diff --git a/sylvia/examples/basic.rs b/sylvia/examples/basic.rs index 07f04d7a..7feb0b98 100644 --- a/sylvia/examples/basic.rs +++ b/sylvia/examples/basic.rs @@ -32,12 +32,11 @@ pub struct MemberResp { } mod group { - use anyhow::Error; use cosmwasm_std::{Response, StdError}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; - use crate::{GroupContract, Member, MemberResp}; + use crate::{Member, MemberResp}; #[interface] pub trait Group { @@ -61,9 +60,19 @@ mod group { #[msg(query)] fn member(&self, ctx: QueryCtx, addr: String) -> Result; } +} + +mod impl_group { + use anyhow::Error; + use cosmwasm_std::Response; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; + + use crate::{GroupContract, MemberResp}; #[contract(module=super)] - impl Group for GroupContract { + #[messages(crate::group as Group)] + impl crate::group::Group for GroupContract { type Error = Error; #[msg(exec)] @@ -80,7 +89,7 @@ mod group { &self, _ctx: ExecCtx, _remove: Vec, - _add: Vec, + _add: Vec, ) -> Result { todo!() } diff --git a/sylvia/tests/custom_msg.rs b/sylvia/tests/custom_msg.rs index 9365ba44..2896b593 100644 --- a/sylvia/tests/custom_msg.rs +++ b/sylvia/tests/custom_msg.rs @@ -21,8 +21,8 @@ pub struct SomeResponse; mod some_interface { use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; use crate::{MyMsg, SomeResponse}; @@ -39,8 +39,18 @@ mod some_interface { #[msg(exec)] fn interface_exec(&self, ctx: ExecCtx) -> StdResult>; } +} + +mod impl_some_interface { + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::{ExecCtx, QueryCtx}; + + use crate::some_interface::SomeInterface; + use crate::{MyMsg, SomeResponse}; #[contract(module=super)] + #[messages(crate::some_interface as SomeInterface)] #[sv::custom(msg=MyMsg)] impl SomeInterface for crate::MyContract { type Error = StdError; @@ -59,10 +69,10 @@ mod some_interface { // Use `#[sv::custom(..)]` if both it and associated type defined mod interface { - use crate::{MyMsg, OtherMsg}; + use crate::MyMsg; use cosmwasm_std::{CustomMsg, Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::ExecCtx; - use sylvia::{contract, interface}; #[interface] #[sv::custom(msg=MyMsg)] @@ -74,8 +84,16 @@ mod interface { #[msg(exec)] fn exec(&self, ctx: ExecCtx) -> StdResult>; } +} +mod impl_interface { + use crate::interface::Interface; + use crate::{MyMsg, OtherMsg}; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::ExecCtx; #[contract(module=super)] + #[messages(crate::interface as Interface)] #[sv::custom(msg=MyMsg)] impl Interface for crate::MyContract { type Error = StdError; @@ -90,8 +108,8 @@ mod interface { mod other_interface { use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::ExecCtx; - use sylvia::{contract, interface}; #[interface] pub trait OtherInterface { @@ -101,8 +119,15 @@ mod other_interface { #[msg(exec)] fn other_interface_exec(&self, ctx: ExecCtx) -> StdResult; } +} +mod impl_other_interface { + use crate::other_interface::OtherInterface; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::ExecCtx; #[contract(module=super)] + #[messages(crate::other_interface as OtherInterface)] #[sv::custom(msg=crate::MyMsg)] impl OtherInterface for crate::MyContract { type Error = StdError; @@ -115,10 +140,9 @@ mod other_interface { } mod associated_interface { - use crate::MyMsg; use cosmwasm_std::{CustomMsg, Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::ExecCtx; - use sylvia::{contract, interface}; #[interface] pub trait AssociatedInterface { @@ -129,8 +153,16 @@ mod associated_interface { #[msg(exec)] fn associated_exec(&self, ctx: ExecCtx) -> StdResult>; } +} +mod impl_associated_interface { + use crate::associated_interface::AssociatedInterface; + use crate::MyMsg; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::ExecCtx; #[contract(module=super)] + #[messages(crate::associated_interface as AssociatedInterface)] #[sv::custom(msg=MyMsg)] impl AssociatedInterface for crate::MyContract { type Error = StdError; @@ -179,14 +211,13 @@ impl MyContract { #[cfg(all(test, feature = "mt"))] mod tests { - use crate::interface::test_utils::Interface; + use crate::impl_associated_interface::test_utils::AssociatedInterface; + use crate::impl_interface::test_utils::Interface; + use crate::impl_other_interface::test_utils::OtherInterface; + use crate::impl_some_interface::test_utils::SomeInterface; use crate::MyContract; - use sylvia::multitest::App; - - use crate::associated_interface::test_utils::AssociatedInterface; - use crate::other_interface::test_utils::OtherInterface; - use crate::some_interface::test_utils::SomeInterface; use crate::MyMsg; + use sylvia::multitest::App; #[test] fn test_custom() { diff --git a/sylvia/tests/custom_query.rs b/sylvia/tests/custom_query.rs index cb92e811..8405f873 100644 --- a/sylvia/tests/custom_query.rs +++ b/sylvia/tests/custom_query.rs @@ -20,10 +20,10 @@ pub struct SomeResponse; mod interface { use cosmwasm_std::{CustomQuery, Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; - use crate::{MyQuery, OtherQuery, SomeResponse}; + use crate::{MyQuery, SomeResponse}; #[interface] #[sv::custom(query=MyQuery)] @@ -39,10 +39,19 @@ mod interface { #[msg(exec)] fn interface_exec(&self, ctx: ExecCtx) -> StdResult; } +} + +mod impl_interface { + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; + + use crate::{MyQuery, OtherQuery, SomeResponse}; #[contract(module=super)] + #[messages(crate::interface as Interface)] #[sv::custom(query=MyQuery)] - impl Interface for crate::MyContract { + impl crate::interface::Interface for crate::MyContract { type Error = StdError; type QueryC = OtherQuery; @@ -60,8 +69,8 @@ mod interface { mod some_interface { use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; use crate::{MyQuery, SomeResponse}; @@ -78,10 +87,19 @@ mod some_interface { #[msg(exec)] fn some_interface_exec(&self, ctx: ExecCtx) -> StdResult; } +} + +mod impl_some_interface { + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; + + use crate::{MyQuery, SomeResponse}; #[contract(module=super)] + #[messages(crate::some_interface as SomeInterface)] #[sv::custom(query=MyQuery)] - impl SomeInterface for crate::MyContract { + impl super::some_interface::SomeInterface for crate::MyContract { type Error = StdError; #[msg(query)] @@ -98,10 +116,10 @@ mod some_interface { mod associated_type_interface { use cosmwasm_std::{CustomQuery, Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; - use crate::{MyQuery, SomeResponse}; + use crate::SomeResponse; #[interface] pub trait AssociatedTypeInterface { @@ -116,8 +134,16 @@ mod associated_type_interface { #[msg(exec)] fn associated_exec(&self, ctx: ExecCtx) -> StdResult; } +} + +mod impl_associated_type_interface { + use crate::{associated_type_interface::AssociatedTypeInterface, MyQuery, SomeResponse}; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; #[contract(module=super)] + #[messages(crate::associated_type_interface as AssociatedTypeInterface)] impl AssociatedTypeInterface for crate::MyContract { type Error = StdError; type QueryC = MyQuery; @@ -136,8 +162,8 @@ mod associated_type_interface { mod default_query_interface { use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; use crate::SomeResponse; @@ -153,8 +179,16 @@ mod default_query_interface { #[msg(exec)] fn default_exec(&self, ctx: ExecCtx) -> StdResult; } +} + +mod impl_default_query_interface { + use crate::{default_query_interface::DefaultQueryInterface, SomeResponse}; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; #[contract(module=super)] + #[messages(crate::default_query_interface as DefaultQueryInterface)] #[sv::custom(query=MyQuery)] impl DefaultQueryInterface for crate::MyContract { type Error = StdError; @@ -207,10 +241,11 @@ impl MyContract { #[cfg(all(test, feature = "mt"))] mod tests { - use crate::associated_type_interface::test_utils::AssociatedTypeInterface; - use crate::default_query_interface::test_utils::DefaultQueryInterface; - use crate::some_interface::test_utils::SomeInterface; - use crate::{interface::test_utils::Interface, MyContract, MyQuery}; + use crate::impl_associated_type_interface::test_utils::AssociatedTypeInterface; + use crate::impl_default_query_interface::test_utils::DefaultQueryInterface; + use crate::impl_interface::test_utils::Interface; + use crate::impl_some_interface::test_utils::SomeInterface; + use crate::{MyContract, MyQuery}; use cosmwasm_std::Empty; use sylvia::multitest::App; diff --git a/sylvia/tests/dispatching.rs b/sylvia/tests/dispatching.rs index 6858b551..8a1a35b6 100644 --- a/sylvia/tests/dispatching.rs +++ b/sylvia/tests/dispatching.rs @@ -122,12 +122,12 @@ fn dispatch() { let env = mock_env(); let info = mock_info("owner", &[]); - let resp = interface::ExecMsg::NoArgsExecution {} + let resp = interface::sv::ExecMsg::NoArgsExecution {} .dispatch(&contract, (deps.as_mut(), env.clone(), info.clone())) .unwrap(); assert_eq!(resp, Response::new()); - let resp = interface::ExecMsg::ArgumentedExecution { + let resp = interface::sv::ExecMsg::ArgumentedExecution { addr: Addr::unchecked("addr1"), coef: Decimal::percent(30), desc: "True".to_owned(), @@ -136,7 +136,7 @@ fn dispatch() { .unwrap(); assert_eq!(resp, Response::new()); - let resp = interface::ExecMsg::ArgumentedExecution { + let resp = interface::sv::ExecMsg::ArgumentedExecution { addr: Addr::unchecked("addr2"), coef: Decimal::percent(70), desc: "False".to_owned(), @@ -145,12 +145,12 @@ fn dispatch() { .unwrap(); assert_eq!(resp, Response::new()); - let resp = interface::QueryMsg::NoArgsQuery {} + let resp = interface::sv::QueryMsg::NoArgsQuery {} .dispatch(&contract, (deps.as_ref(), env.clone())) .unwrap(); let _resp: EmptyQueryResponse = from_binary(&resp).unwrap(); - let resp = interface::QueryMsg::ArgumentedQuery { + let resp = interface::sv::QueryMsg::ArgumentedQuery { user: Addr::unchecked("addr2"), } .dispatch(&contract, (deps.as_ref(), env)) diff --git a/sylvia/tests/messages_generation.rs b/sylvia/tests/messages_generation.rs index 2495fc69..023c71a5 100644 --- a/sylvia/tests/messages_generation.rs +++ b/sylvia/tests/messages_generation.rs @@ -112,26 +112,26 @@ mod contract { #[test] fn interface_messages_constructible() { - let no_args_exec = interface::ExecMsg::NoArgsExecution {}; - let _argumented_exec = interface::ExecMsg::ArgumentedExecution { + let no_args_exec = interface::sv::ExecMsg::NoArgsExecution {}; + let _argumented_exec = interface::sv::ExecMsg::ArgumentedExecution { addr: Addr::unchecked("owner"), coef: Decimal::percent(10), desc: "Some description".to_owned(), }; - let no_args_query = interface::QueryMsg::NoArgsQuery {}; - let _argumented_query = interface::QueryMsg::ArgumentedQuery { + let no_args_query = interface::sv::QueryMsg::NoArgsQuery {}; + let _argumented_query = interface::sv::QueryMsg::ArgumentedQuery { user: Addr::unchecked("owner"), }; // Ensure no extra variants are generated match no_args_exec { - interface::ExecMsg::NoArgsExecution {} => (), - interface::ExecMsg::ArgumentedExecution { .. } => (), + interface::sv::ExecMsg::NoArgsExecution {} => (), + interface::sv::ExecMsg::ArgumentedExecution { .. } => (), } match no_args_query { - interface::QueryMsg::NoArgsQuery {} => (), - interface::QueryMsg::ArgumentedQuery { .. } => (), + interface::sv::QueryMsg::NoArgsQuery {} => (), + interface::sv::QueryMsg::ArgumentedQuery { .. } => (), } } diff --git a/sylvia/tests/querier.rs b/sylvia/tests/querier.rs index 191e8800..b5c32ed3 100644 --- a/sylvia/tests/querier.rs +++ b/sylvia/tests/querier.rs @@ -14,6 +14,7 @@ pub struct CountResponse { pub mod counter { use cosmwasm_std::{Response, StdError, StdResult}; + use sv::Querier; use sylvia::types::{ExecCtx, QueryCtx}; use sylvia::{contract, interface}; @@ -69,7 +70,7 @@ pub mod counter { #[msg(exec)] fn decrease_by_count(&self, ctx: ExecCtx) -> StdResult { let remote = self.remote.load(ctx.deps.storage)?; - let other_count = BoundQuerier::borrowed(&remote.0, &ctx.deps.querier) + let other_count = sv::BoundQuerier::borrowed(&remote.0, &ctx.deps.querier) .count()? .count; self.count.update(ctx.deps.storage, |count| { @@ -124,16 +125,16 @@ mod tests { let remote_addr = Addr::unchecked("remote"); // Remote generation - let remote = super::counter::Remote::new(remote_addr.clone()); - let _: super::counter::BoundQuerier<_> = remote.querier(&querier_wrapper); + let remote = super::counter::sv::Remote::new(remote_addr.clone()); + let _: super::counter::sv::BoundQuerier<_> = remote.querier(&querier_wrapper); let remote = super::Remote::new(remote_addr.clone()); let _: super::BoundQuerier<_> = remote.querier(&querier_wrapper); // Querier generation - let _ = super::counter::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); + let _ = super::counter::sv::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); let querier = super::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); - let _ = super::counter::BoundQuerier::from(&querier); + let _ = super::counter::sv::BoundQuerier::from(&querier); } #[test] diff --git a/sylvia/tests/query_returns.rs b/sylvia/tests/query_returns.rs index f4dd6d08..9bd63ce9 100644 --- a/sylvia/tests/query_returns.rs +++ b/sylvia/tests/query_returns.rs @@ -62,7 +62,7 @@ mod tests { #[test] fn generate_interface_query() { - let _ = msg::InterfaceQueryMsg::Query { + let _ = msg::sv::InterfaceQueryMsg::Query { name: "some_name".to_owned(), }; } diff --git a/sylvia/tests/remote.rs b/sylvia/tests/remote.rs index 0b61719b..0628e1a9 100644 --- a/sylvia/tests/remote.rs +++ b/sylvia/tests/remote.rs @@ -49,9 +49,9 @@ mod tests { #[test] fn remote_generation() { // interface - let _ = some_interface::Remote::new(Addr::unchecked("some_interface")); + let _ = some_interface::sv::Remote::new(Addr::unchecked("some_interface")); let addr = Addr::unchecked("some_interface"); - let _ = some_interface::Remote::borrowed(&addr); + let _ = some_interface::sv::Remote::borrowed(&addr); // contract let new_remote = crate::Remote::new(Addr::unchecked("some_contract")); @@ -59,7 +59,7 @@ mod tests { let borrowed_remote = crate::Remote::borrowed(&addr); assert_eq!(&Addr::unchecked("some_contract"), borrowed_remote.as_ref()); - let _ = some_interface::Remote::from(&borrowed_remote); - let _ = some_interface::Remote::from(&new_remote); + let _ = some_interface::sv::Remote::from(&borrowed_remote); + let _ = some_interface::sv::Remote::from(&new_remote); } } From 9fa87a94a51fa54539077381ef2ce2a1ca374a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 25 Oct 2023 14:27:12 +0200 Subject: [PATCH 12/15] feat: Wrap impl types in `sv` module --- .../cw1-subkeys/src/multitest/tests.rs | 2 +- .../contracts/cw1-whitelist/src/multitest.rs | 4 +-- .../src/multitest/allowances_tests.rs | 2 +- .../cw20-base/src/multitest/base_tests.rs | 2 +- .../src/multitest/marketing_tests.rs | 2 +- .../cw20-base/src/multitest/minting_tests.rs | 2 +- .../cw20-base/src/multitest/receiver.rs | 36 +++++++++++-------- .../src/custom_and_generic.rs | 2 +- .../contracts/generic_contract/src/cw1.rs | 2 +- .../contracts/generic_contract/src/generic.rs | 2 +- .../generic_iface_on_contract/src/contract.rs | 6 ++-- sylvia-derive/src/input.rs | 8 +++-- sylvia/examples/basic.rs | 2 +- sylvia/tests/custom_msg.rs | 16 ++++----- sylvia/tests/custom_query.rs | 16 ++++----- sylvia/tests/querier.rs | 23 ++++++++---- 16 files changed, 73 insertions(+), 54 deletions(-) diff --git a/examples/contracts/cw1-subkeys/src/multitest/tests.rs b/examples/contracts/cw1-subkeys/src/multitest/tests.rs index e0d6c61b..71cc20d0 100644 --- a/examples/contracts/cw1-subkeys/src/multitest/tests.rs +++ b/examples/contracts/cw1-subkeys/src/multitest/tests.rs @@ -370,7 +370,7 @@ mod permissions { } mod cw1_execute { - use crate::cw1::test_utils::Cw1; + use crate::cw1::sv::test_utils::Cw1; use cosmwasm_std::BankMsg; use super::*; diff --git a/examples/contracts/cw1-whitelist/src/multitest.rs b/examples/contracts/cw1-whitelist/src/multitest.rs index c45e7c04..812f982d 100644 --- a/examples/contracts/cw1-whitelist/src/multitest.rs +++ b/examples/contracts/cw1-whitelist/src/multitest.rs @@ -4,9 +4,9 @@ mod test { use whitelist::responses::AdminListResponse; use crate::contract::multitest_utils::CodeId; - use crate::cw1::test_utils::Cw1; + use crate::cw1::sv::test_utils::Cw1; use crate::error::ContractError; - use crate::whitelist::test_utils::Whitelist; + use crate::whitelist::sv::test_utils::Whitelist; use assert_matches::assert_matches; use sylvia::multitest::App; diff --git a/examples/contracts/cw20-base/src/multitest/allowances_tests.rs b/examples/contracts/cw20-base/src/multitest/allowances_tests.rs index 005b0105..22c2735d 100644 --- a/examples/contracts/cw20-base/src/multitest/allowances_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/allowances_tests.rs @@ -7,7 +7,7 @@ use cw_multi_test::next_block; use cw_utils::Expiration; use sylvia::multitest::App; -use crate::allowances::test_utils::Cw20Allowances; +use crate::allowances::sv::test_utils::Cw20Allowances; use crate::contract::multitest_utils::CodeId; use crate::contract::InstantiateMsgData; use crate::error::ContractError; diff --git a/examples/contracts/cw20-base/src/multitest/base_tests.rs b/examples/contracts/cw20-base/src/multitest/base_tests.rs index 30eca4e6..7fdd3821 100644 --- a/examples/contracts/cw20-base/src/multitest/base_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/base_tests.rs @@ -3,7 +3,7 @@ use cw20_allowances::responses::{AllAllowancesResponse, SpenderAllowanceInfo}; use cw_utils::Expiration; use sylvia::multitest::App; -use crate::allowances::test_utils::Cw20Allowances; +use crate::allowances::sv::test_utils::Cw20Allowances; use crate::contract::multitest_utils::CodeId; use crate::contract::InstantiateMsgData; use crate::error::ContractError; diff --git a/examples/contracts/cw20-base/src/multitest/marketing_tests.rs b/examples/contracts/cw20-base/src/multitest/marketing_tests.rs index a32fb8d8..d8da5c10 100644 --- a/examples/contracts/cw20-base/src/multitest/marketing_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/marketing_tests.rs @@ -6,7 +6,7 @@ use sylvia::multitest::App; use crate::contract::multitest_utils::CodeId; use crate::contract::{InstantiateMarketingInfo, InstantiateMsgData}; use crate::error::ContractError; -use crate::marketing::test_utils::Cw20Marketing; +use crate::marketing::sv::test_utils::Cw20Marketing; const PNG_HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; diff --git a/examples/contracts/cw20-base/src/multitest/minting_tests.rs b/examples/contracts/cw20-base/src/multitest/minting_tests.rs index 9ae818d8..72bdad07 100644 --- a/examples/contracts/cw20-base/src/multitest/minting_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/minting_tests.rs @@ -5,7 +5,7 @@ use sylvia::multitest::App; use crate::contract::multitest_utils::CodeId; use crate::contract::InstantiateMsgData; use crate::error::ContractError; -use crate::minting::test_utils::Cw20Minting; +use crate::minting::sv::test_utils::Cw20Minting; use crate::responses::{Cw20Coin, TokenInfoResponse}; #[test] diff --git a/examples/contracts/cw20-base/src/multitest/receiver.rs b/examples/contracts/cw20-base/src/multitest/receiver.rs index b3494612..3a127237 100644 --- a/examples/contracts/cw20-base/src/multitest/receiver.rs +++ b/examples/contracts/cw20-base/src/multitest/receiver.rs @@ -1,8 +1,6 @@ use cosmwasm_std::{Binary, Response, StdError, Uint128}; use sylvia::types::ExecCtx; -use sylvia::{contract, interface, schemars}; - -use super::receiver_contract::ReceiverContract; +use sylvia::{interface, schemars}; #[interface] pub trait Receiver { @@ -18,18 +16,26 @@ pub trait Receiver { ) -> Result; } -#[contract(module=crate::multitest::receiver_contract)] -impl Receiver for ReceiverContract { - type Error = StdError; +pub mod impl_receiver { + use crate::multitest::receiver_contract::ReceiverContract; + use cosmwasm_std::{Response, StdError}; + use sylvia::contract; + use sylvia::types::ExecCtx; - #[msg(exec)] - fn receive( - &self, - _ctx: ExecCtx, - _sender: String, - _amount: cosmwasm_std::Uint128, - _msg: cosmwasm_std::Binary, - ) -> Result { - Ok(Response::default()) + #[contract(module=crate::multitest::receiver_contract)] + #[messages(crate::multitest::receiver as Receiver)] + impl super::Receiver for ReceiverContract { + type Error = StdError; + + #[msg(exec)] + fn receive( + &self, + _ctx: ExecCtx, + _sender: String, + _amount: cosmwasm_std::Uint128, + _msg: cosmwasm_std::Binary, + ) -> Result { + Ok(Response::default()) + } } } diff --git a/examples/contracts/generic_contract/src/custom_and_generic.rs b/examples/contracts/generic_contract/src/custom_and_generic.rs index 98926abc..47a1929f 100644 --- a/examples/contracts/generic_contract/src/custom_and_generic.rs +++ b/examples/contracts/generic_contract/src/custom_and_generic.rs @@ -39,7 +39,7 @@ impl #[cfg(test)] mod tests { - use super::test_utils::CustomAndGeneric; + use super::sv::test_utils::CustomAndGeneric; use crate::contract::multitest_utils::CodeId; use sylvia::{multitest::App, types::SvCustomMsg}; diff --git a/examples/contracts/generic_contract/src/cw1.rs b/examples/contracts/generic_contract/src/cw1.rs index 48bd0870..8113c603 100644 --- a/examples/contracts/generic_contract/src/cw1.rs +++ b/examples/contracts/generic_contract/src/cw1.rs @@ -35,7 +35,7 @@ impl Cw1 #[cfg(test)] mod tests { - use super::test_utils::Cw1; + use super::sv::test_utils::Cw1; use crate::contract::multitest_utils::CodeId; use cosmwasm_std::{CosmosMsg, Empty}; use sylvia::{multitest::App, types::SvCustomMsg}; diff --git a/examples/contracts/generic_contract/src/generic.rs b/examples/contracts/generic_contract/src/generic.rs index aef1ac96..d7b32c2c 100644 --- a/examples/contracts/generic_contract/src/generic.rs +++ b/examples/contracts/generic_contract/src/generic.rs @@ -43,7 +43,7 @@ impl #[cfg(test)] mod tests { - use super::test_utils::Generic; + use super::sv::test_utils::Generic; use crate::contract::multitest_utils::CodeId; use cosmwasm_std::CosmosMsg; use sylvia::multitest::App; diff --git a/examples/contracts/generic_iface_on_contract/src/contract.rs b/examples/contracts/generic_iface_on_contract/src/contract.rs index f35d0d69..2d0adc49 100644 --- a/examples/contracts/generic_iface_on_contract/src/contract.rs +++ b/examples/contracts/generic_iface_on_contract/src/contract.rs @@ -27,9 +27,9 @@ mod tests { use sylvia::{multitest::App, types::SvCustomMsg}; use super::NonGenericContract; - use crate::custom_and_generic::test_utils::CustomAndGeneric; - use crate::cw1::test_utils::Cw1; - use crate::generic::test_utils::Generic; + use crate::custom_and_generic::sv::test_utils::CustomAndGeneric; + use crate::cw1::sv::test_utils::Cw1; + use crate::generic::sv::test_utils::Generic; #[test] fn mt_helpers() { diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 7e0ae593..11afc04a 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -178,9 +178,13 @@ impl<'a> ImplInput<'a> { #[cfg(not(tarpaulin_include))] quote! { - #multitest_helpers + pub mod sv { + use super::*; - #querier_bound_for_impl + #multitest_helpers + + #querier_bound_for_impl + } } } diff --git a/sylvia/examples/basic.rs b/sylvia/examples/basic.rs index 7feb0b98..404aa660 100644 --- a/sylvia/examples/basic.rs +++ b/sylvia/examples/basic.rs @@ -70,7 +70,7 @@ mod impl_group { use crate::{GroupContract, MemberResp}; - #[contract(module=super)] + #[contract(module=crate)] #[messages(crate::group as Group)] impl crate::group::Group for GroupContract { type Error = Error; diff --git a/sylvia/tests/custom_msg.rs b/sylvia/tests/custom_msg.rs index 2896b593..da9aad5a 100644 --- a/sylvia/tests/custom_msg.rs +++ b/sylvia/tests/custom_msg.rs @@ -49,7 +49,7 @@ mod impl_some_interface { use crate::some_interface::SomeInterface; use crate::{MyMsg, SomeResponse}; - #[contract(module=super)] + #[contract(module=crate)] #[messages(crate::some_interface as SomeInterface)] #[sv::custom(msg=MyMsg)] impl SomeInterface for crate::MyContract { @@ -92,7 +92,7 @@ mod impl_interface { use sylvia::contract; use sylvia::types::ExecCtx; - #[contract(module=super)] + #[contract(module=crate)] #[messages(crate::interface as Interface)] #[sv::custom(msg=MyMsg)] impl Interface for crate::MyContract { @@ -126,7 +126,7 @@ mod impl_other_interface { use sylvia::contract; use sylvia::types::ExecCtx; - #[contract(module=super)] + #[contract(module=crate)] #[messages(crate::other_interface as OtherInterface)] #[sv::custom(msg=crate::MyMsg)] impl OtherInterface for crate::MyContract { @@ -161,7 +161,7 @@ mod impl_associated_interface { use sylvia::contract; use sylvia::types::ExecCtx; - #[contract(module=super)] + #[contract(module=crate)] #[messages(crate::associated_interface as AssociatedInterface)] #[sv::custom(msg=MyMsg)] impl AssociatedInterface for crate::MyContract { @@ -211,10 +211,10 @@ impl MyContract { #[cfg(all(test, feature = "mt"))] mod tests { - use crate::impl_associated_interface::test_utils::AssociatedInterface; - use crate::impl_interface::test_utils::Interface; - use crate::impl_other_interface::test_utils::OtherInterface; - use crate::impl_some_interface::test_utils::SomeInterface; + use crate::impl_associated_interface::sv::test_utils::AssociatedInterface; + use crate::impl_interface::sv::test_utils::Interface; + use crate::impl_other_interface::sv::test_utils::OtherInterface; + use crate::impl_some_interface::sv::test_utils::SomeInterface; use crate::MyContract; use crate::MyMsg; use sylvia::multitest::App; diff --git a/sylvia/tests/custom_query.rs b/sylvia/tests/custom_query.rs index 8405f873..bf0a7cf0 100644 --- a/sylvia/tests/custom_query.rs +++ b/sylvia/tests/custom_query.rs @@ -48,7 +48,7 @@ mod impl_interface { use crate::{MyQuery, OtherQuery, SomeResponse}; - #[contract(module=super)] + #[contract(module=crate)] #[messages(crate::interface as Interface)] #[sv::custom(query=MyQuery)] impl crate::interface::Interface for crate::MyContract { @@ -96,7 +96,7 @@ mod impl_some_interface { use crate::{MyQuery, SomeResponse}; - #[contract(module=super)] + #[contract(module=crate)] #[messages(crate::some_interface as SomeInterface)] #[sv::custom(query=MyQuery)] impl super::some_interface::SomeInterface for crate::MyContract { @@ -142,7 +142,7 @@ mod impl_associated_type_interface { use sylvia::types::{ExecCtx, QueryCtx}; use sylvia_derive::contract; - #[contract(module=super)] + #[contract(module=crate)] #[messages(crate::associated_type_interface as AssociatedTypeInterface)] impl AssociatedTypeInterface for crate::MyContract { type Error = StdError; @@ -187,7 +187,7 @@ mod impl_default_query_interface { use sylvia::types::{ExecCtx, QueryCtx}; use sylvia_derive::contract; - #[contract(module=super)] + #[contract(module=crate)] #[messages(crate::default_query_interface as DefaultQueryInterface)] #[sv::custom(query=MyQuery)] impl DefaultQueryInterface for crate::MyContract { @@ -241,10 +241,10 @@ impl MyContract { #[cfg(all(test, feature = "mt"))] mod tests { - use crate::impl_associated_type_interface::test_utils::AssociatedTypeInterface; - use crate::impl_default_query_interface::test_utils::DefaultQueryInterface; - use crate::impl_interface::test_utils::Interface; - use crate::impl_some_interface::test_utils::SomeInterface; + use crate::impl_associated_type_interface::sv::test_utils::AssociatedTypeInterface; + use crate::impl_default_query_interface::sv::test_utils::DefaultQueryInterface; + use crate::impl_interface::sv::test_utils::Interface; + use crate::impl_some_interface::sv::test_utils::SomeInterface; use crate::{MyContract, MyQuery}; use cosmwasm_std::Empty; diff --git a/sylvia/tests/querier.rs b/sylvia/tests/querier.rs index b5c32ed3..8720d794 100644 --- a/sylvia/tests/querier.rs +++ b/sylvia/tests/querier.rs @@ -14,9 +14,8 @@ pub struct CountResponse { pub mod counter { use cosmwasm_std::{Response, StdError, StdResult}; - use sv::Querier; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; use crate::CountResponse; @@ -36,8 +35,17 @@ pub mod counter { #[msg(exec)] fn decrease_by_count(&self, ctx: ExecCtx) -> StdResult; } +} - #[contract(module=super)] +pub mod impl_counter { + use crate::counter::sv::Querier; + use crate::counter::Counter; + use crate::CountResponse; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::{ExecCtx, QueryCtx}; + + #[contract(module=crate)] #[messages(crate::counter as Counter)] impl Counter for super::CounterContract<'_> { type Error = StdError; @@ -70,9 +78,10 @@ pub mod counter { #[msg(exec)] fn decrease_by_count(&self, ctx: ExecCtx) -> StdResult { let remote = self.remote.load(ctx.deps.storage)?; - let other_count = sv::BoundQuerier::borrowed(&remote.0, &ctx.deps.querier) - .count()? - .count; + let other_count = + crate::counter::sv::BoundQuerier::borrowed(&remote.0, &ctx.deps.querier) + .count()? + .count; self.count.update(ctx.deps.storage, |count| { let count = count.saturating_sub(other_count); Ok::<_, StdError>(count) @@ -115,7 +124,7 @@ mod tests { use cosmwasm_std::{Addr, Empty, QuerierWrapper}; use sylvia::multitest::App; - use crate::counter::test_utils::Counter; + use crate::impl_counter::sv::test_utils::Counter; use crate::multitest_utils::CodeId; #[test] From 8ee427ed269d719fb47603324fd345f7eeeb6812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 25 Oct 2023 14:57:38 +0200 Subject: [PATCH 13/15] feat: Wrap contract types in `sv` module --- examples/contracts/custom/src/bin/schema.rs | 2 +- .../contracts/custom/src/multitest/tests.rs | 2 +- .../contracts/cw1-subkeys/src/bin/schema.rs | 2 +- .../cw1-subkeys/src/multitest/tests.rs | 2 +- .../contracts/cw1-whitelist/src/bin/schema.rs | 2 +- .../contracts/cw1-whitelist/src/contract.rs | 2 +- .../contracts/cw1-whitelist/src/multitest.rs | 2 +- .../contracts/cw20-base/src/bin/schema.rs | 2 +- .../src/multitest/allowances_tests.rs | 4 +- .../cw20-base/src/multitest/base_tests.rs | 4 +- .../src/multitest/marketing_tests.rs | 2 +- .../cw20-base/src/multitest/minting_tests.rs | 2 +- .../entry-points-overriding/src/bin/schema.rs | 2 +- .../entry-points-overriding/src/messages.rs | 3 +- .../entry-points-overriding/src/multitest.rs | 7 +- .../generic_contract/src/bin/schema.rs | 2 +- .../generic_contract/src/contract.rs | 2 +- .../src/custom_and_generic.rs | 2 +- .../contracts/generic_contract/src/cw1.rs | 2 +- .../contracts/generic_contract/src/generic.rs | 2 +- .../src/bin/schema.rs | 4 +- .../generic_iface_on_contract/src/contract.rs | 2 +- sylvia-derive/src/input.rs | 14 +- sylvia-derive/src/message.rs | 6 +- sylvia/tests/custom_msg.rs | 2 +- sylvia/tests/custom_query.rs | 2 +- sylvia/tests/dispatching.rs | 144 ++++++++++-------- sylvia/tests/entry_points.rs | 6 +- sylvia/tests/messages_generation.rs | 20 +-- sylvia/tests/querier.rs | 14 +- sylvia/tests/query_returns.rs | 2 +- sylvia/tests/remote.rs | 6 +- sylvia/tests/replies.rs | 8 +- 33 files changed, 150 insertions(+), 130 deletions(-) diff --git a/examples/contracts/custom/src/bin/schema.rs b/examples/contracts/custom/src/bin/schema.rs index a60261b8..554d313c 100644 --- a/examples/contracts/custom/src/bin/schema.rs +++ b/examples/contracts/custom/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use custom::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use custom::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/examples/contracts/custom/src/multitest/tests.rs b/examples/contracts/custom/src/multitest/tests.rs index 5b92208a..566ee7ff 100644 --- a/examples/contracts/custom/src/multitest/tests.rs +++ b/examples/contracts/custom/src/multitest/tests.rs @@ -1,6 +1,6 @@ use sylvia::multitest::App; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use super::custom_module::CustomModule; diff --git a/examples/contracts/cw1-subkeys/src/bin/schema.rs b/examples/contracts/cw1-subkeys/src/bin/schema.rs index b07cdbaa..c5129235 100644 --- a/examples/contracts/cw1-subkeys/src/bin/schema.rs +++ b/examples/contracts/cw1-subkeys/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use cw1_subkeys::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use cw1_subkeys::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/examples/contracts/cw1-subkeys/src/multitest/tests.rs b/examples/contracts/cw1-subkeys/src/multitest/tests.rs index 71cc20d0..a92fe845 100644 --- a/examples/contracts/cw1-subkeys/src/multitest/tests.rs +++ b/examples/contracts/cw1-subkeys/src/multitest/tests.rs @@ -3,7 +3,7 @@ use cw2::{query_contract_info, ContractVersion}; use cw_utils::{Expiration, NativeBalance}; use sylvia::multitest::App; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; const ATOM: &str = "atom"; diff --git a/examples/contracts/cw1-whitelist/src/bin/schema.rs b/examples/contracts/cw1-whitelist/src/bin/schema.rs index fa957edb..3096b127 100644 --- a/examples/contracts/cw1-whitelist/src/bin/schema.rs +++ b/examples/contracts/cw1-whitelist/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use cw1_whitelist::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use cw1_whitelist::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/examples/contracts/cw1-whitelist/src/contract.rs b/examples/contracts/cw1-whitelist/src/contract.rs index 9f5ae735..2bf302af 100644 --- a/examples/contracts/cw1-whitelist/src/contract.rs +++ b/examples/contracts/cw1-whitelist/src/contract.rs @@ -290,7 +290,7 @@ mod tests { mod msgs { use cosmwasm_std::{from_binary, from_slice, to_binary, BankMsg}; - use crate::contract::{ContractExecMsg, ContractQueryMsg}; + use crate::contract::sv::{ContractExecMsg, ContractQueryMsg}; #[test] fn freeze() { diff --git a/examples/contracts/cw1-whitelist/src/multitest.rs b/examples/contracts/cw1-whitelist/src/multitest.rs index 812f982d..a91713d6 100644 --- a/examples/contracts/cw1-whitelist/src/multitest.rs +++ b/examples/contracts/cw1-whitelist/src/multitest.rs @@ -3,7 +3,7 @@ mod test { use cosmwasm_std::{to_binary, WasmMsg}; use whitelist::responses::AdminListResponse; - use crate::contract::multitest_utils::CodeId; + use crate::contract::sv::multitest_utils::CodeId; use crate::cw1::sv::test_utils::Cw1; use crate::error::ContractError; use crate::whitelist::sv::test_utils::Whitelist; diff --git a/examples/contracts/cw20-base/src/bin/schema.rs b/examples/contracts/cw20-base/src/bin/schema.rs index 3482c2fb..ee47c95c 100644 --- a/examples/contracts/cw20-base/src/bin/schema.rs +++ b/examples/contracts/cw20-base/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use cw20_base::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use cw20_base::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/examples/contracts/cw20-base/src/multitest/allowances_tests.rs b/examples/contracts/cw20-base/src/multitest/allowances_tests.rs index 22c2735d..29fdf0ad 100644 --- a/examples/contracts/cw20-base/src/multitest/allowances_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/allowances_tests.rs @@ -8,10 +8,10 @@ use cw_utils::Expiration; use sylvia::multitest::App; use crate::allowances::sv::test_utils::Cw20Allowances; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::InstantiateMsgData; use crate::error::ContractError; -use crate::multitest::receiver_contract::multitest_utils::CodeId as ReceiverCodeId; +use crate::multitest::receiver_contract::sv::multitest_utils::CodeId as ReceiverCodeId; use crate::responses::Cw20Coin; #[test] diff --git a/examples/contracts/cw20-base/src/multitest/base_tests.rs b/examples/contracts/cw20-base/src/multitest/base_tests.rs index 7fdd3821..f97a1699 100644 --- a/examples/contracts/cw20-base/src/multitest/base_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/base_tests.rs @@ -4,10 +4,10 @@ use cw_utils::Expiration; use sylvia::multitest::App; use crate::allowances::sv::test_utils::Cw20Allowances; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::InstantiateMsgData; use crate::error::ContractError; -use crate::multitest::receiver_contract::multitest_utils::CodeId as ReceiverCodeId; +use crate::multitest::receiver_contract::sv::multitest_utils::CodeId as ReceiverCodeId; use crate::responses::{BalanceResponse, Cw20Coin, TokenInfoResponse}; #[test] diff --git a/examples/contracts/cw20-base/src/multitest/marketing_tests.rs b/examples/contracts/cw20-base/src/multitest/marketing_tests.rs index d8da5c10..c5e22350 100644 --- a/examples/contracts/cw20-base/src/multitest/marketing_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/marketing_tests.rs @@ -3,7 +3,7 @@ use cw20_marketing::responses::{DownloadLogoResponse, LogoInfo, MarketingInfoRes use cw20_marketing::{EmbeddedLogo, Logo}; use sylvia::multitest::App; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::{InstantiateMarketingInfo, InstantiateMsgData}; use crate::error::ContractError; use crate::marketing::sv::test_utils::Cw20Marketing; diff --git a/examples/contracts/cw20-base/src/multitest/minting_tests.rs b/examples/contracts/cw20-base/src/multitest/minting_tests.rs index 72bdad07..50418d84 100644 --- a/examples/contracts/cw20-base/src/multitest/minting_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/minting_tests.rs @@ -2,7 +2,7 @@ use cosmwasm_std::{StdError, Uint128}; use cw20_minting::responses::MinterResponse; use sylvia::multitest::App; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::InstantiateMsgData; use crate::error::ContractError; use crate::minting::sv::test_utils::Cw20Minting; diff --git a/examples/contracts/entry-points-overriding/src/bin/schema.rs b/examples/contracts/entry-points-overriding/src/bin/schema.rs index 0c9fdd48..6d5bd4e0 100644 --- a/examples/contracts/entry-points-overriding/src/bin/schema.rs +++ b/examples/contracts/entry-points-overriding/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use entry_points_overriding::contract::{ContractQueryMsg, InstantiateMsg}; +use entry_points_overriding::contract::sv::{ContractQueryMsg, InstantiateMsg}; use entry_points_overriding::messages::CustomExecMsg; use entry_points_overriding::messages::SudoMsg; diff --git a/examples/contracts/entry-points-overriding/src/messages.rs b/examples/contracts/entry-points-overriding/src/messages.rs index c032e2b4..56b372b5 100644 --- a/examples/contracts/entry-points-overriding/src/messages.rs +++ b/examples/contracts/entry-points-overriding/src/messages.rs @@ -2,7 +2,8 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdError, StdResult}; use sylvia::types::ExecCtx; -use crate::contract::{ContractExecMsg, CounterContract}; +use crate::contract::sv::ContractExecMsg; +use crate::contract::CounterContract; #[cw_serde] pub struct CountResponse { diff --git a/examples/contracts/entry-points-overriding/src/multitest.rs b/examples/contracts/entry-points-overriding/src/multitest.rs index d8224516..9efda4e8 100644 --- a/examples/contracts/entry-points-overriding/src/multitest.rs +++ b/examples/contracts/entry-points-overriding/src/multitest.rs @@ -1,9 +1,8 @@ #[cfg(test)] mod test { - use crate::{ - contract::{multitest_utils::CodeId, ContractExecMsg, ExecMsg}, - messages::{CustomExecMsg, SudoMsg, UserExecMsg}, - }; + use crate::contract::sv::multitest_utils::CodeId; + use crate::contract::sv::{ContractExecMsg, ExecMsg}; + use crate::messages::{CustomExecMsg, SudoMsg, UserExecMsg}; use cosmwasm_std::Addr; use cw_multi_test::Executor; use sylvia::multitest::App; diff --git a/examples/contracts/generic_contract/src/bin/schema.rs b/examples/contracts/generic_contract/src/bin/schema.rs index f84d1bb3..db8d7541 100644 --- a/examples/contracts/generic_contract/src/bin/schema.rs +++ b/examples/contracts/generic_contract/src/bin/schema.rs @@ -2,7 +2,7 @@ use cosmwasm_schema::write_api; #[cfg(not(tarpaulin_include))] fn main() { - use generic_contract::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; + use generic_contract::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; use sylvia::types::SvCustomMsg; write_api! { diff --git a/examples/contracts/generic_contract/src/contract.rs b/examples/contracts/generic_contract/src/contract.rs index e253e06e..4fffc93e 100644 --- a/examples/contracts/generic_contract/src/contract.rs +++ b/examples/contracts/generic_contract/src/contract.rs @@ -79,7 +79,7 @@ where #[cfg(test)] mod tests { - use super::multitest_utils::CodeId; + use super::sv::multitest_utils::CodeId; use sylvia::multitest::App; use sylvia::types::SvCustomMsg; diff --git a/examples/contracts/generic_contract/src/custom_and_generic.rs b/examples/contracts/generic_contract/src/custom_and_generic.rs index 47a1929f..d1ce0b12 100644 --- a/examples/contracts/generic_contract/src/custom_and_generic.rs +++ b/examples/contracts/generic_contract/src/custom_and_generic.rs @@ -40,7 +40,7 @@ impl #[cfg(test)] mod tests { use super::sv::test_utils::CustomAndGeneric; - use crate::contract::multitest_utils::CodeId; + use crate::contract::sv::multitest_utils::CodeId; use sylvia::{multitest::App, types::SvCustomMsg}; #[test] diff --git a/examples/contracts/generic_contract/src/cw1.rs b/examples/contracts/generic_contract/src/cw1.rs index 8113c603..4c5c125c 100644 --- a/examples/contracts/generic_contract/src/cw1.rs +++ b/examples/contracts/generic_contract/src/cw1.rs @@ -36,7 +36,7 @@ impl Cw1 #[cfg(test)] mod tests { use super::sv::test_utils::Cw1; - use crate::contract::multitest_utils::CodeId; + use crate::contract::sv::multitest_utils::CodeId; use cosmwasm_std::{CosmosMsg, Empty}; use sylvia::{multitest::App, types::SvCustomMsg}; diff --git a/examples/contracts/generic_contract/src/generic.rs b/examples/contracts/generic_contract/src/generic.rs index d7b32c2c..8504c8ae 100644 --- a/examples/contracts/generic_contract/src/generic.rs +++ b/examples/contracts/generic_contract/src/generic.rs @@ -44,7 +44,7 @@ impl #[cfg(test)] mod tests { use super::sv::test_utils::Generic; - use crate::contract::multitest_utils::CodeId; + use crate::contract::sv::multitest_utils::CodeId; use cosmwasm_std::CosmosMsg; use sylvia::multitest::App; use sylvia::types::SvCustomMsg; diff --git a/examples/contracts/generic_iface_on_contract/src/bin/schema.rs b/examples/contracts/generic_iface_on_contract/src/bin/schema.rs index 8a7c48e5..03983dc6 100644 --- a/examples/contracts/generic_iface_on_contract/src/bin/schema.rs +++ b/examples/contracts/generic_iface_on_contract/src/bin/schema.rs @@ -2,7 +2,9 @@ use cosmwasm_schema::write_api; #[cfg(not(tarpaulin_include))] fn main() { - use generic_iface_on_contract::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; + use generic_iface_on_contract::contract::sv::{ + ContractExecMsg, ContractQueryMsg, InstantiateMsg, + }; write_api! { instantiate: InstantiateMsg, diff --git a/examples/contracts/generic_iface_on_contract/src/contract.rs b/examples/contracts/generic_iface_on_contract/src/contract.rs index 2d0adc49..0a94398b 100644 --- a/examples/contracts/generic_iface_on_contract/src/contract.rs +++ b/examples/contracts/generic_iface_on_contract/src/contract.rs @@ -35,7 +35,7 @@ mod tests { fn mt_helpers() { let _ = NonGenericContract::new(); let app = App::>::custom(|_, _, _| {}); - let code_id = super::multitest_utils::CodeId::store_code(&app); + let code_id = super::sv::multitest_utils::CodeId::store_code(&app); let owner = "owner"; diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 11afc04a..edd55cbf 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -207,15 +207,19 @@ impl<'a> ImplInput<'a> { #[cfg(not(tarpaulin_include))] { quote! { - #messages + pub mod sv { + use super::*; - #multitest_helpers + #messages - #remote + #multitest_helpers - #querier + #remote + + #querier - #(#querier_from_impl)* + #(#querier_from_impl)* + } } } } diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 9c299103..141d1141 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -978,8 +978,8 @@ where .map(|module| quote! { #module ::sv::Querier }) .unwrap_or_else(|| quote! { sv::Querier }); let bound_querier = contract_module - .map(|module| quote! { #module ::BoundQuerier}) - .unwrap_or_else(|| quote! { BoundQuerier }); + .map(|module| quote! { #module ::sv::BoundQuerier}) + .unwrap_or_else(|| quote! { sv::BoundQuerier }); let querier = if !used_generics.is_empty() { quote! { #querier < #(#used_generics,)* > } @@ -1044,7 +1044,7 @@ where #[#sylvia ::cw_std::entry_point] pub fn #ep_name ( #params , - msg: #msg_name #bracketed_generics, + msg: sv:: #msg_name #bracketed_generics, ) -> Result<#resp_type, #error> { msg.dispatch(&#name ::new() , ( #values )).map_err(Into::into) } diff --git a/sylvia/tests/custom_msg.rs b/sylvia/tests/custom_msg.rs index da9aad5a..55e6693c 100644 --- a/sylvia/tests/custom_msg.rs +++ b/sylvia/tests/custom_msg.rs @@ -223,7 +223,7 @@ mod tests { fn test_custom() { let _ = MyContract::new(); let app = App::>::custom(|_, _, _| {}); - let code_id = crate::multitest_utils::CodeId::store_code(&app); + let code_id = crate::sv::multitest_utils::CodeId::store_code(&app); let owner = "owner"; diff --git a/sylvia/tests/custom_query.rs b/sylvia/tests/custom_query.rs index bf0a7cf0..00c33d34 100644 --- a/sylvia/tests/custom_query.rs +++ b/sylvia/tests/custom_query.rs @@ -254,7 +254,7 @@ mod tests { fn test_custom() { let _ = MyContract::new(); let app = App::>::custom(|_, _, _| {}); - let code_id = crate::multitest_utils::CodeId::store_code(&app); + let code_id = crate::sv::multitest_utils::CodeId::store_code(&app); let owner = "owner"; diff --git a/sylvia/tests/dispatching.rs b/sylvia/tests/dispatching.rs index 8a1a35b6..155354cf 100644 --- a/sylvia/tests/dispatching.rs +++ b/sylvia/tests/dispatching.rs @@ -1,12 +1,20 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; -use cosmwasm_std::{from_binary, Addr, Decimal, Response, StdError, StdResult}; -use interface::Interface; +use cosmwasm_std::{from_binary, Addr, Decimal, Response}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::cell::RefCell; -use std::collections::HashMap; -use sylvia::contract; -use sylvia::types::{ExecCtx, QueryCtx}; + +use crate::contract::Contract; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct EmptyQueryResponse {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct QueryResponse { + coef: Decimal, + desc: String, +} mod interface { use cosmwasm_std::{Addr, Decimal, Response, StdError}; @@ -40,77 +48,83 @@ mod interface { } } -#[derive(Default)] -pub struct Contract { - execs: RefCell, - queries: RefCell, +mod impl_interface { + use cosmwasm_std::{Addr, Decimal, Response, StdError}; + use sylvia::types::{ExecCtx, QueryCtx}; - data: RefCell>, -} + use crate::{EmptyQueryResponse, QueryResponse}; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct EmptyQueryResponse {} + #[sylvia::contract(module = crate::contract)] + #[messages(crate::interface as Interface)] + impl crate::interface::Interface for crate::contract::Contract { + type Error = StdError; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct QueryResponse { - coef: Decimal, - desc: String, -} - -#[contract] -#[messages(interface as Interface)] -impl Interface for Contract { - type Error = StdError; + #[msg(exec)] + fn no_args_execution(&self, _: ExecCtx) -> Result { + *self.execs.borrow_mut() += 1; + Ok(Response::new()) + } - #[msg(exec)] - fn no_args_execution(&self, _: ExecCtx) -> Result { - *self.execs.borrow_mut() += 1; - Ok(Response::new()) - } + #[msg(exec)] + fn argumented_execution( + &self, + _: ExecCtx, + addr: Addr, + coef: Decimal, + desc: String, + ) -> Result { + *self.execs.borrow_mut() += 1; - #[msg(exec)] - fn argumented_execution( - &self, - _: ExecCtx, - addr: Addr, - coef: Decimal, - desc: String, - ) -> Result { - *self.execs.borrow_mut() += 1; - - self.data - .borrow_mut() - .insert(addr, QueryResponse { coef, desc }); - Ok(Response::new()) - } + self.data + .borrow_mut() + .insert(addr, QueryResponse { coef, desc }); + Ok(Response::new()) + } - #[msg(query)] - fn no_args_query(&self, _: QueryCtx) -> Result { - *self.queries.borrow_mut() += 1; - Ok(dbg!(EmptyQueryResponse {})) - } + #[msg(query)] + fn no_args_query(&self, _: QueryCtx) -> Result { + *self.queries.borrow_mut() += 1; + Ok(dbg!(EmptyQueryResponse {})) + } - #[msg(query)] - fn argumented_query(&self, _: QueryCtx, user: Addr) -> Result { - *self.queries.borrow_mut() += 1; - Ok(self.data.borrow().get(&user).unwrap().clone()) + #[msg(query)] + fn argumented_query(&self, _: QueryCtx, user: Addr) -> Result { + *self.queries.borrow_mut() += 1; + Ok(self.data.borrow().get(&user).unwrap().clone()) + } } } -#[allow(dead_code)] -#[cfg(not(tarpaulin_include))] -#[contract] -#[messages(interface as Interface)] -impl Contract { - fn new() -> Self { - Self::default() +mod contract { + use std::{cell::RefCell, collections::HashMap}; + + use cosmwasm_std::{Addr, Response, StdResult}; + use sylvia::types::ExecCtx; + use sylvia_derive::contract; + + use crate::QueryResponse; + + #[derive(Default)] + pub struct Contract { + pub(crate) execs: RefCell, + pub(crate) queries: RefCell, + + pub(crate) data: RefCell>, } - #[msg(instantiate)] - fn instanciate(&self, _: ExecCtx) -> StdResult { - Ok(Response::new()) + #[allow(dead_code)] + #[cfg(not(tarpaulin_include))] + #[contract] + #[messages(crate::interface as Interface)] + impl Contract { + fn new() -> Self { + Self::default() + } + + #[msg(instantiate)] + fn instanciate(&self, _: ExecCtx) -> StdResult { + Ok(Response::new()) + } } } diff --git a/sylvia/tests/entry_points.rs b/sylvia/tests/entry_points.rs index d7bbb2e4..951dfee6 100644 --- a/sylvia/tests/entry_points.rs +++ b/sylvia/tests/entry_points.rs @@ -1,4 +1,4 @@ -use contract::ContractExecMsg; +use contract::sv::ContractExecMsg; use cosmwasm_schema::cw_serde; use cosmwasm_std::Coin; use cw_storage_plus::Item; @@ -145,8 +145,8 @@ mod tests { use cw_multi_test::Executor; use sylvia::multitest::App; - use crate::contract::multitest_utils::CodeId; - use crate::contract::{ContractExecMsg, ExecMsg}; + use crate::contract::sv::multitest_utils::CodeId; + use crate::contract::sv::{ContractExecMsg, ExecMsg}; use crate::exec::{CustomExecMsg, UserExecMsg}; use crate::SudoMsg; diff --git a/sylvia/tests/messages_generation.rs b/sylvia/tests/messages_generation.rs index 023c71a5..6fe0b947 100644 --- a/sylvia/tests/messages_generation.rs +++ b/sylvia/tests/messages_generation.rs @@ -137,28 +137,28 @@ fn interface_messages_constructible() { #[test] fn contract_messages_constructible() { - let no_args_exec = contract::ExecMsg::NoArgsExecution {}; - let _argumented_exec = contract::ExecMsg::ArgumentedExecution { + let no_args_exec = contract::sv::ExecMsg::NoArgsExecution {}; + let _argumented_exec = contract::sv::ExecMsg::ArgumentedExecution { _addr: Addr::unchecked("owner"), _coef: Decimal::percent(10), _desc: "Some description".to_owned(), }; - let no_args_query = contract::QueryMsg::NoArgsQuery {}; - let _argumented_query = contract::QueryMsg::ArgumentedQuery { + let no_args_query = contract::sv::QueryMsg::NoArgsQuery {}; + let _argumented_query = contract::sv::QueryMsg::ArgumentedQuery { _user: Addr::unchecked("owner"), }; - let _ = contract::InstantiateMsg {}; - let _ = contract::MigrateMsg {}; + let _ = contract::sv::InstantiateMsg {}; + let _ = contract::sv::MigrateMsg {}; // Ensure no extra variants are generated match no_args_exec { - contract::ExecMsg::NoArgsExecution {} => (), - contract::ExecMsg::ArgumentedExecution { .. } => (), + contract::sv::ExecMsg::NoArgsExecution {} => (), + contract::sv::ExecMsg::ArgumentedExecution { .. } => (), } match no_args_query { - contract::QueryMsg::NoArgsQuery {} => (), - contract::QueryMsg::ArgumentedQuery { .. } => (), + contract::sv::QueryMsg::NoArgsQuery {} => (), + contract::sv::QueryMsg::ArgumentedQuery { .. } => (), } } diff --git a/sylvia/tests/querier.rs b/sylvia/tests/querier.rs index 8720d794..9a78ed3b 100644 --- a/sylvia/tests/querier.rs +++ b/sylvia/tests/querier.rs @@ -79,7 +79,7 @@ pub mod impl_counter { fn decrease_by_count(&self, ctx: ExecCtx) -> StdResult { let remote = self.remote.load(ctx.deps.storage)?; let other_count = - crate::counter::sv::BoundQuerier::borrowed(&remote.0, &ctx.deps.querier) + crate::counter::sv::BoundQuerier::borrowed(remote.as_ref(), &ctx.deps.querier) .count()? .count; self.count.update(ctx.deps.storage, |count| { @@ -94,7 +94,7 @@ pub mod impl_counter { pub struct CounterContract<'a> { pub count: Item<'static, u64>, - pub remote: Item<'static, Remote<'a>>, + pub remote: Item<'static, sv::Remote<'a>>, } #[contract] @@ -112,7 +112,7 @@ impl CounterContract<'_> { fn instantiate(&self, ctx: InstantiateCtx, remote_addr: Addr) -> StdResult { self.count.save(ctx.deps.storage, &0)?; self.remote - .save(ctx.deps.storage, &Remote::new(remote_addr))?; + .save(ctx.deps.storage, &sv::Remote::new(remote_addr))?; Ok(Response::new()) } } @@ -125,7 +125,7 @@ mod tests { use sylvia::multitest::App; use crate::impl_counter::sv::test_utils::Counter; - use crate::multitest_utils::CodeId; + use crate::sv::multitest_utils::CodeId; #[test] fn querier_generation() { @@ -136,12 +136,12 @@ mod tests { // Remote generation let remote = super::counter::sv::Remote::new(remote_addr.clone()); let _: super::counter::sv::BoundQuerier<_> = remote.querier(&querier_wrapper); - let remote = super::Remote::new(remote_addr.clone()); - let _: super::BoundQuerier<_> = remote.querier(&querier_wrapper); + let remote = super::sv::Remote::new(remote_addr.clone()); + let _: super::sv::BoundQuerier<_> = remote.querier(&querier_wrapper); // Querier generation let _ = super::counter::sv::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); - let querier = super::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); + let querier = super::sv::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); let _ = super::counter::sv::BoundQuerier::from(&querier); } diff --git a/sylvia/tests/query_returns.rs b/sylvia/tests/query_returns.rs index 9bd63ce9..b85dd6a1 100644 --- a/sylvia/tests/query_returns.rs +++ b/sylvia/tests/query_returns.rs @@ -69,7 +69,7 @@ mod tests { #[test] fn generate_contract_query() { - let _ = QueryMsg::ContractQuery { + let _ = sv::QueryMsg::ContractQuery { _name: "some_name".to_owned(), }; } diff --git a/sylvia/tests/remote.rs b/sylvia/tests/remote.rs index 0628e1a9..4c937085 100644 --- a/sylvia/tests/remote.rs +++ b/sylvia/tests/remote.rs @@ -37,7 +37,7 @@ impl SomeContract { #[cw_serde] #[allow(dead_code)] struct CustomStorage { - remote: crate::Remote<'static>, + remote: crate::sv::Remote<'static>, } #[cfg(test)] @@ -54,9 +54,9 @@ mod tests { let _ = some_interface::sv::Remote::borrowed(&addr); // contract - let new_remote = crate::Remote::new(Addr::unchecked("some_contract")); + let new_remote = crate::sv::Remote::new(Addr::unchecked("some_contract")); let addr = Addr::unchecked("some_contract"); - let borrowed_remote = crate::Remote::borrowed(&addr); + let borrowed_remote = crate::sv::Remote::borrowed(&addr); assert_eq!(&Addr::unchecked("some_contract"), borrowed_remote.as_ref()); let _ = some_interface::sv::Remote::from(&borrowed_remote); diff --git a/sylvia/tests/replies.rs b/sylvia/tests/replies.rs index a7bf5021..979db3cc 100644 --- a/sylvia/tests/replies.rs +++ b/sylvia/tests/replies.rs @@ -55,7 +55,7 @@ mod reply_contract { #[msg(exec)] fn poke(&self, _ctx: ExecCtx, noop: String) -> StdResult { - let msg = noop_contract::ExecMsg::Noop {}; + let msg = noop_contract::sv::ExecMsg::Noop {}; let msg = WasmMsg::Execute { contract_addr: noop, msg: to_binary(&msg)?, @@ -93,16 +93,16 @@ fn entry_point_generation() { assert_eq!(data, "data"); } -#[cfg(feature = "mt")] +#[cfg(all(test, feature = "mt"))] #[test] fn mt_helper_generation() { let app = sylvia::multitest::App::default(); let owner = "owner"; - let noop_contract_code = noop_contract::multitest_utils::CodeId::store_code(&app); + let noop_contract_code = noop_contract::sv::multitest_utils::CodeId::store_code(&app); let noop_contract = noop_contract_code.instantiate().call(owner).unwrap(); - let reply_contract_code = reply_contract::multitest_utils::CodeId::store_code(&app); + let reply_contract_code = reply_contract::sv::multitest_utils::CodeId::store_code(&app); let reply_contract = reply_contract_code.instantiate().call(owner).unwrap(); let resp = reply_contract From 0a0f75673a6fb0352586277a02aced94f71e55f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 25 Oct 2023 22:24:58 +0200 Subject: [PATCH 14/15] feat: Impl ContractApi extension trait --- .../interfaces/custom-and-generic/src/lib.rs | 6 +- examples/interfaces/generic/src/lib.rs | 11 +- sylvia-derive/src/input.rs | 15 ++- sylvia-derive/src/interfaces.rs | 4 +- sylvia-derive/src/message.rs | 111 ++++++++++++++- sylvia-derive/src/multitest.rs | 3 +- sylvia-derive/src/remote.rs | 4 +- sylvia/src/types.rs | 13 +- sylvia/tests/api.rs | 127 ++++++++++++++++++ 9 files changed, 270 insertions(+), 24 deletions(-) create mode 100644 sylvia/tests/api.rs diff --git a/examples/interfaces/custom-and-generic/src/lib.rs b/examples/interfaces/custom-and-generic/src/lib.rs index 80eb0cfd..f56d4503 100644 --- a/examples/interfaces/custom-and-generic/src/lib.rs +++ b/examples/interfaces/custom-and-generic/src/lib.rs @@ -34,7 +34,7 @@ where mod tests { use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::{Addr, CosmosMsg, Empty, QuerierWrapper}; - use sylvia::types::{InterfaceMessages, SvCustomMsg}; + use sylvia::types::{InterfaceApi, SvCustomMsg}; use crate::sv::Querier; @@ -58,11 +58,11 @@ mod tests { // Construct messages with Interface extension let _ = - as InterfaceMessages>::Query::custom_generic_query( + as InterfaceApi>::Query::custom_generic_query( SvCustomMsg {}, ); let _= - as InterfaceMessages>::Exec::custom_generic_execute( + as InterfaceApi>::Exec::custom_generic_execute( vec![ CosmosMsg::Custom(SvCustomMsg{}), ]); } diff --git a/examples/interfaces/generic/src/lib.rs b/examples/interfaces/generic/src/lib.rs index 827067ea..f6801536 100644 --- a/examples/interfaces/generic/src/lib.rs +++ b/examples/interfaces/generic/src/lib.rs @@ -27,7 +27,7 @@ where #[cfg(test)] mod tests { use cosmwasm_std::{testing::mock_dependencies, Addr, CosmosMsg, Empty, QuerierWrapper}; - use sylvia::types::{InterfaceMessages, SvCustomMsg}; + use sylvia::types::{InterfaceApi, SvCustomMsg}; use crate::sv::Querier; @@ -49,12 +49,11 @@ mod tests { let _: Result = querier.generic_query(SvCustomMsg {}); // Construct messages with Interface extension - let _ = - as InterfaceMessages>::Query::generic_query( - SvCustomMsg {}, - ); + let _ = as InterfaceApi>::Query::generic_query( + SvCustomMsg {}, + ); let _= - as InterfaceMessages>::Exec::generic_exec(vec![ + as InterfaceApi>::Exec::generic_exec(vec![ CosmosMsg::Custom(SvCustomMsg{}), ]); } diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index edd55cbf..839702cc 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -12,7 +12,8 @@ use crate::check_generics::GetPath; use crate::crate_module; use crate::interfaces::Interfaces; use crate::message::{ - ContractEnumMessage, EnumMessage, GlueMessage, InterfaceMessages, MsgVariants, StructMessage, + ContractApi, ContractEnumMessage, EnumMessage, GlueMessage, InterfaceApi, MsgVariants, + StructMessage, }; use crate::multitest::{MultitestHelpers, TraitMultitestHelpers}; use crate::parser::{ContractArgs, ContractErrorAttr, Custom, MsgType, OverrideEntryPoints}; @@ -77,7 +78,7 @@ impl<'a> TraitInput<'a> { ) .emit_querier(); - let interface_messages = InterfaceMessages::new(self.item, &self.generics).emit(); + let interface_messages = InterfaceApi::new(self.item, &self.generics).emit(); #[cfg(not(tarpaulin_include))] { @@ -189,7 +190,12 @@ impl<'a> ImplInput<'a> { } fn process_contract(&self) -> TokenStream { - let Self { item, generics, .. } = self; + let Self { + item, + generics, + custom, + .. + } = self; let multitest_helpers = self.emit_multitest_helpers(generics); let where_clause = &item.generics.where_clause; @@ -203,6 +209,7 @@ impl<'a> ImplInput<'a> { let messages = self.emit_messages(); let remote = Remote::new(&self.interfaces).emit(); let querier_from_impl = self.interfaces.emit_querier_from_impl(); + let contract_api = ContractApi::new(item, generics, custom).emit(); #[cfg(not(tarpaulin_include))] { @@ -219,6 +226,8 @@ impl<'a> ImplInput<'a> { #querier #(#querier_from_impl)* + + #contract_api } } } diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs index 61455b1e..0e75edd1 100644 --- a/sylvia-derive/src/interfaces.rs +++ b/sylvia-derive/src/interfaces.rs @@ -95,7 +95,7 @@ impl Interfaces { }; let interface_enum = - quote! { <#module ::sv::InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> }; + quote! { <#module ::sv::Api #generics as #sylvia ::types::InterfaceApi> }; if msg_ty == &MsgType::Query { quote! { #variant ( #interface_enum :: Query) } } else { @@ -161,7 +161,7 @@ impl Interfaces { let type_name = msg_ty.as_accessor_name(); quote! { - <#module ::sv::InterfaceTypes #generics as #sylvia ::types::InterfaceMessages> :: #type_name :: response_schemas_impl() + <#module ::sv::Api #generics as #sylvia ::types::InterfaceApi> :: #type_name :: response_schemas_impl() } }) .collect() diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 141d1141..fceb8c58 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -745,7 +745,8 @@ impl<'a> MsgVariant<'a> { let params = fields.iter().map(|field| field.emit_method_field()); let arguments = fields.iter().map(MsgField::name); let bracketed_generics = emit_bracketed_generics(generics); - let interface_enum = quote! { < #module sv::InterfaceTypes #bracketed_generics as #sylvia ::types::InterfaceMessages> }; + let interface_enum = + quote! { < #module sv::Api #bracketed_generics as #sylvia ::types::InterfaceApi> }; let type_name = msg_ty.as_accessor_name(); let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); @@ -1434,13 +1435,113 @@ impl<'a> GlueMessage<'a> { } } -pub struct InterfaceMessages<'a> { +pub struct ContractApi<'a> { + source: &'a ItemImpl, + exec_variants: MsgVariants<'a, GenericParam>, + query_variants: MsgVariants<'a, GenericParam>, + instantiate_variants: MsgVariants<'a, GenericParam>, + migrate_variants: MsgVariants<'a, GenericParam>, + generics: &'a [&'a GenericParam], + custom: &'a Custom<'a>, +} + +impl<'a> ContractApi<'a> { + pub fn new( + source: &'a ItemImpl, + generics: &'a [&'a GenericParam], + custom: &'a Custom<'a>, + ) -> Self { + let exec_variants = MsgVariants::new( + source.as_variants(), + MsgType::Exec, + generics, + &source.generics.where_clause, + ); + + let query_variants = MsgVariants::new( + source.as_variants(), + MsgType::Query, + generics, + &source.generics.where_clause, + ); + + let instantiate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Instantiate, + generics, + &source.generics.where_clause, + ); + + let migrate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Migrate, + generics, + &source.generics.where_clause, + ); + + Self { + source, + exec_variants, + query_variants, + instantiate_variants, + migrate_variants, + generics, + custom, + } + } + + pub fn emit(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { + source, + exec_variants, + query_variants, + instantiate_variants, + migrate_variants, + generics, + custom, + } = self; + + let contract_name = &source.self_ty; + let exec_generics = &exec_variants.used_generics; + let query_generics = &query_variants.used_generics; + let instantiate_generics = &instantiate_variants.used_generics; + let migrate_generics = &migrate_variants.used_generics; + + let bracket_generics = emit_bracketed_generics(generics); + let exec_bracketed_generics = emit_bracketed_generics(exec_generics); + let query_bracketed_generics = emit_bracketed_generics(query_generics); + let instantiate_bracketed_generics = emit_bracketed_generics(instantiate_generics); + let migrate_bracketed_generics = emit_bracketed_generics(migrate_generics); + + let migrate_type = match !migrate_variants.variants().is_empty() { + true => quote! { type Migrate = MigrateMsg #migrate_bracketed_generics; }, + false => quote! { type Migrate = #sylvia ::cw_std::Empty; }, + }; + let custom_query = custom.query_or_default(); + + quote! { + impl #bracket_generics #sylvia ::types::ContractApi for #contract_name { + type ContractExec = ContractExecMsg #exec_bracketed_generics; + type ContractQuery = ContractQueryMsg #query_bracketed_generics; + type Exec = ExecMsg #exec_bracketed_generics; + type Query = QueryMsg #query_bracketed_generics; + type Instantiate = InstantiateMsg #instantiate_bracketed_generics; + #migrate_type + type Remote<'remote> = Remote<'remote>; + type Querier<'querier> = BoundQuerier<'querier, #custom_query >; + } + } + } +} + +pub struct InterfaceApi<'a> { exec_variants: MsgVariants<'a, GenericParam>, query_variants: MsgVariants<'a, GenericParam>, generics: &'a [&'a GenericParam], } -impl<'a> InterfaceMessages<'a> { +impl<'a> InterfaceApi<'a> { pub fn new(source: &'a ItemTrait, generics: &'a [&'a GenericParam]) -> Self { let exec_variants = MsgVariants::new( source.as_variants(), @@ -1487,11 +1588,11 @@ impl<'a> InterfaceMessages<'a> { }; quote! { - pub struct InterfaceTypes #bracket_generics { + pub struct Api #bracket_generics { #phantom } - impl #bracket_generics #sylvia ::types::InterfaceMessages for InterfaceTypes #bracket_generics { + impl #bracket_generics #sylvia ::types::InterfaceApi for Api #bracket_generics { type Exec = ExecMsg #exec_bracketed_generics; type Query = QueryMsg #query_bracketed_generics; } diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs index 3ea3ad8e..15e788fe 100644 --- a/sylvia-derive/src/multitest.rs +++ b/sylvia-derive/src/multitest.rs @@ -323,7 +323,8 @@ where }; let bracketed_generics = emit_bracketed_generics(generics); - let interface_enum = quote! { < #module sv::InterfaceTypes #bracketed_generics as #sylvia ::types::InterfaceMessages> }; + let interface_enum = + quote! { < #module sv::Api #bracketed_generics as #sylvia ::types::InterfaceApi> }; let exec_methods = exec_variants.emit_interface_multitest_proxy_methods( &custom_msg, diff --git a/sylvia-derive/src/remote.rs b/sylvia-derive/src/remote.rs index bfe63420..76fcdf1a 100644 --- a/sylvia-derive/src/remote.rs +++ b/sylvia-derive/src/remote.rs @@ -33,13 +33,11 @@ impl<'a> Remote<'a> { #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] pub struct Remote<'a>(std::borrow::Cow<'a, #sylvia ::cw_std::Addr>); - impl Remote<'static> { + impl<'a> Remote<'a> { pub fn new(addr: #sylvia ::cw_std::Addr) -> Self { Self(std::borrow::Cow::Owned(addr)) } - } - impl<'a> Remote<'a> { pub fn borrowed(addr: &'a #sylvia ::cw_std::Addr) -> Self { Self(std::borrow::Cow::Borrowed(addr)) } diff --git a/sylvia/src/types.rs b/sylvia/src/types.rs index 059ede46..d5c3d330 100644 --- a/sylvia/src/types.rs +++ b/sylvia/src/types.rs @@ -105,7 +105,18 @@ pub struct SvCustomMsg; impl cosmwasm_std::CustomMsg for SvCustomMsg {} -pub trait InterfaceMessages { +pub trait InterfaceApi { type Exec; type Query; } + +pub trait ContractApi { + type Instantiate; + type Query; + type Exec; + type ContractQuery; + type ContractExec; + type Migrate; + type Querier<'querier>; + type Remote<'remote>; +} diff --git a/sylvia/tests/api.rs b/sylvia/tests/api.rs new file mode 100644 index 00000000..e9c39a68 --- /dev/null +++ b/sylvia/tests/api.rs @@ -0,0 +1,127 @@ +use cosmwasm_std::{Response, StdResult}; +use std::marker::PhantomData; + +use sylvia::types::{CustomMsg, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx}; +use sylvia_derive::contract; + +pub struct SomeContract { + _phantom: PhantomData<(Instantiate, Query, Exec, Migrate, Ret)>, +} + +#[contract] +impl SomeContract +where + Instantiate: CustomMsg + 'static, + Query: CustomMsg + 'static, + Exec: CustomMsg + 'static, + Migrate: CustomMsg + 'static, + Ret: CustomMsg + 'static, +{ + pub const fn new() -> Self { + Self { + _phantom: PhantomData, + } + } + + #[msg(instantiate)] + pub fn instantiate(&self, _ctx: InstantiateCtx, _param: Instantiate) -> StdResult { + Ok(Response::new()) + } + + #[msg(exec)] + pub fn exec(&self, _ctx: ExecCtx, _param: Exec) -> StdResult { + Ok(Response::new()) + } + + #[msg(query)] + pub fn query(&self, _ctx: QueryCtx, _param: Query) -> StdResult { + Ok(Response::new()) + } + + #[msg(migrate)] + pub fn migrate(&self, _ctx: MigrateCtx, _param: Migrate) -> StdResult { + Ok(Response::new()) + } +} + +#[cfg(test)] +mod tests { + use crate::SomeContract; + use cosmwasm_std::{testing::mock_dependencies, Addr, QuerierWrapper}; + use sylvia::types::{ContractApi, SvCustomMsg}; + + #[test] + fn api() { + let owner = Addr::unchecked("owner"); + + let _: crate::sv::InstantiateMsg = as ContractApi>::Instantiate::new( + SvCustomMsg + ); + + let exec: crate::sv::ExecMsg = as ContractApi>::Exec::exec( + SvCustomMsg + ); + + let query: crate::sv::QueryMsg = as ContractApi>::Query::query( + SvCustomMsg + ); + + let _: crate::sv::ContractExecMsg = as ContractApi>::ContractExec::SomeContract( + exec + ); + + let _: crate::sv::ContractQueryMsg = as ContractApi>::ContractQuery::SomeContract( + query + ); + + let _: crate::sv::Remote<'_> = as ContractApi>::Remote::new(owner.clone()); + + let deps = mock_dependencies(); + let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier); + let _: crate::sv::BoundQuerier<'_, cosmwasm_std::Empty> = as ContractApi>::Querier::borrowed( + &owner, &querier_wrapper + ); + } +} From 58ad1ac9d63fdec169f581a2d02bbefaf6c7c562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Wed, 25 Oct 2023 19:03:22 +0200 Subject: [PATCH 15/15] feat: Support generic types in entry points --- .../generic_contract/src/contract.rs | 30 +++++++++--- .../src/custom_and_generic.rs | 4 +- .../contracts/generic_contract/src/cw1.rs | 4 +- .../contracts/generic_contract/src/generic.rs | 4 +- .../generic_iface_on_contract/src/contract.rs | 4 ++ sylvia-derive/src/interfaces.rs | 2 +- sylvia-derive/src/lib.rs | 5 +- sylvia-derive/src/message.rs | 49 ++++++++++++------- sylvia-derive/src/parser.rs | 45 ++++++++++++++--- 9 files changed, 111 insertions(+), 36 deletions(-) diff --git a/examples/contracts/generic_contract/src/contract.rs b/examples/contracts/generic_contract/src/contract.rs index 4fffc93e..12f605f9 100644 --- a/examples/contracts/generic_contract/src/contract.rs +++ b/examples/contracts/generic_contract/src/contract.rs @@ -1,4 +1,5 @@ use cosmwasm_std::{Reply, Response, StdResult}; +use cw_storage_plus::Item; use serde::de::DeserializeOwned; use serde::Deserialize; use sylvia::types::{ @@ -6,32 +7,48 @@ use sylvia::types::{ }; use sylvia::{contract, schemars}; -pub struct GenericContract( - std::marker::PhantomData<( +#[cfg(not(feature = "library"))] +use sylvia::entry_points; + +pub struct GenericContract< + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + FieldType, +> { + _field: Item<'static, FieldType>, + _phantom: std::marker::PhantomData<( InstantiateParam, ExecParam, QueryParam, MigrateParam, RetType, )>, -); +} +#[cfg_attr(not(feature = "library"), entry_points(generics))] #[contract] #[messages(cw1 as Cw1: custom(msg))] #[messages(generic as Generic: custom(msg))] #[messages(custom_and_generic as CustomAndGeneric)] #[sv::custom(msg=SvCustomMsg)] -impl - GenericContract +impl + GenericContract where for<'msg_de> InstantiateParam: CustomMsg + Deserialize<'msg_de> + 'msg_de, ExecParam: CustomMsg + DeserializeOwned + 'static, QueryParam: CustomMsg + DeserializeOwned + 'static, MigrateParam: CustomMsg + DeserializeOwned + 'static, RetType: CustomMsg + DeserializeOwned + 'static, + FieldType: 'static, { pub const fn new() -> Self { - Self(std::marker::PhantomData) + Self { + _field: Item::new("field"), + _phantom: std::marker::PhantomData, + } } #[msg(instantiate)] @@ -92,6 +109,7 @@ mod tests { SvCustomMsg, super::SvCustomMsg, super::SvCustomMsg, + String, _, > = CodeId::store_code(&app); diff --git a/examples/contracts/generic_contract/src/custom_and_generic.rs b/examples/contracts/generic_contract/src/custom_and_generic.rs index d1ce0b12..2dfadcd0 100644 --- a/examples/contracts/generic_contract/src/custom_and_generic.rs +++ b/examples/contracts/generic_contract/src/custom_and_generic.rs @@ -6,7 +6,7 @@ use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; #[contract(module = crate::contract)] #[messages(custom_and_generic as CustomAndGeneric)] #[sv::custom(msg=sylvia::types::SvCustomMsg)] -impl +impl CustomAndGeneric for crate::contract::GenericContract< InstantiateParam, @@ -14,6 +14,7 @@ impl QueryParam, MigrateParam, RetType, + FieldType, > { type Error = StdError; @@ -52,6 +53,7 @@ mod tests { SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg, + String, _, >::store_code(&app); diff --git a/examples/contracts/generic_contract/src/cw1.rs b/examples/contracts/generic_contract/src/cw1.rs index 4c5c125c..410bd13d 100644 --- a/examples/contracts/generic_contract/src/cw1.rs +++ b/examples/contracts/generic_contract/src/cw1.rs @@ -6,13 +6,14 @@ use sylvia::types::{ExecCtx, QueryCtx}; #[contract(module = crate::contract)] #[messages(cw1 as Cw1)] #[sv::custom(msg=sylvia::types::SvCustomMsg)] -impl Cw1 +impl Cw1 for crate::contract::GenericContract< InstantiateParam, ExecParam, QueryParam, MigrateParam, RetType, + FieldType, > { type Error = StdError; @@ -49,6 +50,7 @@ mod tests { SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg, + String, _, >::store_code(&app); diff --git a/examples/contracts/generic_contract/src/generic.rs b/examples/contracts/generic_contract/src/generic.rs index 8504c8ae..27176d94 100644 --- a/examples/contracts/generic_contract/src/generic.rs +++ b/examples/contracts/generic_contract/src/generic.rs @@ -6,7 +6,7 @@ use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; #[contract(module = crate::contract)] #[messages(generic as Generic)] #[sv::custom(msg=SvCustomMsg)] -impl +impl Generic for crate::contract::GenericContract< InstantiateParam, @@ -14,6 +14,7 @@ impl QueryParam, MigrateParam, RetType, + FieldType, > { type Error = StdError; @@ -58,6 +59,7 @@ mod tests { SvCustomMsg, SvCustomMsg, sylvia::types::SvCustomMsg, + String, _, > = CodeId::store_code(&app); diff --git a/examples/contracts/generic_iface_on_contract/src/contract.rs b/examples/contracts/generic_iface_on_contract/src/contract.rs index 0a94398b..a8f92805 100644 --- a/examples/contracts/generic_iface_on_contract/src/contract.rs +++ b/examples/contracts/generic_iface_on_contract/src/contract.rs @@ -2,8 +2,12 @@ use cosmwasm_std::{Response, StdResult}; use sylvia::types::{InstantiateCtx, SvCustomMsg}; use sylvia::{contract, schemars}; +#[cfg(not(feature = "library"))] +use sylvia::entry_points; + pub struct NonGenericContract; +#[cfg_attr(not(feature = "library"), entry_points)] #[contract] #[messages(generic as Generic: custom(msg))] #[messages(custom_and_generic as CustomAndGeneric)] diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs index 0e75edd1..fbfe9718 100644 --- a/sylvia-derive/src/interfaces.rs +++ b/sylvia-derive/src/interfaces.rs @@ -159,7 +159,7 @@ impl Interfaces { quote! {} }; - let type_name = msg_ty.as_accessor_name(); + let type_name = msg_ty.as_accessor_name(false); quote! { <#module ::sv::Api #generics as #sylvia ::types::InterfaceApi> :: #type_name :: response_schemas_impl() } diff --git a/sylvia-derive/src/lib.rs b/sylvia-derive/src/lib.rs index a002c044..a5c6ca8e 100644 --- a/sylvia-derive/src/lib.rs +++ b/sylvia-derive/src/lib.rs @@ -258,9 +258,10 @@ pub fn entry_points(attr: TokenStream, item: TokenStream) -> TokenStream { #[cfg(not(tarpaulin_include))] fn entry_points_impl(attr: TokenStream2, item: TokenStream2) -> TokenStream2 { - fn inner(_attr: TokenStream2, item: TokenStream2) -> syn::Result { + fn inner(attr: TokenStream2, item: TokenStream2) -> syn::Result { + let attrs: parser::EntryPointArgs = parse2(attr)?; let input: ItemImpl = parse2(item)?; - let expanded = EntryPoints::new(&input).emit(); + let expanded = EntryPoints::new(&input, attrs).emit(); Ok(quote! { #input diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index fceb8c58..9a67a37f 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -3,7 +3,7 @@ use crate::crate_module; use crate::interfaces::Interfaces; use crate::parser::{ parse_associated_custom_type, parse_struct_message, ContractErrorAttr, ContractMessageAttr, - Custom, MsgAttr, MsgType, OverrideEntryPoints, + Custom, EntryPointArgs, MsgAttr, MsgType, OverrideEntryPoints, }; use crate::strip_generics::StripGenerics; use crate::utils::{ @@ -16,11 +16,12 @@ use proc_macro_error::emit_error; use quote::{quote, ToTokens}; use syn::fold::Fold; use syn::parse::{Parse, Parser}; +use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::visit::Visit; use syn::{ - parse_quote, Attribute, GenericParam, Ident, ItemImpl, ItemTrait, Pat, PatType, Path, - ReturnType, Signature, TraitItem, Type, WhereClause, WherePredicate, + parse_quote, Attribute, GenericArgument, GenericParam, Ident, ItemImpl, ItemTrait, Pat, + PatType, Path, ReturnType, Signature, Token, TraitItem, Type, WhereClause, WherePredicate, }; /// Representation of single struct message @@ -747,7 +748,7 @@ impl<'a> MsgVariant<'a> { let bracketed_generics = emit_bracketed_generics(generics); let interface_enum = quote! { < #module sv::Api #bracketed_generics as #sylvia ::types::InterfaceApi> }; - let type_name = msg_ty.as_accessor_name(); + let type_name = msg_ty.as_accessor_name(false); let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); match msg_ty { @@ -790,7 +791,7 @@ impl<'a> MsgVariant<'a> { } = self; let params = fields.iter().map(|field| field.emit_method_field()); - let type_name = msg_ty.as_accessor_name(); + let type_name = msg_ty.as_accessor_name(false); let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); match msg_ty { @@ -1023,12 +1024,9 @@ where custom_query: &Type, name: &Type, error: &Type, + contract_generics: &Option>, ) -> TokenStream { - let Self { - used_generics, - msg_ty, - .. - } = self; + let Self { msg_ty, .. } = self; let sylvia = crate_module(); let resp_type = match msg_ty { @@ -1038,16 +1036,19 @@ where let params = msg_ty.emit_ctx_params(custom_query); let values = msg_ty.emit_ctx_values(); let ep_name = msg_ty.emit_ep_name(); - let msg_name = msg_ty.emit_msg_name(true); - let bracketed_generics = emit_bracketed_generics(used_generics); + let bracketed_generics = match &contract_generics { + Some(generics) => quote! { ::< #generics > }, + None => quote! {}, + }; + let associated_name = msg_ty.as_accessor_name(true); quote! { #[#sylvia ::cw_std::entry_point] pub fn #ep_name ( #params , - msg: sv:: #msg_name #bracketed_generics, + msg: < #name < #contract_generics > as #sylvia ::types::ContractApi> :: #associated_name, ) -> Result<#resp_type, #error> { - msg.dispatch(&#name ::new() , ( #values )).map_err(Into::into) + msg.dispatch(&#name #bracketed_generics ::new() , ( #values )).map_err(Into::into) } } } @@ -1608,10 +1609,11 @@ pub struct EntryPoints<'a> { override_entry_points: OverrideEntryPoints, generics: Vec<&'a GenericParam>, where_clause: &'a Option, + attrs: EntryPointArgs, } impl<'a> EntryPoints<'a> { - pub fn new(source: &'a ItemImpl) -> Self { + pub fn new(source: &'a ItemImpl, attrs: EntryPointArgs) -> Self { let sylvia = crate_module(); let name = StripGenerics.fold_type(*source.self_ty.clone()); let override_entry_points = OverrideEntryPoints::new(&source.attrs); @@ -1643,6 +1645,7 @@ impl<'a> EntryPoints<'a> { override_entry_points, generics, where_clause, + attrs, } } @@ -1655,6 +1658,7 @@ impl<'a> EntryPoints<'a> { override_entry_points, generics, where_clause, + attrs, } = self; let sylvia = crate_module(); @@ -1683,6 +1687,10 @@ impl<'a> EntryPoints<'a> { .iter() .map(|variant| variant.function_name.clone()) .next(); + let contract_generics = match &attrs.generics { + Some(generics) => quote! { ::< #generics > }, + None => quote! {}, + }; #[cfg(not(tarpaulin_include))] { @@ -1696,6 +1704,7 @@ impl<'a> EntryPoints<'a> { &custom_query, name, error, + &attrs.generics, ), }, ); @@ -1706,7 +1715,13 @@ impl<'a> EntryPoints<'a> { let migrate = if migrate_not_overridden && migrate_variants.get_only_variant().is_some() { - migrate_variants.emit_default_entry_point(&custom_msg, &custom_query, name, error) + migrate_variants.emit_default_entry_point( + &custom_msg, + &custom_query, + name, + error, + &attrs.generics, + ) } else { quote! {} }; @@ -1722,7 +1737,7 @@ impl<'a> EntryPoints<'a> { env: #sylvia ::cw_std::Env, msg: #sylvia ::cw_std::Reply, ) -> Result<#sylvia ::cw_std::Response < #custom_msg >, #error> { - #name ::new(). #reply((deps, env).into(), msg).map_err(Into::into) + #name #contract_generics ::new(). #reply((deps, env).into(), msg).map_err(Into::into) } }, _ => quote! {}, diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs index 709ed404..569e9f83 100644 --- a/sylvia-derive/src/parser.rs +++ b/sylvia-derive/src/parser.rs @@ -13,9 +13,10 @@ use syn::{ use crate::crate_module; use crate::strip_generics::StripGenerics; -/// Parser arguments for `contract` macro +/// Parsed arguments for `contract` macro pub struct ContractArgs { - /// Module name wrapping generated messages, by default no additional module is created + /// Module in which contract impl block is defined. + /// Used only while implementing `Interface` on `Contract`. pub module: Option, } @@ -46,6 +47,31 @@ impl Parse for ContractArgs { } } +/// Parsed arguments for `entry_points` macro +pub struct EntryPointArgs { + /// Types used in place of contracts generics. + pub generics: Option>, +} + +impl Parse for EntryPointArgs { + fn parse(input: ParseStream) -> Result { + if input.is_empty() { + return Ok(Self { generics: None }); + } + + let path: Path = input.parse()?; + + let generics = match path.segments.last() { + Some(segment) if segment.ident == "generics" => Some(extract_generics_from_path(&path)), + _ => return Err(Error::new(path.span(), "Expected `generics`")), + }; + + let _: Nothing = input.parse()?; + + Ok(Self { generics }) + } +} + /// Type of message to be generated #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum MsgType { @@ -158,11 +184,16 @@ impl MsgType { } } - pub fn as_accessor_name(&self) -> Option { + pub fn as_accessor_name(&self, is_wrapper: bool) -> Option { match self { + MsgType::Exec if is_wrapper => Some(parse_quote! { ContractExec }), + MsgType::Query if is_wrapper => Some(parse_quote! { ContractQuery }), + MsgType::Instantiate => Some(parse_quote! { Instantiate }), MsgType::Exec => Some(parse_quote! { Exec }), MsgType::Query => Some(parse_quote! { Query }), - _ => None, + MsgType::Migrate => Some(parse_quote! { Migrate }), + MsgType::Sudo => Some(parse_quote! { Sudo }), + MsgType::Reply => Some(parse_quote! { Reply }), } } } @@ -295,7 +326,7 @@ fn interface_has_custom(content: ParseStream) -> Result { Ok(customs) } -fn extract_generics_from_path(module: &mut Path) -> Punctuated { +fn extract_generics_from_path(module: &Path) -> Punctuated { let generics = module.segments.last().map(|segment| { match segment.arguments.clone(){ PathArguments::AngleBracketed(generics) => { @@ -322,8 +353,8 @@ impl Parse for ContractMessageAttr { let content; parenthesized!(content in input); - let mut module = content.parse()?; - let generics = extract_generics_from_path(&mut module); + let module = content.parse()?; + let generics = extract_generics_from_path(&module); let module = StripGenerics.fold_path(module); let _: Token![as] = content.parse()?;