diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs index 40ee4392..9733c3ed 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 @@ -107,28 +100,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)), @@ -140,13 +151,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 4398f6e8..926e9b74 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -968,7 +968,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(); @@ -1002,7 +1002,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! { @@ -1018,7 +1018,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 0b768755..d4eee93f 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 { diff --git a/sylvia/tests/generics.rs b/sylvia/tests/generics.rs index 88d25fcc..92913ebe 100644 --- a/sylvia/tests/generics.rs +++ b/sylvia/tests/generics.rs @@ -34,9 +34,14 @@ pub mod cw1_contract { use sylvia::types::InstantiateCtx; use sylvia_derive::contract; + use crate::{ExternalMsg, ExternalQuery}; + pub struct Cw1Contract; #[contract] + #[messages(crate::cw1, as Cw1)] + /// Required if interface returns generic `Response` + #[sv::custom(msg=ExternalMsg)] impl Cw1Contract { pub const fn new() -> Self { Self @@ -91,12 +96,11 @@ impl cosmwasm_std::CustomQuery for ExternalQuery {} #[cfg(test)] mod tests { + use crate::cw1::{InterfaceTypes, Querier as Cw1Querier}; + 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::types::InterfaceMessages; + #[test] fn construct_messages() { let contract = Addr::unchecked("contract"); @@ -110,9 +114,13 @@ mod tests { 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 {}); + // Construct messages with Interface extension let _ = as InterfaceMessages>::Query::some_query(