diff --git a/MIGRATING.md b/MIGRATING.md index e31b641a..b75c1a84 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -11,7 +11,7 @@ The `, custom(msg=..., query=...)` part is now not supported and can be safely r ```diff -#[entry_points(generics, custom(msg=Empty, query=Empty))] -+#[entry_points(generics)] ++#[entry_points(generics)] #[contract] #[sv::custom(msg=E, query=Q)] impl CounterContract @@ -24,15 +24,14 @@ where ### Removed `InterfaceApi` trait -The `InterfaceApi` trait has been removed in favor of the `InterfaceMessagesApi`. +The `InterfaceApi` trait has been deprecated in favor of the `InterfaceMessagesApi`. +It will be removed in the `2.0.0` release. ```diff -let _ = ::Query::query_something(); +let _ = ::Query::query_something(); ``` -Note: the `InterfaceMessagesApi` does not expose Querier type. - ## 1.0.2 -> 1.1.0 ### Generics in `sv::messages` not required diff --git a/examples/interfaces/custom-and-generic/src/lib.rs b/examples/interfaces/custom-and-generic/src/lib.rs index d24a7f7d..97637a0a 100644 --- a/examples/interfaces/custom-and-generic/src/lib.rs +++ b/examples/interfaces/custom-and-generic/src/lib.rs @@ -69,9 +69,11 @@ pub trait CustomAndGeneric { } #[cfg(test)] +#[allow(deprecated)] mod tests { use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::{Addr, CosmosMsg, Empty, QuerierWrapper}; + use sylvia::types::InterfaceApi; use crate::sv::Querier; @@ -232,5 +234,130 @@ mod tests { CosmosMsg::Custom(SvCustomMsg {}), CosmosMsg::Custom(SvCustomMsg {}), ); + + let _ = as super::sv::InterfaceMessagesApi>::Querier::borrowed( + &contract, &querier_wrapper + ); + + // Construct messages with InterfaceApi + let _ = as InterfaceApi>::Query::custom_generic_query_one( + SvCustomMsg {}, SvCustomMsg {} + ); + + let _ = as InterfaceApi>::Query::custom_generic_query_two( + SvCustomMsg {}, SvCustomMsg {} + ); + + let _ = as InterfaceApi>::Exec::custom_generic_execute_one( + vec![CosmosMsg::Custom(SvCustomMsg {})], + vec![CosmosMsg::Custom(SvCustomMsg {})], + ); + + let _ = as InterfaceApi>::Exec::custom_generic_execute_two( + vec![CosmosMsg::Custom(SvCustomMsg {})], + vec![CosmosMsg::Custom(SvCustomMsg {})], + ); + + let _ = as InterfaceApi>::Sudo::custom_generic_sudo_one( + CosmosMsg::Custom(SvCustomMsg {}), + CosmosMsg::Custom(SvCustomMsg {}), + ); + + let _ = as InterfaceApi>::Sudo::custom_generic_sudo_two( + CosmosMsg::Custom(SvCustomMsg {}), + CosmosMsg::Custom(SvCustomMsg {}), + ); } } diff --git a/examples/interfaces/generic/src/lib.rs b/examples/interfaces/generic/src/lib.rs index 174ad87f..b2e1dc3e 100644 --- a/examples/interfaces/generic/src/lib.rs +++ b/examples/interfaces/generic/src/lib.rs @@ -68,9 +68,11 @@ pub trait Generic { } #[cfg(test)] +#[allow(deprecated)] mod tests { use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::{Addr, CosmosMsg, Empty, QuerierWrapper}; + use sylvia::types::InterfaceApi; use crate::sv::Querier; @@ -209,6 +211,7 @@ mod tests { CosmosMsg::Custom(SvCustomMsg {}), CosmosMsg::Custom(SvCustomMsg {}), ); + let _ = as super::sv::InterfaceMessagesApi>::Querier::borrowed( + &contract, &querier_wrapper + ); + + // Construct messages with InterfaceApi + let _ = as InterfaceApi>::Query::generic_query_one(SvCustomMsg {}, SvCustomMsg {}); + + let _ = as InterfaceApi>::Query::generic_query_two(SvCustomMsg {}, SvCustomMsg {}); + + let _ = as InterfaceApi>::Exec::generic_exec_one( + vec![CosmosMsg::Custom(SvCustomMsg {})], + vec![CosmosMsg::Custom(SvCustomMsg {})], + ); + + let _ = as InterfaceApi>::Exec::generic_exec_two( + vec![CosmosMsg::Custom(SvCustomMsg {})], + vec![CosmosMsg::Custom(SvCustomMsg {})], + ); + + let _ = as InterfaceApi>::Sudo::generic_sudo_one( + CosmosMsg::Custom(SvCustomMsg {}), + CosmosMsg::Custom(SvCustomMsg {}), + ); + + let _ = as InterfaceApi>::Sudo::generic_sudo_two( + CosmosMsg::Custom(SvCustomMsg {}), + CosmosMsg::Custom(SvCustomMsg {}), + ); } } diff --git a/sylvia-derive/src/interface.rs b/sylvia-derive/src/interface.rs index 8909452b..5f956cc9 100644 --- a/sylvia-derive/src/interface.rs +++ b/sylvia-derive/src/interface.rs @@ -96,7 +96,7 @@ impl<'a> InterfaceInput<'a> { let Self { associated_types, item, - .. + custom, } = self; let messages = self.emit_messages(); let associated_names: Vec<_> = associated_types @@ -113,7 +113,7 @@ impl<'a> InterfaceInput<'a> { let querier = Querier::new(&query_variants, associated_types, &item.ident).emit_querier_trait(); - let interface_messages = Api::new(item, associated_types).emit(); + let interface_messages = Api::new(item, custom, associated_types).emit(); let multitest_helpers = self.emit_multitest_helpers(); diff --git a/sylvia-derive/src/interface/communication/api.rs b/sylvia-derive/src/interface/communication/api.rs index 082b5e2a..1288c9b5 100644 --- a/sylvia-derive/src/interface/communication/api.rs +++ b/sylvia-derive/src/interface/communication/api.rs @@ -1,5 +1,6 @@ +use crate::crate_module; use crate::parser::variant_descs::AsVariantDescs; -use crate::parser::MsgType; +use crate::parser::{Custom, MsgType}; use crate::types::associated_types::{AssociatedTypes, ItemType}; use crate::types::msg_variant::MsgVariants; use proc_macro2::TokenStream; @@ -13,23 +14,33 @@ use syn::ItemTrait; /// It ease the dispatch of generic types. pub struct Api<'a> { source: &'a ItemTrait, + custom: &'a Custom, associated_types: &'a AssociatedTypes<'a>, } impl<'a> Api<'a> { - pub fn new(source: &'a ItemTrait, associated_types: &'a AssociatedTypes<'a>) -> Self { + pub fn new( + source: &'a ItemTrait, + custom: &'a Custom, + associated_types: &'a AssociatedTypes<'a>, + ) -> Self { Self { source, + custom, associated_types, } } pub fn emit(&self) -> TokenStream { + let sylvia = crate_module(); let Self { source, + custom, associated_types, } = self; + let where_clause = &self.associated_types.as_where_clause(); + let custom_query = custom.query_or_default(); let interface_name = &source.ident; let generics: Vec<_> = associated_types .without_error() @@ -58,23 +69,46 @@ impl<'a> Api<'a> { let query_generics = &query_variants.used_generics(); let sudo_generics = &sudo_variants.used_generics(); + let phantom = if !generics.is_empty() { + quote! { + _phantom: std::marker::PhantomData<( #(#generics,)* )>, + } + } else { + quote! {} + }; + quote! { pub trait InterfaceMessagesApi { type Exec; type Query; type Sudo; + type Querier<'querier>; } impl InterfaceMessagesApi for Contract { type Exec = ExecMsg < #(:: #exec_generics,)* >; type Query = QueryMsg < #(:: #query_generics,)* >; type Sudo = SudoMsg < #(:: #sudo_generics ,)* >; + type Querier<'querier> = #sylvia ::types::BoundQuerier<'querier, #custom_query, Contract >; } impl<'sv_iface_msg_api, Error, #(#generics),*> InterfaceMessagesApi for dyn #interface_name < Error = Error, #(#generics = #generics,)* > + 'sv_iface_msg_api { type Exec = ExecMsg < #(#exec_generics,)* >; type Query = QueryMsg < #(#query_generics,)* >; type Sudo = SudoMsg < #(#sudo_generics,)* >; + type Querier<'querier> = #sylvia ::types::BoundQuerier<'querier, #custom_query, Self >; + } + + pub struct Api < #(#generics,)* > { + #phantom + } + + #[allow(deprecated)] + impl < #(#generics,)* > #sylvia ::types::InterfaceApi for Api < #(#generics,)* > #where_clause { + type Exec = ExecMsg < #(#exec_generics,)* >; + type Query = QueryMsg < #(#query_generics,)* >; + type Sudo = SudoMsg < #(#sudo_generics,)* >; + type Querier<'querier, Contract> = #sylvia ::types::BoundQuerier<'querier, #custom_query, Contract >; } } } diff --git a/sylvia-derive/src/parser/entry_point.rs b/sylvia-derive/src/parser/entry_point.rs index 584eb926..0a610aa3 100644 --- a/sylvia-derive/src/parser/entry_point.rs +++ b/sylvia-derive/src/parser/entry_point.rs @@ -1,9 +1,11 @@ use proc_macro2::TokenStream as TokenStream2; -use proc_macro_error::emit_error; -use syn::parse::{Error, Nothing, Parse, ParseStream}; +use proc_macro_error::{emit_error, emit_warning}; +use syn::parse::{Error, Nothing, Parse, ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::{parse2, GenericArgument, Path, Result, Token}; +use syn::{parenthesized, parse2, GenericArgument, Path, Result, Token}; + +use crate::parser::Custom; use super::extract_generics_from_path; @@ -40,6 +42,28 @@ impl Parse for EntryPointArgs { _ => return Err(Error::new(generics.span(), "Expected `generics`.")), }; + let comma: Option = input.parse().ok(); + if comma.is_some() { + emit_warning!( + comma.span(), "Deprecated `, custom(msg=.., query=..)` found."; + note = "You can safely remove this parameter as `entry_points` macro does not require it anymore." + ); + + // Parse custom attribute to not break semver + let custom: Option = input.parse().ok(); + match custom { + Some(custom) + if custom.get_ident().map(|custom| custom.to_string()) + == Some("custom".to_owned()) => + { + let content; + parenthesized!(content in input); + let _ = Custom::parse.parse2(content.parse()?); + } + _ => (), + }; + } + let _: Nothing = input.parse()?; Ok(entry_points_args) diff --git a/sylvia/src/types.rs b/sylvia/src/types.rs index 30a98ec8..0c0e9378 100644 --- a/sylvia/src/types.rs +++ b/sylvia/src/types.rs @@ -562,6 +562,18 @@ pub trait CustomQuery: cosmwasm_std::CustomQuery + DeserializeOwned + JsonSchema impl CustomQuery for T where T: cosmwasm_std::CustomQuery + DeserializeOwned + JsonSchema {} +/// Api trait for easier access to generated types and messages. +#[deprecated( + since = "1.2.1", + note = "InterfaceApi will be removed in 2.0.0. Please use generated sv::InterfaceMessagesApi instead." +)] +pub trait InterfaceApi { + type Exec; + type Query; + type Sudo; + type Querier<'querier, Contract>; +} + /// Api trait for easier access to generated types and messages. pub trait ContractApi { type Instantiate; diff --git a/sylvia/tests/interface_impl.rs b/sylvia/tests/interface_impl.rs index 88d5427c..ac638f8e 100644 --- a/sylvia/tests/interface_impl.rs +++ b/sylvia/tests/interface_impl.rs @@ -1,138 +1,178 @@ -use cosmwasm_std::{Response, StdError, StdResult}; -use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx, SudoCtx}; +use cosmwasm_std::{Empty, Response, StdError, StdResult}; +use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, InstantiateCtx, QueryCtx, SudoCtx}; use sylvia::{contract, entry_points}; mod interface1 { use cosmwasm_std::{Response, StdError, StdResult}; use sylvia::interface; - use sylvia::types::{ExecCtx, QueryCtx, SudoCtx}; + use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, QueryCtx, SudoCtx}; #[interface] - #[sv::custom(msg=cosmwasm_std::Empty, query=cosmwasm_std::Empty)] pub trait SylviaInterface1 { type Error: From; + type ExecC: CustomMsg; + type QueryC: CustomQuery; #[sv::msg(exec)] - fn interface1_method_exec(&self, _ctx: ExecCtx) -> StdResult; + fn interface1_method_exec( + &self, + _ctx: ExecCtx, + ) -> StdResult>; #[sv::msg(query)] - fn interface1_method_query(&self, _ctx: QueryCtx) -> StdResult; + fn interface1_method_query( + &self, + _ctx: QueryCtx, + ) -> StdResult>; #[sv::msg(sudo)] - fn interface1_method_sudo(&self, _ctx: SudoCtx) -> StdResult; + fn interface1_method_sudo( + &self, + _ctx: SudoCtx, + ) -> StdResult>; } } mod interface2 { use cosmwasm_std::{Response, StdError, StdResult}; use sylvia::interface; - use sylvia::types::{ExecCtx, QueryCtx, SudoCtx}; + use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, QueryCtx, SudoCtx}; #[interface] - #[sv::custom(msg=cosmwasm_std::Empty, query=cosmwasm_std::Empty)] pub trait SylviaInterface2 { type Error: From; + type ExecC: CustomMsg; + type QueryC: CustomQuery; #[sv::msg(exec)] - fn interface2_method_exec(&self, _ctx: ExecCtx) -> StdResult; + fn interface2_method_exec( + &self, + _ctx: ExecCtx, + ) -> StdResult>; #[sv::msg(query)] - fn interface2_method_query(&self, _ctx: QueryCtx) -> StdResult; + fn interface2_method_query( + &self, + _ctx: QueryCtx, + ) -> StdResult>; #[sv::msg(sudo)] - fn interface2_method_sudo(&self, _ctx: SudoCtx) -> StdResult; + fn interface2_method_sudo( + &self, + _ctx: SudoCtx, + ) -> StdResult>; } } -pub struct Contract; +pub struct Contract { + _phantom: std::marker::PhantomData<(E, Q)>, +} -#[entry_points] +// Check that the macro expansion won't fail due to deprecated `, custom` parameter. +#[entry_points(generics, custom(msg=Empty, query=Empty))] #[contract] #[sv::messages(interface1)] #[sv::messages(interface2)] -impl Contract { +#[sv::custom(msg=E, query=Q)] +impl Contract +where + E: CustomMsg + 'static, + Q: CustomQuery + 'static, +{ pub fn new() -> Self { - Self + Self { + _phantom: Default::default(), + } } #[sv::msg(instantiate)] - fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult { + fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult> { Ok(Response::new()) } #[sv::msg(exec)] - fn contract_method_exec(&self, _ctx: ExecCtx) -> StdResult { + fn contract_method_exec(&self, _ctx: ExecCtx) -> StdResult> { Ok(Response::new()) } #[sv::msg(query)] - fn contract_method_query(&self, _ctx: QueryCtx) -> StdResult { + fn contract_method_query(&self, _ctx: QueryCtx) -> StdResult> { Ok(Response::new()) } #[sv::msg(sudo)] - fn contract_method_sudo(&self, _ctx: SudoCtx) -> StdResult { + fn contract_method_sudo(&self, _ctx: SudoCtx) -> StdResult> { Ok(Response::new()) } } -impl interface1::SylviaInterface1 for Contract { +impl interface1::SylviaInterface1 for Contract +where + E: CustomMsg + 'static, + Q: CustomQuery + 'static, +{ type Error = StdError; + type ExecC = E; + type QueryC = Q; - fn interface1_method_exec(&self, _ctx: ExecCtx) -> StdResult { + fn interface1_method_exec(&self, _ctx: ExecCtx) -> StdResult> { Ok(Response::new()) } - fn interface1_method_query(&self, _ctx: QueryCtx) -> StdResult { + fn interface1_method_query(&self, _ctx: QueryCtx) -> StdResult> { Ok(Response::new()) } - fn interface1_method_sudo(&self, _ctx: SudoCtx) -> StdResult { + fn interface1_method_sudo(&self, _ctx: SudoCtx) -> StdResult> { Ok(Response::new()) } } -impl interface2::SylviaInterface2 for Contract { +impl interface2::SylviaInterface2 for Contract +where + E: CustomMsg + 'static, + Q: CustomQuery + 'static, +{ type Error = StdError; + type ExecC = E; + type QueryC = Q; - fn interface2_method_exec(&self, _ctx: ExecCtx) -> StdResult { + fn interface2_method_exec(&self, _ctx: ExecCtx) -> StdResult> { Ok(Response::new()) } - fn interface2_method_query(&self, _ctx: QueryCtx) -> StdResult { + fn interface2_method_query(&self, _ctx: QueryCtx) -> StdResult> { Ok(Response::new()) } - fn interface2_method_sudo(&self, _ctx: SudoCtx) -> StdResult { + fn interface2_method_sudo(&self, _ctx: SudoCtx) -> StdResult> { Ok(Response::new()) } } -impl Default for Contract { - fn default() -> Self { - Self::new() - } -} - #[test] fn check_from_trait_implementations() { - let _ = - sv::ContractExecMsg::from(interface1::sv::SylviaInterface1ExecMsg::Interface1MethodExec {}); - let _ = sv::ContractQueryMsg::from( + let _ = sv::ContractExecMsg::::from( + interface1::sv::SylviaInterface1ExecMsg::Interface1MethodExec {}, + ); + let _ = sv::ContractQueryMsg::::from( interface1::sv::SylviaInterface1QueryMsg::Interface1MethodQuery {}, ); - let _ = - sv::ContractSudoMsg::from(interface1::sv::SylviaInterface1SudoMsg::Interface1MethodSudo {}); + let _ = sv::ContractSudoMsg::::from( + interface1::sv::SylviaInterface1SudoMsg::Interface1MethodSudo {}, + ); - let _ = - sv::ContractExecMsg::from(interface2::sv::SylviaInterface2ExecMsg::Interface2MethodExec {}); - let _ = sv::ContractQueryMsg::from( + let _ = sv::ContractExecMsg::::from( + interface2::sv::SylviaInterface2ExecMsg::Interface2MethodExec {}, + ); + let _ = sv::ContractQueryMsg::::from( interface2::sv::SylviaInterface2QueryMsg::Interface2MethodQuery {}, ); - let _ = - sv::ContractSudoMsg::from(interface2::sv::SylviaInterface2SudoMsg::Interface2MethodSudo {}); + let _ = sv::ContractSudoMsg::::from( + interface2::sv::SylviaInterface2SudoMsg::Interface2MethodSudo {}, + ); - let _ = sv::ContractExecMsg::from(sv::ExecMsg::ContractMethodExec {}); - let _ = sv::ContractQueryMsg::from(sv::QueryMsg::ContractMethodQuery {}); - let _ = sv::ContractSudoMsg::from(sv::SudoMsg::ContractMethodSudo {}); + let _ = sv::ContractExecMsg::::from(sv::ExecMsg::ContractMethodExec {}); + let _ = sv::ContractQueryMsg::::from(sv::QueryMsg::ContractMethodQuery {}); + let _ = sv::ContractSudoMsg::::from(sv::SudoMsg::ContractMethodSudo {}); }