From c653e88df1cd0ae9e8864d6a73f8778922a0f7b1 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] 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 + ); + } +}