From 6cedd94258fb8f9a05bfedc50852c4425733d6af Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:48:14 +0200 Subject: [PATCH 01/13] test: Generalize whitelist --- .../contracts/cw1-whitelist/src/whitelist.rs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/contracts/cw1-whitelist/src/whitelist.rs b/examples/contracts/cw1-whitelist/src/whitelist.rs index ed003146..900e5c6c 100644 --- a/examples/contracts/cw1-whitelist/src/whitelist.rs +++ b/examples/contracts/cw1-whitelist/src/whitelist.rs @@ -1,17 +1,21 @@ use cosmwasm_std::{Empty, Order, Response, StdResult}; -use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, QueryCtx}; use whitelist::responses::AdminListResponse; use whitelist::Whitelist; use crate::contract::Cw1WhitelistContract; use crate::error::ContractError; -impl Whitelist for Cw1WhitelistContract { +impl Whitelist for Cw1WhitelistContract +where + E: CustomMsg + 'static, + Q: CustomQuery + 'static, +{ type Error = ContractError; - type ExecC = Empty; - type QueryC = Empty; + type ExecC = E; + type QueryC = Q; - fn freeze(&self, ctx: ExecCtx) -> Result { + fn freeze(&self, ctx: ExecCtx) -> Result, ContractError> { if !self.is_admin(ctx.deps.as_ref(), &ctx.info.sender) { return Err(ContractError::Unauthorized); } @@ -24,9 +28,9 @@ impl Whitelist for Cw1WhitelistContract { fn update_admins( &self, - ctx: ExecCtx, + ctx: ExecCtx, mut admins: Vec, - ) -> Result { + ) -> Result, ContractError> { if !self.is_admin(ctx.deps.as_ref(), &ctx.info.sender) { return Err(ContractError::Unauthorized); } @@ -81,7 +85,7 @@ impl Whitelist for Cw1WhitelistContract { Ok(resp) } - fn admin_list(&self, ctx: QueryCtx) -> StdResult { + fn admin_list(&self, ctx: QueryCtx) -> StdResult { let admins: Result<_, _> = self .admins .keys(ctx.deps.storage, None, None, Order::Ascending) From 8451be1bb7759663f902b2287a8cf7ae75ffc0e6 Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:48:31 +0200 Subject: [PATCH 02/13] test: Generalize cw1 --- examples/contracts/cw1-whitelist/src/cw1.rs | 26 ++++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/examples/contracts/cw1-whitelist/src/cw1.rs b/examples/contracts/cw1-whitelist/src/cw1.rs index 2dc4e424..6455b5ea 100644 --- a/examples/contracts/cw1-whitelist/src/cw1.rs +++ b/examples/contracts/cw1-whitelist/src/cw1.rs @@ -1,17 +1,25 @@ -use cosmwasm_std::{Addr, CosmosMsg, Empty, Response, StdResult}; +use cosmwasm_std::{Addr, CosmosMsg, Response, StdResult}; use cw1::{CanExecuteResp, Cw1}; -use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, QueryCtx}; use crate::contract::Cw1WhitelistContract; use crate::error::ContractError; -impl Cw1 for Cw1WhitelistContract { +impl Cw1 for Cw1WhitelistContract +where + E: CustomMsg + 'static, + Q: CustomQuery + 'static, +{ type Error = ContractError; - type ExecC = Empty; - type QueryC = Empty; - type CosmosCustomMsg = Empty; + type ExecC = E; + type QueryC = Q; + type CosmosCustomMsg = E; - fn execute(&self, ctx: ExecCtx, msgs: Vec) -> Result { + fn execute( + &self, + ctx: ExecCtx, + msgs: Vec>, + ) -> Result, ContractError> { if !self.is_admin(ctx.deps.as_ref(), &ctx.info.sender) { return Err(ContractError::Unauthorized); } @@ -24,9 +32,9 @@ impl Cw1 for Cw1WhitelistContract { fn can_execute( &self, - ctx: QueryCtx, + ctx: QueryCtx, sender: String, - _msg: CosmosMsg, + _msg: CosmosMsg, ) -> StdResult { let resp = CanExecuteResp { can_execute: self.is_admin(ctx.deps, &Addr::unchecked(sender)), From a245783913bc4766a085ec7f89cb22ba2890570b Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:48:55 +0200 Subject: [PATCH 03/13] test: Generalize Cw1WhitelistContract --- .../contracts/cw1-whitelist/src/contract.rs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/examples/contracts/cw1-whitelist/src/contract.rs b/examples/contracts/cw1-whitelist/src/contract.rs index 8797c0da..25de76c7 100644 --- a/examples/contracts/cw1-whitelist/src/contract.rs +++ b/examples/contracts/cw1-whitelist/src/contract.rs @@ -3,7 +3,7 @@ use cosmwasm_std::{Addr, Deps, Empty, Response}; use cw2::set_contract_version; use cw_storage_plus::{Item, Map}; -use sylvia::types::InstantiateCtx; +use sylvia::types::{CustomMsg, CustomQuery, InstantiateCtx}; use sylvia::{contract, schemars}; #[cfg(not(feature = "library"))] @@ -12,30 +12,41 @@ use sylvia::entry_points; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -pub struct Cw1WhitelistContract { +#[cosmwasm_schema::cw_serde] +pub struct SvCustomQuery; +impl cosmwasm_std::CustomQuery for SvCustomQuery {} + +pub struct Cw1WhitelistContract { pub(crate) admins: Map<&'static Addr, Empty>, pub(crate) mutable: Item, + pub(crate) _phantom: std::marker::PhantomData<(E, Q)>, } -#[cfg_attr(not(feature = "library"), entry_points)] +#[cfg_attr(not(feature = "library"), entry_points(generics))] #[contract] #[sv::error(ContractError)] #[sv::messages(cw1 as Cw1)] #[sv::messages(whitelist as Whitelist)] -impl Cw1WhitelistContract { +#[sv::custom(msg=E, query=Q)] +impl Cw1WhitelistContract +where + E: CustomMsg + 'static, + Q: CustomQuery + 'static, +{ pub const fn new() -> Self { Self { admins: Map::new("admins"), mutable: Item::new("mutable"), + _phantom: std::marker::PhantomData, } } #[sv::msg(instantiate)] pub fn instantiate( &self, - ctx: InstantiateCtx, + ctx: InstantiateCtx, admins: Vec, mutable: bool, - ) -> Result { + ) -> Result, ContractError> { set_contract_version(ctx.deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; for admin in admins { @@ -48,7 +59,7 @@ impl Cw1WhitelistContract { Ok(Response::new()) } - pub fn is_admin(&self, deps: Deps, addr: &Addr) -> bool { + pub fn is_admin(&self, deps: Deps, addr: &Addr) -> bool { self.admins.has(deps.storage, addr) } } From 07fcdc65f19cfce29473419c6609c70d11dddfc9 Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:49:25 +0200 Subject: [PATCH 04/13] test: Use concrete types in schema --- examples/contracts/cw1-whitelist/src/bin/schema.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/contracts/cw1-whitelist/src/bin/schema.rs b/examples/contracts/cw1-whitelist/src/bin/schema.rs index 3096b127..afe477e4 100644 --- a/examples/contracts/cw1-whitelist/src/bin/schema.rs +++ b/examples/contracts/cw1-whitelist/src/bin/schema.rs @@ -1,12 +1,14 @@ use cosmwasm_schema::write_api; +use cosmwasm_std::Empty; use cw1_whitelist::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use cw1_whitelist::contract::SvCustomQuery; #[cfg(not(tarpaulin_include))] fn main() { write_api! { instantiate: InstantiateMsg, - execute: ContractExecMsg, - query: ContractQueryMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, } } From 948deecfc1cd50b09c20c05d9b6f858bcdce1a08 Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:25:31 +0200 Subject: [PATCH 05/13] test: Adapt tests --- .../contracts/cw1-whitelist/src/contract.rs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/contracts/cw1-whitelist/src/contract.rs b/examples/contracts/cw1-whitelist/src/contract.rs index 25de76c7..db588189 100644 --- a/examples/contracts/cw1-whitelist/src/contract.rs +++ b/examples/contracts/cw1-whitelist/src/contract.rs @@ -88,7 +88,7 @@ mod tests { let anyone = "anyone".into_addr(); - let contract = Cw1WhitelistContract::new(); + let contract: Cw1WhitelistContract = Cw1WhitelistContract::new(); // instantiate the contract let info = message_info(&anyone, &[]); @@ -185,7 +185,7 @@ mod tests { let bob = "bob".into_bech32(); let carl = "carl".into_bech32(); - let contract = Cw1WhitelistContract::new(); + let contract: Cw1WhitelistContract = Cw1WhitelistContract::new(); // instantiate the contract let info = message_info(&bob, &[]); @@ -240,7 +240,7 @@ mod tests { let anyone = "anyone".into_bech32(); - let contract = Cw1WhitelistContract::new(); + let contract: Cw1WhitelistContract = Cw1WhitelistContract::new(); // instantiate the contract let info = message_info(&anyone, &[]); @@ -304,7 +304,7 @@ mod tests { } mod msgs { - use cosmwasm_std::{from_json, to_json_binary, BankMsg}; + use cosmwasm_std::{from_json, to_json_binary, BankMsg, Empty}; use crate::contract::sv::{ContractExecMsg, ContractQueryMsg}; @@ -312,14 +312,14 @@ mod tests { fn freeze() { let original = whitelist::sv::ExecMsg::Freeze {}; let serialized = to_json_binary(&original).unwrap(); - let deserialized = from_json(serialized).unwrap(); + let deserialized: ContractExecMsg = from_json(serialized).unwrap(); assert_eq!(ContractExecMsg::Whitelist(original), deserialized); let json = br#"{ "freeze": {} }"#; - let deserialized = from_json(json).unwrap(); + let deserialized: ContractExecMsg = from_json(json).unwrap(); assert_eq!( ContractExecMsg::Whitelist(whitelist::sv::ExecMsg::Freeze {}), @@ -333,7 +333,7 @@ mod tests { admins: vec!["admin1".to_owned(), "admin2".to_owned()], }; let serialized = to_json_binary(&original).unwrap(); - let deserialized = from_json(serialized).unwrap(); + let deserialized: ContractExecMsg = from_json(serialized).unwrap(); assert_eq!(ContractExecMsg::Whitelist(original), deserialized); @@ -342,7 +342,7 @@ mod tests { "admins": ["admin1", "admin3"] } }"#; - let deserialized = from_json(json).unwrap(); + let deserialized: ContractExecMsg = from_json(json).unwrap(); assert_eq!( ContractExecMsg::Whitelist(whitelist::sv::ExecMsg::UpdateAdmins { @@ -356,14 +356,14 @@ mod tests { fn admin_list() { let original = whitelist::sv::QueryMsg::AdminList {}; let serialized = to_json_binary(&original).unwrap(); - let deserialized = from_json(serialized).unwrap(); + let deserialized: ContractQueryMsg = from_json(serialized).unwrap(); assert_eq!(ContractQueryMsg::Whitelist(original), deserialized); let json = br#"{ "admin_list": {} }"#; - let deserialized = from_json(json).unwrap(); + let deserialized: ContractQueryMsg = from_json(json).unwrap(); assert_eq!( ContractQueryMsg::Whitelist(whitelist::sv::QueryMsg::AdminList {}), @@ -381,7 +381,7 @@ mod tests { .into()], }; let serialized = to_json_binary(&original).unwrap(); - let deserialized = from_json(serialized).unwrap(); + let deserialized: ContractExecMsg = from_json(serialized).unwrap(); assert_eq!(ContractExecMsg::Cw1(original), deserialized); } @@ -396,7 +396,7 @@ mod tests { .into(), }; let serialized = to_json_binary(&original).unwrap(); - let deserialized = from_json(serialized).unwrap(); + let deserialized: ContractQueryMsg = from_json(serialized).unwrap(); assert_eq!(ContractQueryMsg::Cw1(original), deserialized); } } From b66185aa5fcbd3209cb435b6a4b1ca521c6341b9 Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:37:19 +0200 Subject: [PATCH 06/13] test: Generalize subkeys whitelist --- .../contracts/cw1-subkeys/src/whitelist.rs | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/examples/contracts/cw1-subkeys/src/whitelist.rs b/examples/contracts/cw1-subkeys/src/whitelist.rs index c0e8af0d..7da9331c 100644 --- a/examples/contracts/cw1-subkeys/src/whitelist.rs +++ b/examples/contracts/cw1-subkeys/src/whitelist.rs @@ -1,27 +1,35 @@ -use cosmwasm_std::{Empty, Response, StdResult}; -use sylvia::types::{ExecCtx, QueryCtx}; +use cosmwasm_std::{Response, StdResult}; +use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, QueryCtx}; use whitelist::responses::AdminListResponse; use whitelist::Whitelist; use crate::contract::Cw1SubkeysContract; use crate::error::ContractError; -impl Whitelist for Cw1SubkeysContract { +impl Whitelist for Cw1SubkeysContract +where + E: CustomMsg + 'static, + Q: CustomQuery + 'static, +{ type Error = ContractError; - type ExecC = Empty; - type QueryC = Empty; + type ExecC = E; + type QueryC = Q; - fn freeze(&self, ctx: ExecCtx) -> Result { + fn freeze(&self, ctx: ExecCtx) -> Result, Self::Error> { self.whitelist.freeze(ctx).map_err(From::from) } - fn update_admins(&self, ctx: ExecCtx, admins: Vec) -> Result { + fn update_admins( + &self, + ctx: ExecCtx, + admins: Vec, + ) -> Result, Self::Error> { self.whitelist .update_admins(ctx, admins) .map_err(From::from) } - fn admin_list(&self, ctx: QueryCtx) -> StdResult { + fn admin_list(&self, ctx: QueryCtx) -> StdResult { self.whitelist.admin_list(ctx) } } From d89227ad25abb2e866a43c3771a1522820c505ed Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:37:33 +0200 Subject: [PATCH 07/13] test: Generalize subkeys cw1 --- examples/contracts/cw1-subkeys/src/cw1.rs | 26 +++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/contracts/cw1-subkeys/src/cw1.rs b/examples/contracts/cw1-subkeys/src/cw1.rs index 2e3a1d2e..fee4d136 100644 --- a/examples/contracts/cw1-subkeys/src/cw1.rs +++ b/examples/contracts/cw1-subkeys/src/cw1.rs @@ -1,21 +1,25 @@ -use cosmwasm_std::{ensure, Addr, Empty, Response, StdResult}; +use cosmwasm_std::{ensure, Addr, CosmosMsg, Response, StdResult}; use cw1::{CanExecuteResp, Cw1}; -use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, QueryCtx}; use crate::contract::Cw1SubkeysContract; use crate::error::ContractError; -impl Cw1 for Cw1SubkeysContract { +impl Cw1 for Cw1SubkeysContract +where + E: CustomMsg + 'static, + Q: CustomQuery + 'static, +{ type Error = ContractError; - type ExecC = Empty; - type QueryC = Empty; - type CosmosCustomMsg = Empty; + type ExecC = E; + type QueryC = Q; + type CosmosCustomMsg = E; fn execute( &self, - ctx: ExecCtx, - msgs: Vec, - ) -> Result { + ctx: ExecCtx, + msgs: Vec>, + ) -> Result, Self::Error> { let authorized: StdResult<_> = msgs.iter().try_fold(true, |acc, msg| { Ok(acc & self.is_authorized(ctx.deps.as_ref(), &ctx.env, &ctx.info.sender, msg)?) }); @@ -31,9 +35,9 @@ impl Cw1 for Cw1SubkeysContract { fn can_execute( &self, - ctx: QueryCtx, + ctx: QueryCtx, sender: String, - msg: cosmwasm_std::CosmosMsg, + msg: CosmosMsg, ) -> StdResult { let sender = Addr::unchecked(sender); From f8dd0128e7df43f058ae214eb386be17287ac93c Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:37:53 +0200 Subject: [PATCH 08/13] test: Generalize Cw1SubkeysContract --- .../contracts/cw1-subkeys/src/contract.rs | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/examples/contracts/cw1-subkeys/src/contract.rs b/examples/contracts/cw1-subkeys/src/contract.rs index f25192c3..c9b79783 100644 --- a/examples/contracts/cw1-subkeys/src/contract.rs +++ b/examples/contracts/cw1-subkeys/src/contract.rs @@ -6,7 +6,7 @@ use cw1_whitelist::contract::Cw1WhitelistContract; use cw2::set_contract_version; use cw_storage_plus::{Bound, Map}; use cw_utils::Expiration; -use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx}; +use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, InstantiateCtx, QueryCtx}; use sylvia::{contract, schemars}; #[cfg(not(feature = "library"))] @@ -25,18 +25,31 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const MAX_LIMIT: u32 = 30; const DEFAULT_LIMIT: u32 = 10; -pub struct Cw1SubkeysContract { - pub(crate) whitelist: Cw1WhitelistContract, +#[cosmwasm_schema::cw_serde] +pub struct SvCustomMsg; +impl cosmwasm_std::CustomMsg for SvCustomMsg {} + +#[cosmwasm_schema::cw_serde] +pub struct SvCustomQuery; +impl cosmwasm_std::CustomQuery for SvCustomQuery {} + +pub struct Cw1SubkeysContract { + pub(crate) whitelist: Cw1WhitelistContract, pub(crate) permissions: Map<&'static Addr, Permissions>, pub(crate) allowances: Map<&'static Addr, Allowance>, } -#[cfg_attr(not(feature = "library"), entry_points)] +#[cfg_attr(not(feature = "library"), entry_points(generics))] #[contract] #[sv::error(ContractError)] #[sv::messages(cw1 as Cw1)] #[sv::messages(whitelist as Whitelist)] -impl Cw1SubkeysContract { +#[sv::custom(msg=E, query=Q)] +impl Cw1SubkeysContract +where + E: CustomMsg + 'static, + Q: CustomQuery + 'static, +{ pub const fn new() -> Self { Self { whitelist: Cw1WhitelistContract::new(), @@ -48,10 +61,10 @@ impl Cw1SubkeysContract { #[sv::msg(instantiate)] pub fn instantiate( &self, - mut ctx: InstantiateCtx, + mut ctx: InstantiateCtx, admins: Vec, mutable: bool, - ) -> Result { + ) -> Result, ContractError> { let result = self.whitelist.instantiate(ctx.branch(), admins, mutable)?; set_contract_version(ctx.deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; Ok(result) @@ -60,11 +73,11 @@ impl Cw1SubkeysContract { #[sv::msg(exec)] pub fn increase_allowance( &self, - ctx: ExecCtx, + ctx: ExecCtx, spender: String, amount: Coin, expires: Option, - ) -> Result { + ) -> Result, ContractError> { ensure!( self.whitelist.is_admin(ctx.deps.as_ref(), &ctx.info.sender), ContractError::Unauthorized @@ -110,11 +123,11 @@ impl Cw1SubkeysContract { #[sv::msg(exec)] pub fn decrease_allowance( &self, - ctx: ExecCtx, + ctx: ExecCtx, spender: String, amount: Coin, expires: Option, - ) -> Result { + ) -> Result, ContractError> { ensure!( self.whitelist.is_admin(ctx.deps.as_ref(), &ctx.info.sender), ContractError::Unauthorized @@ -161,10 +174,10 @@ impl Cw1SubkeysContract { #[sv::msg(exec)] pub fn set_permissions( &self, - ctx: ExecCtx, + ctx: ExecCtx, spender: String, permissions: Permissions, - ) -> Result { + ) -> Result, ContractError> { ensure!( self.whitelist.is_admin(ctx.deps.as_ref(), &ctx.info.sender), ContractError::Unauthorized @@ -184,7 +197,7 @@ impl Cw1SubkeysContract { } #[sv::msg(query)] - pub fn allowance(&self, ctx: QueryCtx, spender: String) -> StdResult { + pub fn allowance(&self, ctx: QueryCtx, spender: String) -> StdResult { // we can use unchecked here as it is a query - bad value means a miss, we never write it let spender = Addr::unchecked(spender); let allow = self @@ -197,7 +210,7 @@ impl Cw1SubkeysContract { } #[sv::msg(query)] - pub fn permissions(&self, ctx: QueryCtx, spender: String) -> StdResult { + pub fn permissions(&self, ctx: QueryCtx, spender: String) -> StdResult { let spender = Addr::unchecked(spender); let permissions = self .permissions @@ -210,7 +223,7 @@ impl Cw1SubkeysContract { #[sv::msg(query)] pub fn all_allowances( &self, - ctx: QueryCtx, + ctx: QueryCtx, start_after: Option, limit: Option, ) -> StdResult { @@ -246,7 +259,7 @@ impl Cw1SubkeysContract { #[sv::msg(query)] pub fn all_permissions( &self, - ctx: QueryCtx, + ctx: QueryCtx, start_after: Option, limit: Option, ) -> StdResult { @@ -272,10 +285,10 @@ impl Cw1SubkeysContract { pub fn is_authorized( &self, - deps: Deps, + deps: Deps, env: &Env, sender: &Addr, - msg: &CosmosMsg, + msg: &CosmosMsg, ) -> StdResult { if self.whitelist.is_admin(deps, sender) { return Ok(true); From 69099dd73459dcc5c5e08eb3b266bb9554361b7c Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:38:10 +0200 Subject: [PATCH 09/13] test: Use concrete types in subkeys schema --- examples/contracts/cw1-subkeys/src/bin/schema.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/contracts/cw1-subkeys/src/bin/schema.rs b/examples/contracts/cw1-subkeys/src/bin/schema.rs index c5129235..7f0173c2 100644 --- a/examples/contracts/cw1-subkeys/src/bin/schema.rs +++ b/examples/contracts/cw1-subkeys/src/bin/schema.rs @@ -1,12 +1,13 @@ use cosmwasm_schema::write_api; use cw1_subkeys::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use cw1_subkeys::contract::{SvCustomMsg, SvCustomQuery}; #[cfg(not(tarpaulin_include))] fn main() { write_api! { instantiate: InstantiateMsg, - execute: ContractExecMsg, - query: ContractQueryMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, } } From 180161efb86e2b6c43ca48974e508fc109d91433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Mon, 29 Jul 2024 17:32:47 +0200 Subject: [PATCH 10/13] test: Allow usage of `ExecC` and `QueryC` in other params then `Response` and `Ctx` types --- examples/contracts/custom/src/cw1.rs | 1 - examples/contracts/cw1-subkeys/src/cw1.rs | 5 +- examples/contracts/cw1-whitelist/src/cw1.rs | 5 +- .../contracts/generic_contract/src/cw1.rs | 1 - .../generic_iface_on_contract/src/cw1.rs | 1 - .../contracts/generics_forwarded/src/cw1.rs | 1 - .../interfaces/custom-and-generic/src/lib.rs | 17 +- examples/interfaces/cw1/src/lib.rs | 5 +- sylvia-derive/src/interface.rs | 5 +- .../src/interface/communication/executor.rs | 4 +- .../src/interface/communication/querier.rs | 4 +- sylvia-derive/src/message.rs | 1446 +++++++++++++++++ sylvia-derive/src/types/associated_types.rs | 29 +- 13 files changed, 1476 insertions(+), 48 deletions(-) create mode 100644 sylvia-derive/src/message.rs diff --git a/examples/contracts/custom/src/cw1.rs b/examples/contracts/custom/src/cw1.rs index 17c24978..372eef9e 100644 --- a/examples/contracts/custom/src/cw1.rs +++ b/examples/contracts/custom/src/cw1.rs @@ -8,7 +8,6 @@ impl Cw1 for CustomContract { type Error = StdError; type ExecC = Empty; type QueryC = Empty; - type CosmosCustomMsg = Empty; fn execute(&self, _ctx: ExecCtx, _msgs: Vec) -> StdResult { Ok(Response::new()) diff --git a/examples/contracts/cw1-subkeys/src/cw1.rs b/examples/contracts/cw1-subkeys/src/cw1.rs index fee4d136..60bd5281 100644 --- a/examples/contracts/cw1-subkeys/src/cw1.rs +++ b/examples/contracts/cw1-subkeys/src/cw1.rs @@ -13,12 +13,11 @@ where type Error = ContractError; type ExecC = E; type QueryC = Q; - type CosmosCustomMsg = E; fn execute( &self, ctx: ExecCtx, - msgs: Vec>, + msgs: Vec>, ) -> Result, Self::Error> { let authorized: StdResult<_> = msgs.iter().try_fold(true, |acc, msg| { Ok(acc & self.is_authorized(ctx.deps.as_ref(), &ctx.env, &ctx.info.sender, msg)?) @@ -37,7 +36,7 @@ where &self, ctx: QueryCtx, sender: String, - msg: CosmosMsg, + msg: CosmosMsg, ) -> StdResult { let sender = Addr::unchecked(sender); diff --git a/examples/contracts/cw1-whitelist/src/cw1.rs b/examples/contracts/cw1-whitelist/src/cw1.rs index 6455b5ea..ef850a81 100644 --- a/examples/contracts/cw1-whitelist/src/cw1.rs +++ b/examples/contracts/cw1-whitelist/src/cw1.rs @@ -13,12 +13,11 @@ where type Error = ContractError; type ExecC = E; type QueryC = Q; - type CosmosCustomMsg = E; fn execute( &self, ctx: ExecCtx, - msgs: Vec>, + msgs: Vec>, ) -> Result, ContractError> { if !self.is_admin(ctx.deps.as_ref(), &ctx.info.sender) { return Err(ContractError::Unauthorized); @@ -34,7 +33,7 @@ where &self, ctx: QueryCtx, sender: String, - _msg: CosmosMsg, + _msg: CosmosMsg, ) -> StdResult { let resp = CanExecuteResp { can_execute: self.is_admin(ctx.deps, &Addr::unchecked(sender)), diff --git a/examples/contracts/generic_contract/src/cw1.rs b/examples/contracts/generic_contract/src/cw1.rs index 420f06f7..dc8b2075 100644 --- a/examples/contracts/generic_contract/src/cw1.rs +++ b/examples/contracts/generic_contract/src/cw1.rs @@ -35,7 +35,6 @@ impl< type Error = StdError; type ExecC = Empty; type QueryC = Empty; - type CosmosCustomMsg = Empty; fn execute(&self, _ctx: ExecCtx, _msgs: Vec) -> StdResult { Ok(Response::new()) diff --git a/examples/contracts/generic_iface_on_contract/src/cw1.rs b/examples/contracts/generic_iface_on_contract/src/cw1.rs index 746e6a17..c9cc5a99 100644 --- a/examples/contracts/generic_iface_on_contract/src/cw1.rs +++ b/examples/contracts/generic_iface_on_contract/src/cw1.rs @@ -6,7 +6,6 @@ impl Cw1 for crate::contract::NonGenericContract { type Error = StdError; type ExecC = Empty; type QueryC = Empty; - type CosmosCustomMsg = Empty; fn execute(&self, _ctx: ExecCtx, _msgs: Vec) -> StdResult { Ok(Response::new()) diff --git a/examples/contracts/generics_forwarded/src/cw1.rs b/examples/contracts/generics_forwarded/src/cw1.rs index 44b78d74..f4306463 100644 --- a/examples/contracts/generics_forwarded/src/cw1.rs +++ b/examples/contracts/generics_forwarded/src/cw1.rs @@ -59,7 +59,6 @@ where type Error = ContractError; type ExecC = Empty; type QueryC = Empty; - type CosmosCustomMsg = Empty; fn execute(&self, _ctx: ExecCtx, _msgs: Vec) -> Result { Ok(Response::new()) diff --git a/examples/interfaces/custom-and-generic/src/lib.rs b/examples/interfaces/custom-and-generic/src/lib.rs index f860b301..adfb57e9 100644 --- a/examples/interfaces/custom-and-generic/src/lib.rs +++ b/examples/interfaces/custom-and-generic/src/lib.rs @@ -79,6 +79,9 @@ mod tests { #[cosmwasm_schema::cw_serde] pub struct SvCustomMsg; impl cosmwasm_std::CustomMsg for SvCustomMsg {} + #[cosmwasm_schema::cw_serde] + pub struct SvCustomQuery; + impl cosmwasm_std::CustomQuery for SvCustomQuery {} #[test] fn construct_messages() { @@ -121,8 +124,8 @@ mod tests { Sudo2T = SvCustomMsg, Sudo3T = SvCustomMsg, Error = (), - ExecC = (), - QueryC = (), + ExecC = SvCustomMsg, + QueryC = SvCustomQuery, >, >::borrowed(&contract, &querier_wrapper); @@ -147,6 +150,8 @@ mod tests { SvCustomMsg, SvCustomMsg, SvCustomMsg, + SvCustomQuery, + SvCustomMsg, > as InterfaceApi>::Query::custom_generic_query_one( SvCustomMsg {}, SvCustomMsg {} ); @@ -162,6 +167,8 @@ mod tests { SvCustomMsg, SvCustomMsg, SvCustomMsg, + SvCustomQuery, + SvCustomMsg, > as InterfaceApi>::Exec::custom_generic_execute_one( vec![CosmosMsg::Custom(SvCustomMsg {})], vec![CosmosMsg::Custom(SvCustomMsg {})], @@ -178,6 +185,8 @@ mod tests { SvCustomMsg, SvCustomMsg, SvCustomMsg, + SvCustomQuery, + SvCustomMsg, > as InterfaceApi>::Exec::custom_generic_execute_two( vec![CosmosMsg::Custom(SvCustomMsg {})], vec![CosmosMsg::Custom(SvCustomMsg {})], @@ -194,6 +203,8 @@ mod tests { SvCustomMsg, SvCustomMsg, SvCustomMsg, + SvCustomQuery, + SvCustomMsg, > as InterfaceApi>::Sudo::custom_generic_sudo_one( CosmosMsg::Custom(SvCustomMsg {}), CosmosMsg::Custom(SvCustomMsg {}), @@ -210,6 +221,8 @@ mod tests { SvCustomMsg, SvCustomMsg, SvCustomMsg, + SvCustomQuery, + SvCustomMsg, > as InterfaceApi>::Sudo::custom_generic_sudo_one( CosmosMsg::Custom(SvCustomMsg {}), CosmosMsg::Custom(SvCustomMsg {}), diff --git a/examples/interfaces/cw1/src/lib.rs b/examples/interfaces/cw1/src/lib.rs index 8146a113..1f59e587 100644 --- a/examples/interfaces/cw1/src/lib.rs +++ b/examples/interfaces/cw1/src/lib.rs @@ -15,7 +15,6 @@ pub trait Cw1 { type Error: From; type ExecC: CustomMsg; type QueryC: CustomQuery; - type CosmosCustomMsg: CustomMsg; /// Execute requests the contract to re-dispatch all these messages with the /// contract's address as sender. Every implementation has it's own logic to @@ -24,7 +23,7 @@ pub trait Cw1 { fn execute( &self, ctx: ExecCtx, - msgs: Vec>, + msgs: Vec>, ) -> Result, Self::Error>; /// Checks permissions of the caller on this proxy. @@ -35,7 +34,7 @@ pub trait Cw1 { &self, ctx: QueryCtx, sender: String, - msg: CosmosMsg, + msg: CosmosMsg, ) -> StdResult; } diff --git a/sylvia-derive/src/interface.rs b/sylvia-derive/src/interface.rs index f17cad01..b75bbca3 100644 --- a/sylvia-derive/src/interface.rs +++ b/sylvia-derive/src/interface.rs @@ -88,7 +88,10 @@ impl<'a> InterfaceInput<'a> { custom, } = self; let messages = self.emit_messages(); - let associated_names: Vec<_> = associated_types.as_filtered_names().collect(); + let associated_names: Vec<_> = associated_types + .without_error() + .map(ItemType::as_name) + .collect(); let executor_variants = MsgVariants::new(item.as_variants(), MsgType::Exec, &associated_names, &None); diff --git a/sylvia-derive/src/interface/communication/executor.rs b/sylvia-derive/src/interface/communication/executor.rs index 74d44a8b..a01cc853 100644 --- a/sylvia-derive/src/interface/communication/executor.rs +++ b/sylvia-derive/src/interface/communication/executor.rs @@ -42,7 +42,7 @@ where } = self; let generics: Vec<_> = associated_types - .without_special() + .without_error() .map(ItemType::as_name) .collect(); let all_generics: Vec<_> = associated_types.all_names().collect(); @@ -65,7 +65,7 @@ where .variants() .map(|variant| variant.emit_executor_method_declaration()); - let types_declaration = associated_types.filtered().collect::>(); + let types_declaration = associated_types.without_error().collect::>(); let where_clause = associated_types.as_where_clause(); quote! { diff --git a/sylvia-derive/src/interface/communication/querier.rs b/sylvia-derive/src/interface/communication/querier.rs index 2156cd7b..81e8e471 100644 --- a/sylvia-derive/src/interface/communication/querier.rs +++ b/sylvia-derive/src/interface/communication/querier.rs @@ -42,7 +42,7 @@ where } = self; let generics: Vec<_> = associated_types - .without_special() + .without_error() .map(ItemType::as_name) .collect(); let all_generics: Vec<_> = associated_types.all_names().collect(); @@ -64,7 +64,7 @@ where .variants() .map(|variant| variant.emit_querier_method_declaration()); - let types_declaration = associated_types.filtered().collect::>(); + let types_declaration = associated_types.without_error().collect::>(); let where_clause = associated_types.as_where_clause(); quote! { diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs new file mode 100644 index 00000000..d9e342f9 --- /dev/null +++ b/sylvia-derive/src/message.rs @@ -0,0 +1,1446 @@ +use crate::associated_types::{AssociatedTypes, ItemType, EXEC_TYPE, QUERY_TYPE}; +use crate::check_generics::{CheckGenerics, GetPath}; +use crate::crate_module; +use crate::interfaces::Interfaces; +use crate::parser::attributes::{MsgAttrForwarding, VariantAttrForwarding}; +use crate::parser::{ + parse_associated_custom_type, ContractErrorAttr, Custom, EntryPointArgs, + FilteredOverrideEntryPoints, MsgAttr, MsgType, OverrideEntryPoint, ParsedSylviaAttributes, +}; +use crate::strip_generics::StripGenerics; +use crate::strip_self_path::StripSelfPath; +use crate::utils::{ + as_where_clause, emit_bracketed_generics, extract_return_type, filter_wheres, process_fields, + SvCasing, +}; +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, ToTokens}; +use syn::fold::Fold; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::visit::Visit; +use syn::{ + parse_quote, Attribute, GenericArgument, GenericParam, Ident, ImplItem, ImplItemFn, ItemImpl, + ItemTrait, Pat, PatType, ReturnType, Signature, Token, Type, WhereClause, WherePredicate, +}; + +/// Representation of single struct message +pub struct StructMessage<'a> { + contract_type: &'a Type, + fields: Vec>, + function_name: &'a Ident, + generics: Vec<&'a GenericParam>, + unused_generics: Vec<&'a GenericParam>, + wheres: Vec<&'a WherePredicate>, + full_where: Option<&'a WhereClause>, + result: &'a ReturnType, + msg_attr: MsgAttr, + custom: &'a Custom, + msg_attrs_to_forward: Vec, +} + +impl<'a> StructMessage<'a> { + /// Creates new struct message of given type from impl block + pub fn new( + source: &'a ItemImpl, + ty: MsgType, + generics: &'a [&'a GenericParam], + custom: &'a Custom, + ) -> Option> { + let mut generics_checker = CheckGenerics::new(generics); + + let contract_type = &source.self_ty; + + let parsed = Self::parse_struct_message(source, ty); + let (method, msg_attr) = parsed?; + + let function_name = &method.sig.ident; + let fields = process_fields(&method.sig, &mut generics_checker); + let (used_generics, unused_generics) = generics_checker.used_unused(); + let wheres = filter_wheres(&source.generics.where_clause, generics, &used_generics); + + let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter()) + .msg_attrs_forward + .into_iter() + .filter(|attr| attr.msg_type == ty) + .collect(); + + Some(Self { + contract_type, + fields, + function_name, + generics: used_generics, + unused_generics, + wheres, + full_where: source.generics.where_clause.as_ref(), + result: &method.sig.output, + msg_attr, + custom, + msg_attrs_to_forward, + }) + } + + fn parse_struct_message(source: &ItemImpl, ty: MsgType) -> Option<(&ImplItemFn, MsgAttr)> { + let mut methods = source.items.iter().filter_map(|item| match item { + ImplItem::Fn(method) => { + let attr = ParsedSylviaAttributes::new(method.attrs.iter()).msg_attr?; + if attr == ty { + Some((method, attr)) + } else { + None + } + } + _ => None, + }); + + let (method, msg_attr) = if let Some(method) = methods.next() { + method + } else { + if ty == MsgType::Instantiate { + emit_error!( + source.span(), "Missing instantiation message."; + note = source.span() => "`sylvia::contract` requires exactly one method marked with `#[sv::msg(instantiation)]` attribute." + ); + } + return None; + }; + + if let Some((obsolete, _)) = methods.next() { + emit_error!( + obsolete.span(), "More than one instantiation or migration message"; + note = method.span() => "Instantiation/Migration message previously defined here" + ); + } + Some((method, msg_attr)) + } + + pub fn emit(&self) -> TokenStream { + use MsgAttr::*; + + let instantiate_msg = Ident::new("InstantiateMsg", self.function_name.span()); + let migrate_msg = Ident::new("MigrateMsg", self.function_name.span()); + + match &self.msg_attr { + Instantiate { .. } => self.emit_struct(&instantiate_msg), + Migrate { .. } => self.emit_struct(&migrate_msg), + _ => { + emit_error!(Span::mixed_site(), "Invalid message type"); + quote! {} + } + } + } + + pub fn emit_struct(&self, name: &Ident) -> TokenStream { + let sylvia = crate_module(); + + let Self { + contract_type, + fields, + function_name, + generics, + unused_generics, + wheres, + full_where, + result, + msg_attr, + custom, + msg_attrs_to_forward, + } = self; + + let ctx_type = msg_attr + .msg_type() + .emit_ctx_type(&custom.query_or_default()); + let fields_names: Vec<_> = fields.iter().map(MsgField::name).collect(); + let parameters = fields.iter().map(|field| { + let name = field.name; + let ty = &field.ty; + quote! { #name : #ty} + }); + let fields = fields.iter().map(MsgField::emit); + + let where_clause = as_where_clause(wheres); + let generics = emit_bracketed_generics(generics); + let unused_generics = emit_bracketed_generics(unused_generics); + let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs); + + quote! { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] + #( #[ #msg_attrs_to_forward ] )* + #[serde(rename_all="snake_case")] + pub struct #name #generics { + #(pub #fields,)* + } + + impl #generics #name #generics #where_clause { + pub fn new(#(#parameters,)*) -> Self { + Self { #(#fields_names,)* } + } + + pub fn dispatch #unused_generics(self, contract: &#contract_type, ctx: #ctx_type) + #result #full_where + { + let Self { #(#fields_names,)* } = self; + contract.#function_name(Into::into(ctx), #(#fields_names,)*).map_err(Into::into) + } + } + } + } +} + +/// Representation of single enum message +pub struct EnumMessage<'a> { + source: &'a ItemTrait, + variants: MsgVariants<'a, Ident>, + associated_types: &'a AssociatedTypes<'a>, + msg_ty: MsgType, + resp_type: Type, + query_type: Type, + msg_attrs_to_forward: Vec, +} + +impl<'a> EnumMessage<'a> { + pub fn new( + source: &'a ItemTrait, + msg_ty: MsgType, + custom: &'a Custom, + variants: MsgVariants<'a, Ident>, + associated_types: &'a AssociatedTypes<'a>, + ) -> Self { + let associated_exec = parse_associated_custom_type(source, EXEC_TYPE); + let associated_query = parse_associated_custom_type(source, QUERY_TYPE); + + let resp_type = custom + .msg + .clone() + .or(associated_exec) + .unwrap_or_else(Custom::default_type); + + let query_type = custom + .query + .clone() + .or(associated_query) + .unwrap_or_else(Custom::default_type); + + let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter()) + .msg_attrs_forward + .into_iter() + .filter(|attr| attr.msg_type == msg_ty) + .collect(); + + Self { + source, + variants, + associated_types, + msg_ty, + resp_type, + query_type, + msg_attrs_to_forward, + } + } + + pub fn emit(&self) -> TokenStream { + let Self { + source, + variants, + associated_types, + msg_ty, + resp_type, + query_type, + msg_attrs_to_forward, + } = self; + + let trait_name = &source.ident; + let enum_name = msg_ty.emit_msg_name(); + let unique_enum_name = + Ident::new(&format!("{}{}", trait_name, enum_name), enum_name.span()); + + let match_arms = variants.emit_dispatch_legs(); + let mut msgs = variants.as_names_snake_cased(); + msgs.sort(); + let msgs_cnt = msgs.len(); + let variants_constructors = variants.emit_constructors(); + let msg_variants = variants.emit(); + + let ctx_type = msg_ty.emit_ctx_type(query_type); + let dispatch_type = msg_ty.emit_result_type(resp_type, &parse_quote!(ContractT::Error)); + + let used_generics = variants.used_generics(); + let unused_generics = variants.unused_generics(); + let where_predicates = associated_types + .without_error() + .map(ItemType::as_where_predicate); + let where_clause = variants.where_clause(); + let contract_predicate = associated_types.emit_contract_predicate(trait_name); + + let phantom_variant = variants.emit_phantom_variant(); + let phatom_match_arm = variants.emit_phantom_match_arm(); + let bracketed_used_generics = emit_bracketed_generics(used_generics); + + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), enum_name.span()); + let derive_call = msg_ty.emit_derive_call(); + let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs); + + quote! { + #[allow(clippy::derive_partial_eq_without_eq)] + #derive_call + #( #[ #msg_attrs_to_forward ] )* + #[serde(rename_all="snake_case")] + pub enum #unique_enum_name #bracketed_used_generics { + #(#msg_variants,)* + #phantom_variant + } + pub type #enum_name #bracketed_used_generics = #unique_enum_name #bracketed_used_generics; + + impl #bracketed_used_generics #unique_enum_name #bracketed_used_generics #where_clause { + pub fn dispatch(self, contract: &ContractT, ctx: #ctx_type) + -> #dispatch_type + where + #(#where_predicates,)* + #contract_predicate + { + use #unique_enum_name::*; + + match self { + #(#match_arms,)* + #phatom_match_arm + } + } + #(#variants_constructors)* + } + + pub const fn #messages_fn_name () -> [&'static str; #msgs_cnt] { + [#(#msgs,)*] + } + } + } +} + +/// Representation of single enum message +pub struct ContractEnumMessage<'a> { + variants: MsgVariants<'a, GenericParam>, + msg_ty: MsgType, + contract: &'a Type, + error: &'a ContractErrorAttr, + custom: &'a Custom, + where_clause: &'a Option, + msg_attrs_to_forward: Vec, +} + +impl<'a> ContractEnumMessage<'a> { + pub fn new( + source: &'a ItemImpl, + msg_ty: MsgType, + generics: &'a [&'a GenericParam], + error: &'a ContractErrorAttr, + custom: &'a Custom, + ) -> Self { + let where_clause = &source.generics.where_clause; + let variants = MsgVariants::new(source.as_variants(), msg_ty, generics, where_clause); + let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter()) + .msg_attrs_forward + .into_iter() + .filter(|attr| attr.msg_type == msg_ty) + .collect(); + + Self { + variants, + msg_ty, + contract: &source.self_ty, + error, + custom, + where_clause, + msg_attrs_to_forward, + } + } + + pub fn emit(&self) -> TokenStream { + let sylvia = crate_module(); + + let Self { + variants, + msg_ty, + contract, + error, + custom, + where_clause, + msg_attrs_to_forward, + .. + } = self; + + let enum_name = msg_ty.emit_msg_name(); + let match_arms = variants.emit_dispatch_legs(); + let unused_generics = variants.unused_generics(); + let bracketed_unused_generics = emit_bracketed_generics(unused_generics); + let used_generics = variants.used_generics(); + let bracketed_used_generics = emit_bracketed_generics(used_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 ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), &error.error); + + let derive_query = match msg_ty { + MsgType::Query => quote! { #sylvia ::cw_schema::QueryResponses }, + _ => quote! {}, + }; + + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span()); + + let phantom_variant = msg_ty.emit_phantom_variant(used_generics); + let phantom_match_arm = match !used_generics.is_empty() { + true => quote! { + _Phantom(_) => Err(#sylvia ::cw_std::StdError::generic_err("Phantom message should not be constructed.")).map_err(Into::into), + }, + false => quote! {}, + }; + let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs); + + quote! { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, #derive_query )] + #( #[ #msg_attrs_to_forward ] )* + #[serde(rename_all="snake_case")] + pub enum #enum_name #bracketed_used_generics { + #(#variants,)* + #phantom_variant + } + + impl #bracketed_used_generics #enum_name #bracketed_used_generics { + pub fn dispatch #bracketed_unused_generics (self, contract: &#contract, ctx: #ctx_type) -> #ret_type #where_clause { + use #enum_name::*; + + match self { + #(#match_arms,)* + #phantom_match_arm + } + } + + #(#variants_constructors)* + } + + pub const fn #messages_fn_name () -> [&'static str; #variants_cnt] { + [#(#variant_names,)*] + } + } + } +} + +/// Representation of whole message variant +#[derive(Debug)] +pub struct MsgVariant<'a> { + name: Ident, + function_name: &'a Ident, + fields: Vec>, + /// Type extracted only in case of `Query` and used in `cosmwasm_schema::QueryResponses` + /// `returns` attribute. + return_type: Option, + msg_type: MsgType, + attrs_to_forward: Vec, +} + +impl<'a> MsgVariant<'a> { + /// Creates new message variant from trait method + pub fn new( + sig: &'a Signature, + generics_checker: &mut CheckGenerics, + msg_attr: MsgAttr, + attrs_to_forward: Vec, + ) -> MsgVariant<'a> + where + Generic: GetPath + PartialEq, + { + let function_name = &sig.ident; + + let name = function_name.to_case(Case::UpperCamel); + let fields = process_fields(sig, generics_checker); + let msg_type = msg_attr.msg_type(); + + let return_type = if let MsgAttr::Query { resp_type, .. } = msg_attr { + match resp_type { + Some(resp_type) => { + let resp_type = parse_quote! { #resp_type }; + generics_checker.visit_type(&resp_type); + Some(resp_type) + } + None => { + let return_type = extract_return_type(&sig.output); + let stripped_return_type = StripSelfPath.fold_path(return_type.clone()); + generics_checker.visit_path(&stripped_return_type); + Some(parse_quote! { #return_type }) + } + } + } else { + None + }; + + Self { + name, + function_name, + fields, + return_type, + msg_type, + attrs_to_forward, + } + } + + /// Emits message variant + pub fn emit(&self) -> TokenStream { + let Self { + name, + fields, + msg_type, + return_type, + attrs_to_forward, + .. + } = self; + let fields = fields.iter().map(MsgField::emit); + let returns_attribute = msg_type.emit_returns_attribute(return_type); + let attrs_to_forward = attrs_to_forward.iter().map(|attr| &attr.attrs); + + quote! { + #returns_attribute + #( #[ #attrs_to_forward ] )* + #name { + #(#fields,)* + } + } + } + + /// 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) -> TokenStream { + let Self { + name, + fields, + function_name, + msg_type, + .. + } = self; + + let args: Vec<_> = fields + .iter() + .zip(1..) + .map(|(field, num)| Ident::new(&format!("field{}", num), field.name.span())) + .collect(); + + let fields = fields + .iter() + .map(|field| field.name) + .zip(args.clone()) + .map(|(field, num_field)| quote!(#field : #num_field)); + + let method_call = msg_type.emit_dispatch_leg(function_name, &args); + + quote! { + #name { + #(#fields,)* + } => #method_call + } + } + + /// Emits variants constructors. Constructors names are variants names in snake_case. + pub fn emit_variants_constructors(&self) -> TokenStream { + let Self { name, fields, .. } = self; + + let method_name = name.to_case(Case::Snake); + let parameters = fields.iter().map(MsgField::emit_method_field); + let arguments = fields.iter().map(MsgField::name); + + quote! { + pub fn #method_name( #(#parameters),*) -> Self { + Self :: #name { #(#arguments),* } + } + } + } + + 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() + } + + pub fn name(&self) -> &Ident { + &self.name + } + + pub fn fields(&self) -> &Vec { + &self.fields + } + + pub fn msg_type(&self) -> &MsgType { + &self.msg_type + } + + pub fn return_type(&self) -> &Option { + &self.return_type + } +} + +#[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> +where + Generic: GetPath + PartialEq + ToTokens, +{ + pub fn new( + source: VariantDescs<'a>, + msg_ty: MsgType, + all_generics: &'a [&'a Generic], + unfiltered_where_clause: &'a Option, + ) -> Self { + let mut generics_checker = CheckGenerics::new(all_generics); + let variants: Vec<_> = source + .filter_map(|variant_desc| { + let msg_attr: MsgAttr = variant_desc.attr_msg()?; + let attrs_to_forward = variant_desc.attrs_to_forward(); + + if msg_attr.msg_type() != msg_ty { + return None; + } + + Some(MsgVariant::new( + variant_desc.into_sig(), + &mut generics_checker, + msg_attr, + attrs_to_forward, + )) + }) + .collect(); + + 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, + } + } + + 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 + } + } + + pub fn variants(&self) -> impl Iterator { + self.variants.iter() + } + + pub fn used_generics(&self) -> &Vec<&'a Generic> { + &self.used_generics + } + + pub fn unused_generics(&self) -> &Vec<&'a Generic> { + &self.unused_generics + } + + pub fn emit_default_entry_point( + &self, + custom_msg: &Type, + custom_query: &Type, + name: &Type, + error: &Type, + contract_generics: &Option>, + ) -> TokenStream { + let Self { 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 bracketed_generics = match &contract_generics { + Some(generics) => quote! { ::< #generics > }, + None => quote! {}, + }; + let associated_name = msg_ty.as_accessor_wrapper_name(); + + quote! { + #[#sylvia ::cw_std::entry_point] + pub fn #ep_name ( + #params , + msg: < #name < #contract_generics > as #sylvia ::types::ContractApi> :: #associated_name, + ) -> Result<#resp_type, #error> { + msg.dispatch(&#name #bracketed_generics ::new() , ( #values )).map_err(Into::into) + } + } + } + + pub fn emit_phantom_match_arm(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { used_generics, .. } = self; + if used_generics.is_empty() { + return quote! {}; + } + quote! { + _Phantom(_) => Err(#sylvia ::cw_std::StdError::generic_err("Phantom message should not be constructed.")).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(&self) -> impl Iterator + '_ { + self.variants.iter().map(MsgVariant::emit) + } + + pub fn get_only_variant(&self) -> Option<&MsgVariant> { + self.variants.first() + } + + pub fn emit_phantom_variant(&self) -> TokenStream { + let Self { + msg_ty, + used_generics, + .. + } = self; + + if used_generics.is_empty() { + return quote! {}; + } + + let return_attr = match msg_ty { + MsgType::Query => quote! { #[returns((#(#used_generics,)*))] }, + _ => quote! {}, + }; + + quote! { + #[serde(skip)] + #return_attr + _Phantom(std::marker::PhantomData<( #(#used_generics,)* )>), + } + } +} + +/// Representation of single message variant field +#[derive(Debug)] +pub struct MsgField<'a> { + name: &'a Ident, + ty: &'a Type, + stripped_ty: Type, + attrs: &'a Vec, +} + +impl<'a> MsgField<'a> { + /// Creates new field from trait method argument + 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 => { + // TODO: Support pattern arguments, when decorated with argument with item + // name + // + // Eg. + // + // ``` + // fn exec_foo(&self, ctx: Ctx, #[sv::msg(name=metadata)] SomeData { addr, sender }: SomeData); + // ``` + // + // should expand to enum variant: + // + // ``` + // ExecFoo { + // metadata: SomeDaa + // } + // ``` + emit_error!(pat.span(), "Expected argument name, pattern occurred"); + None + } + }?; + + let ty = &item.ty; + let stripped_ty = StripSelfPath.fold_type((*item.ty).clone()); + let attrs = &item.attrs; + generics_checker.visit_type(&stripped_ty); + + Some(Self { + name, + ty, + stripped_ty, + attrs, + }) + } + + /// Emits message field + pub fn emit(&self) -> TokenStream { + let Self { + name, + stripped_ty, + attrs, + .. + } = self; + + quote! { + #(#attrs)* + #name: #stripped_ty + } + } + + /// Emits method field + pub fn emit_method_field(&self) -> TokenStream { + let Self { + name, stripped_ty, .. + } = self; + + quote! { + #name: #stripped_ty + } + } + + pub fn emit_method_field_folded(&self) -> TokenStream { + let Self { name, ty, .. } = self; + + quote! { + #name: #ty + } + } + + pub fn name(&self) -> &'a Ident { + self.name + } +} + +/// Glue message is the message composing Exec/Query messages from several traits +#[derive(Debug)] +pub struct GlueMessage<'a> { + source: &'a ItemImpl, + contract: &'a Type, + msg_ty: MsgType, + error: &'a ContractErrorAttr, + custom: &'a Custom, + interfaces: &'a Interfaces, +} + +impl<'a> GlueMessage<'a> { + pub fn new( + source: &'a ItemImpl, + msg_ty: MsgType, + error: &'a ContractErrorAttr, + custom: &'a Custom, + interfaces: &'a Interfaces, + ) -> Self { + GlueMessage { + source, + contract: &source.self_ty, + msg_ty, + error, + custom, + interfaces, + } + } + + pub fn emit(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { + source, + contract, + msg_ty, + error, + custom, + interfaces, + .. + } = self; + + let generics: Vec<_> = source.generics.params.iter().collect(); + let full_where_clause = &source.generics.where_clause; + let bracketed_wrapper_generics = emit_bracketed_generics(&generics); + + let contract_enum_name = msg_ty.emit_msg_wrapper_name(); + let enum_accessor = msg_ty.as_accessor_name(); + let contract_name = StripGenerics.fold_type((*contract).clone()); + + let variants = interfaces.emit_glue_message_variants(msg_ty, contract); + let types = interfaces.emit_glue_message_types(msg_ty, contract); + + 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 ( <#contract as #sylvia ::types::ContractApi> :: #enum_accessor ) }; + let mut messages_call = interfaces.emit_messages_call(msg_ty); + messages_call.push(quote! { &#messages_fn_name() }); + + let variants_cnt = messages_call.len(); + + let dispatch_arms = interfaces.emit_dispatch_arms(msg_ty); + + let dispatch_arm = + quote! {#contract_enum_name :: #contract_name (msg) => msg.dispatch(contract, ctx)}; + + let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(msg_ty); + + let contract_deserialization_attempt = quote! { + 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)), + Err(err) => return Err(D::Error::custom(err)).map(Self:: #contract_name ) + }; + } + }; + + 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.error); + + let mut response_schemas_calls = interfaces.emit_response_schemas_calls(msg_ty, contract); + response_schemas_calls + .push(quote! {<#contract as #sylvia ::types::ContractApi> :: #enum_accessor ::response_schemas_impl()}); + + let response_schemas = match msg_ty { + MsgType::Query => { + quote! { + #[cfg(not(target_arch = "wasm32"))] + impl #bracketed_wrapper_generics #sylvia ::cw_schema::QueryResponses for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { + fn response_schemas_impl() -> std::collections::BTreeMap { + let responses = [#(#response_schemas_calls),*]; + responses.into_iter().flatten().collect() + } + } + } + } + _ => { + quote! {} + } + }; + + let modules_names = interfaces.variants_modules(); + let variants_names = interfaces.variants_names(); + + quote! { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq)] + #[serde(rename_all="snake_case", untagged)] + pub enum #contract_enum_name #bracketed_wrapper_generics #full_where_clause { + #(#variants,)* + #contract_variant + } + + // `schemars` v0.8.16 requires every generic type to implement JsonSchema in + // order to use derive JsonSchema macro. The goal of that trait bound is to + // generate schema_name. Currently there's no way to provide such a name in an + // attribute, so Sylvia needs to implement this trait manually: + // + impl #bracketed_wrapper_generics #sylvia ::schemars::JsonSchema + for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { + + fn schema_name() -> std::string::String { + { + let res = format!( + "{0}", + std::any::type_name::() + ); + res + } + } + + fn json_schema( + gen: &mut #sylvia ::schemars::gen::SchemaGenerator, + ) -> #sylvia ::schemars::schema::Schema { + #sylvia ::schemars::schema::Schema::Object( #sylvia ::schemars::schema::SchemaObject { + subschemas: Some( + Box::new( #sylvia ::schemars::schema::SubschemaValidation { + any_of: Some( + <[_]>::into_vec( + Box::new([ + #(gen.subschema_for::<#types>(),)* + gen.subschema_for::< <#contract as #sylvia ::types::ContractApi> :: #enum_accessor >(), + ]), + ), + ), + ..Default::default() + }), + ), + ..Default::default() + }) + } + } + + impl #bracketed_wrapper_generics #contract_enum_name #bracketed_wrapper_generics #full_where_clause { + pub fn dispatch ( + self, + contract: &#contract, + ctx: #ctx_type, + ) -> #ret_type #full_where_clause { + const _: () = { + let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*]; + #sylvia ::utils::assert_no_intersection(msgs); + }; + + match self { + #(#dispatch_arms,)* + #dispatch_arm + } + } + } + + #response_schemas + + impl<'sv_de, #(#generics,)* > serde::Deserialize<'sv_de> for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { + fn deserialize(deserializer: D) -> Result + where D: serde::Deserializer<'sv_de>, + { + use serde::de::Error; + + let val = #sylvia ::serde_value::Value::deserialize(deserializer)?; + let map = match &val { + #sylvia ::serde_value::Value::Map(map) => map, + _ => return Err(D::Error::custom("Wrong message format!")) + }; + if map.len() != 1 { + return Err(D::Error::custom(format!("Expected exactly one message. Received {}", map.len()))) + } + + // Due to earlier size check of map this unwrap is safe + let recv_msg_name = map.into_iter().next().unwrap(); + + if let #sylvia ::serde_value::Value::String(recv_msg_name) = &recv_msg_name .0 { + #(#interfaces_deserialization_attempts)* + #contract_deserialization_attempt + } + + 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. + format!( + "Unsupported message received: {}. Messages supported by this contract: ", + #sylvia ::serde_json::to_string(&val).unwrap_or_else(|_| String::new()) + ), + |mut acc, message| acc + message + ", ", + ); + err_msg.truncate(err_msg.len() - 2); + Err(D::Error::custom(err_msg)) + } + } + + impl #bracketed_wrapper_generics From<<#contract as #sylvia ::types::ContractApi>:: #enum_accessor> + for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { + fn from(a: <#contract as #sylvia ::types::ContractApi>:: #enum_accessor ) -> Self { + Self:: #contract_name (a) + } + } + + #( + impl #bracketed_wrapper_generics From<<#contract as #modules_names ::sv::InterfaceMessagesApi>:: #enum_accessor> + for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { + fn from(a: <#contract as #modules_names ::sv::InterfaceMessagesApi>:: #enum_accessor ) -> Self { + Self:: #variants_names (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>, + sudo_variants: MsgVariants<'a, GenericParam>, + generics: &'a [&'a GenericParam], + custom: &'a Custom, +} + +impl<'a> ContractApi<'a> { + pub fn new(source: &'a ItemImpl, generics: &'a [&'a GenericParam], custom: &'a Custom) -> 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, + ); + + let sudo_variants = MsgVariants::new( + source.as_variants(), + MsgType::Sudo, + generics, + &source.generics.where_clause, + ); + + Self { + source, + exec_variants, + query_variants, + instantiate_variants, + migrate_variants, + sudo_variants, + generics, + custom, + } + } + + pub fn emit(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { + source, + exec_variants, + query_variants, + instantiate_variants, + migrate_variants, + sudo_variants, + generics, + custom, + .. + } = self; + + let where_clause = &source.generics.where_clause; + 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 sudo_generics = &sudo_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 sudo_bracketed_generics = emit_bracketed_generics(sudo_generics); + let instantiate_bracketed_generics = emit_bracketed_generics(instantiate_generics); + let migrate_bracketed_generics = emit_bracketed_generics(migrate_generics); + + let migrate_type = if migrate_variants.variants().count() != 0 { + quote! { type Migrate = MigrateMsg #migrate_bracketed_generics; } + } else { + quote! { type Migrate = #sylvia ::cw_std::Empty; } + }; + let custom_msg = custom.msg_or_default(); + let custom_query = custom.query_or_default(); + + quote! { + impl #bracket_generics #sylvia ::types::ContractApi for #contract_name #where_clause { + type ContractExec = ContractExecMsg #bracket_generics; + type ContractQuery = ContractQueryMsg #bracket_generics; + type ContractSudo = ContractSudoMsg #bracket_generics; + type Exec = ExecMsg #exec_bracketed_generics; + type Query = QueryMsg #query_bracketed_generics; + type Sudo = SudoMsg #sudo_bracketed_generics; + type Instantiate = InstantiateMsg #instantiate_bracketed_generics; + #migrate_type + type Remote<'remote> = #sylvia ::types::Remote<'remote, Self >; + type Querier<'querier> = #sylvia ::types::BoundQuerier<'querier, #custom_query, Self >; + type CustomMsg = #custom_msg; + type CustomQuery = #custom_query; + } + } + } +} + +pub struct InterfaceApi<'a> { + source: &'a ItemTrait, + custom: &'a Custom, + associated_types: &'a AssociatedTypes<'a>, +} + +impl<'a> InterfaceApi<'a> { + pub fn new( + source: &'a ItemTrait, + associated_types: &'a AssociatedTypes<'a>, + custom: &'a Custom, + ) -> Self { + Self { + source, + custom, + associated_types, + } + } + + pub fn emit(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { + source, + custom, + associated_types, + } = self; + + let interface_name = &source.ident; + let generics: Vec<_> = associated_types + .without_error() + .map(ItemType::as_name) + .collect(); + 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 sudo_variants = MsgVariants::new( + source.as_variants(), + MsgType::Sudo, + &generics, + &source.generics.where_clause, + ); + + let where_clause = &self.associated_types.as_where_clause(); + let custom_query = custom.query_or_default(); + let exec_generics = &exec_variants.used_generics; + 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 >; + } + + + pub struct Api < #(#generics,)* > { + #phantom + } + + 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 >; + } + } + } +} + +pub struct EntryPoints<'a> { + source: &'a ItemImpl, + name: Type, + error: Type, + override_entry_points: Vec, + generics: Vec<&'a GenericParam>, + where_clause: &'a Option, + attrs: EntryPointArgs, +} + +impl<'a> EntryPoints<'a> { + pub fn new(source: &'a ItemImpl, attrs: EntryPointArgs) -> Self { + let name = StripGenerics.fold_type(*source.self_ty.clone()); + let parsed_attrs = ParsedSylviaAttributes::new(source.attrs.iter()); + let override_entry_points = parsed_attrs.override_entry_point_attrs; + + let error = parsed_attrs.error_attrs.unwrap_or_default().error; + + let generics: Vec<_> = source.generics.params.iter().collect(); + let where_clause = &source.generics.where_clause; + + Self { + source, + name, + error, + override_entry_points, + generics, + where_clause, + attrs, + } + } + + pub fn emit(&self) -> TokenStream { + let Self { + source, + name, + error, + override_entry_points, + generics, + where_clause, + attrs, + } = self; + let sylvia = crate_module(); + + let bracketed_generics = attrs + .generics + .as_ref() + .map(|generics| match generics.is_empty() { + true => quote! {}, + false => quote! { < #generics > }, + }) + .unwrap_or(quote! {}); + + let custom_msg = parse_quote! { < #name #bracketed_generics as #sylvia ::types::ContractApi > :: CustomMsg }; + let custom_query = parse_quote! { < #name #bracketed_generics as #sylvia ::types::ContractApi > :: CustomQuery }; + + 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() + .map(|variant| variant.function_name.clone()) + .next(); + let sudo_variants = + MsgVariants::new(source.as_variants(), MsgType::Sudo, generics, where_clause); + let contract_generics = match &attrs.generics { + Some(generics) => quote! { ::< #generics > }, + None => quote! {}, + }; + + { + let entry_points = [ + instantiate_variants, + exec_variants, + query_variants, + sudo_variants, + ] + .into_iter() + .map(|variants| { + match override_entry_points.get_entry_point(variants.msg_ty) { + Some(_) => quote! {}, + None => variants.emit_default_entry_point( + &custom_msg, + &custom_query, + name, + error, + &attrs.generics, + ), + } + }); + + let migrate_not_overridden = override_entry_points + .get_entry_point(MsgType::Migrate) + .is_none(); + + 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, + &attrs.generics, + ) + } else { + quote! {} + }; + + let reply_ep = override_entry_points.get_entry_point(MsgType::Reply) + .map(|_| quote! {}) + .unwrap_or_else(|| match reply { + Some(reply) => quote! { + #[#sylvia ::cw_std::entry_point] + pub fn reply( + deps: #sylvia ::cw_std::DepsMut< #custom_query >, + env: #sylvia ::cw_std::Env, + msg: #sylvia ::cw_std::Reply, + ) -> Result<#sylvia ::cw_std::Response < #custom_msg >, #error> { + #name #contract_generics ::new(). #reply((deps, env).into(), msg).map_err(Into::into) + } + }, + _ => quote! {}, + }); + + quote! { + pub mod entry_points { + use super::*; + + #(#entry_points)* + + #migrate + + #reply_ep + } + } + } + } +} diff --git a/sylvia-derive/src/types/associated_types.rs b/sylvia-derive/src/types/associated_types.rs index bcee8571..ccdb19e4 100644 --- a/sylvia-derive/src/types/associated_types.rs +++ b/sylvia-derive/src/types/associated_types.rs @@ -5,7 +5,6 @@ use syn::{parse_quote, ItemTrait, TraitItem, TraitItemType, WhereClause, WherePr pub const ERROR_TYPE: &str = "Error"; pub const EXEC_TYPE: &str = "ExecC"; pub const QUERY_TYPE: &str = "QueryC"; -pub const RESERVED_TYPES: [&str; 3] = [ERROR_TYPE, EXEC_TYPE, QUERY_TYPE]; #[derive(Default)] pub struct AssociatedTypes<'a>(Vec<&'a TraitItemType>); @@ -35,19 +34,8 @@ impl<'a> AssociatedTypes<'a> { .cloned() } - pub fn without_special(&self) -> impl Iterator { - self.0 - .iter() - .filter(|associated| { - !RESERVED_TYPES - .iter() - .any(|reserved| reserved == &associated.ident.to_string().as_str()) - }) - .cloned() - } - pub fn as_where_predicates(&self) -> Vec { - self.without_special() + self.without_error() .map(|associated| { let name = &associated.ident; let colon = &associated.colon_token; @@ -66,10 +54,6 @@ impl<'a> AssociatedTypes<'a> { } } - pub fn as_filtered_names(&self) -> impl Iterator { - self.filtered().map(|associated| &associated.ident) - } - pub fn emit_contract_predicate(&self, trait_name: &Ident) -> TokenStream { let predicate = quote! { ContractT: #trait_name }; if self.0.is_empty() { @@ -85,17 +69,6 @@ impl<'a> AssociatedTypes<'a> { #predicate < #(#bounds,)* > } } - - pub fn filtered(&self) -> impl Iterator { - self.0 - .iter() - .filter(|associated| { - !RESERVED_TYPES - .iter() - .any(|reserved| reserved == &associated.ident.to_string().as_str()) - }) - .cloned() - } } pub trait ItemType { From 23129af621a2ccd3f52bfb15588430d7cc3ed81f Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Tue, 6 Aug 2024 18:01:38 +0200 Subject: [PATCH 11/13] test: Replace without_special with without_error --- sylvia-derive/src/interface/communication/api.rs | 2 +- sylvia-derive/src/interface/mt.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sylvia-derive/src/interface/communication/api.rs b/sylvia-derive/src/interface/communication/api.rs index ab3e271a..ebe777e0 100644 --- a/sylvia-derive/src/interface/communication/api.rs +++ b/sylvia-derive/src/interface/communication/api.rs @@ -36,7 +36,7 @@ impl<'a> Api<'a> { let interface_name = &source.ident; let generics: Vec<_> = associated_types - .without_special() + .without_error() .map(ItemType::as_name) .collect(); let exec_variants = MsgVariants::new( diff --git a/sylvia-derive/src/interface/mt.rs b/sylvia-derive/src/interface/mt.rs index 40f39b76..951bfc0b 100644 --- a/sylvia-derive/src/interface/mt.rs +++ b/sylvia-derive/src/interface/mt.rs @@ -89,7 +89,7 @@ impl<'a> MtHelpers<'a> { .collect(); let associated_args_for_api: Vec<_> = associated_types - .without_special() + .without_error() .map(|associated| { let assoc = &associated.ident; quote! { Self:: #assoc } From 1fba77788497a3be34a990487dfa68b1e77417a7 Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:44:43 +0200 Subject: [PATCH 12/13] test: Remove unused file --- sylvia-derive/src/message.rs | 1446 ---------------------------------- 1 file changed, 1446 deletions(-) delete mode 100644 sylvia-derive/src/message.rs diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs deleted file mode 100644 index d9e342f9..00000000 --- a/sylvia-derive/src/message.rs +++ /dev/null @@ -1,1446 +0,0 @@ -use crate::associated_types::{AssociatedTypes, ItemType, EXEC_TYPE, QUERY_TYPE}; -use crate::check_generics::{CheckGenerics, GetPath}; -use crate::crate_module; -use crate::interfaces::Interfaces; -use crate::parser::attributes::{MsgAttrForwarding, VariantAttrForwarding}; -use crate::parser::{ - parse_associated_custom_type, ContractErrorAttr, Custom, EntryPointArgs, - FilteredOverrideEntryPoints, MsgAttr, MsgType, OverrideEntryPoint, ParsedSylviaAttributes, -}; -use crate::strip_generics::StripGenerics; -use crate::strip_self_path::StripSelfPath; -use crate::utils::{ - as_where_clause, emit_bracketed_generics, extract_return_type, filter_wheres, process_fields, - SvCasing, -}; -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, ToTokens}; -use syn::fold::Fold; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::visit::Visit; -use syn::{ - parse_quote, Attribute, GenericArgument, GenericParam, Ident, ImplItem, ImplItemFn, ItemImpl, - ItemTrait, Pat, PatType, ReturnType, Signature, Token, Type, WhereClause, WherePredicate, -}; - -/// Representation of single struct message -pub struct StructMessage<'a> { - contract_type: &'a Type, - fields: Vec>, - function_name: &'a Ident, - generics: Vec<&'a GenericParam>, - unused_generics: Vec<&'a GenericParam>, - wheres: Vec<&'a WherePredicate>, - full_where: Option<&'a WhereClause>, - result: &'a ReturnType, - msg_attr: MsgAttr, - custom: &'a Custom, - msg_attrs_to_forward: Vec, -} - -impl<'a> StructMessage<'a> { - /// Creates new struct message of given type from impl block - pub fn new( - source: &'a ItemImpl, - ty: MsgType, - generics: &'a [&'a GenericParam], - custom: &'a Custom, - ) -> Option> { - let mut generics_checker = CheckGenerics::new(generics); - - let contract_type = &source.self_ty; - - let parsed = Self::parse_struct_message(source, ty); - let (method, msg_attr) = parsed?; - - let function_name = &method.sig.ident; - let fields = process_fields(&method.sig, &mut generics_checker); - let (used_generics, unused_generics) = generics_checker.used_unused(); - let wheres = filter_wheres(&source.generics.where_clause, generics, &used_generics); - - let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter()) - .msg_attrs_forward - .into_iter() - .filter(|attr| attr.msg_type == ty) - .collect(); - - Some(Self { - contract_type, - fields, - function_name, - generics: used_generics, - unused_generics, - wheres, - full_where: source.generics.where_clause.as_ref(), - result: &method.sig.output, - msg_attr, - custom, - msg_attrs_to_forward, - }) - } - - fn parse_struct_message(source: &ItemImpl, ty: MsgType) -> Option<(&ImplItemFn, MsgAttr)> { - let mut methods = source.items.iter().filter_map(|item| match item { - ImplItem::Fn(method) => { - let attr = ParsedSylviaAttributes::new(method.attrs.iter()).msg_attr?; - if attr == ty { - Some((method, attr)) - } else { - None - } - } - _ => None, - }); - - let (method, msg_attr) = if let Some(method) = methods.next() { - method - } else { - if ty == MsgType::Instantiate { - emit_error!( - source.span(), "Missing instantiation message."; - note = source.span() => "`sylvia::contract` requires exactly one method marked with `#[sv::msg(instantiation)]` attribute." - ); - } - return None; - }; - - if let Some((obsolete, _)) = methods.next() { - emit_error!( - obsolete.span(), "More than one instantiation or migration message"; - note = method.span() => "Instantiation/Migration message previously defined here" - ); - } - Some((method, msg_attr)) - } - - pub fn emit(&self) -> TokenStream { - use MsgAttr::*; - - let instantiate_msg = Ident::new("InstantiateMsg", self.function_name.span()); - let migrate_msg = Ident::new("MigrateMsg", self.function_name.span()); - - match &self.msg_attr { - Instantiate { .. } => self.emit_struct(&instantiate_msg), - Migrate { .. } => self.emit_struct(&migrate_msg), - _ => { - emit_error!(Span::mixed_site(), "Invalid message type"); - quote! {} - } - } - } - - pub fn emit_struct(&self, name: &Ident) -> TokenStream { - let sylvia = crate_module(); - - let Self { - contract_type, - fields, - function_name, - generics, - unused_generics, - wheres, - full_where, - result, - msg_attr, - custom, - msg_attrs_to_forward, - } = self; - - let ctx_type = msg_attr - .msg_type() - .emit_ctx_type(&custom.query_or_default()); - let fields_names: Vec<_> = fields.iter().map(MsgField::name).collect(); - let parameters = fields.iter().map(|field| { - let name = field.name; - let ty = &field.ty; - quote! { #name : #ty} - }); - let fields = fields.iter().map(MsgField::emit); - - let where_clause = as_where_clause(wheres); - let generics = emit_bracketed_generics(generics); - let unused_generics = emit_bracketed_generics(unused_generics); - let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs); - - quote! { - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] - #( #[ #msg_attrs_to_forward ] )* - #[serde(rename_all="snake_case")] - pub struct #name #generics { - #(pub #fields,)* - } - - impl #generics #name #generics #where_clause { - pub fn new(#(#parameters,)*) -> Self { - Self { #(#fields_names,)* } - } - - pub fn dispatch #unused_generics(self, contract: &#contract_type, ctx: #ctx_type) - #result #full_where - { - let Self { #(#fields_names,)* } = self; - contract.#function_name(Into::into(ctx), #(#fields_names,)*).map_err(Into::into) - } - } - } - } -} - -/// Representation of single enum message -pub struct EnumMessage<'a> { - source: &'a ItemTrait, - variants: MsgVariants<'a, Ident>, - associated_types: &'a AssociatedTypes<'a>, - msg_ty: MsgType, - resp_type: Type, - query_type: Type, - msg_attrs_to_forward: Vec, -} - -impl<'a> EnumMessage<'a> { - pub fn new( - source: &'a ItemTrait, - msg_ty: MsgType, - custom: &'a Custom, - variants: MsgVariants<'a, Ident>, - associated_types: &'a AssociatedTypes<'a>, - ) -> Self { - let associated_exec = parse_associated_custom_type(source, EXEC_TYPE); - let associated_query = parse_associated_custom_type(source, QUERY_TYPE); - - let resp_type = custom - .msg - .clone() - .or(associated_exec) - .unwrap_or_else(Custom::default_type); - - let query_type = custom - .query - .clone() - .or(associated_query) - .unwrap_or_else(Custom::default_type); - - let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter()) - .msg_attrs_forward - .into_iter() - .filter(|attr| attr.msg_type == msg_ty) - .collect(); - - Self { - source, - variants, - associated_types, - msg_ty, - resp_type, - query_type, - msg_attrs_to_forward, - } - } - - pub fn emit(&self) -> TokenStream { - let Self { - source, - variants, - associated_types, - msg_ty, - resp_type, - query_type, - msg_attrs_to_forward, - } = self; - - let trait_name = &source.ident; - let enum_name = msg_ty.emit_msg_name(); - let unique_enum_name = - Ident::new(&format!("{}{}", trait_name, enum_name), enum_name.span()); - - let match_arms = variants.emit_dispatch_legs(); - let mut msgs = variants.as_names_snake_cased(); - msgs.sort(); - let msgs_cnt = msgs.len(); - let variants_constructors = variants.emit_constructors(); - let msg_variants = variants.emit(); - - let ctx_type = msg_ty.emit_ctx_type(query_type); - let dispatch_type = msg_ty.emit_result_type(resp_type, &parse_quote!(ContractT::Error)); - - let used_generics = variants.used_generics(); - let unused_generics = variants.unused_generics(); - let where_predicates = associated_types - .without_error() - .map(ItemType::as_where_predicate); - let where_clause = variants.where_clause(); - let contract_predicate = associated_types.emit_contract_predicate(trait_name); - - let phantom_variant = variants.emit_phantom_variant(); - let phatom_match_arm = variants.emit_phantom_match_arm(); - let bracketed_used_generics = emit_bracketed_generics(used_generics); - - let ep_name = msg_ty.emit_ep_name(); - let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), enum_name.span()); - let derive_call = msg_ty.emit_derive_call(); - let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs); - - quote! { - #[allow(clippy::derive_partial_eq_without_eq)] - #derive_call - #( #[ #msg_attrs_to_forward ] )* - #[serde(rename_all="snake_case")] - pub enum #unique_enum_name #bracketed_used_generics { - #(#msg_variants,)* - #phantom_variant - } - pub type #enum_name #bracketed_used_generics = #unique_enum_name #bracketed_used_generics; - - impl #bracketed_used_generics #unique_enum_name #bracketed_used_generics #where_clause { - pub fn dispatch(self, contract: &ContractT, ctx: #ctx_type) - -> #dispatch_type - where - #(#where_predicates,)* - #contract_predicate - { - use #unique_enum_name::*; - - match self { - #(#match_arms,)* - #phatom_match_arm - } - } - #(#variants_constructors)* - } - - pub const fn #messages_fn_name () -> [&'static str; #msgs_cnt] { - [#(#msgs,)*] - } - } - } -} - -/// Representation of single enum message -pub struct ContractEnumMessage<'a> { - variants: MsgVariants<'a, GenericParam>, - msg_ty: MsgType, - contract: &'a Type, - error: &'a ContractErrorAttr, - custom: &'a Custom, - where_clause: &'a Option, - msg_attrs_to_forward: Vec, -} - -impl<'a> ContractEnumMessage<'a> { - pub fn new( - source: &'a ItemImpl, - msg_ty: MsgType, - generics: &'a [&'a GenericParam], - error: &'a ContractErrorAttr, - custom: &'a Custom, - ) -> Self { - let where_clause = &source.generics.where_clause; - let variants = MsgVariants::new(source.as_variants(), msg_ty, generics, where_clause); - let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter()) - .msg_attrs_forward - .into_iter() - .filter(|attr| attr.msg_type == msg_ty) - .collect(); - - Self { - variants, - msg_ty, - contract: &source.self_ty, - error, - custom, - where_clause, - msg_attrs_to_forward, - } - } - - pub fn emit(&self) -> TokenStream { - let sylvia = crate_module(); - - let Self { - variants, - msg_ty, - contract, - error, - custom, - where_clause, - msg_attrs_to_forward, - .. - } = self; - - let enum_name = msg_ty.emit_msg_name(); - let match_arms = variants.emit_dispatch_legs(); - let unused_generics = variants.unused_generics(); - let bracketed_unused_generics = emit_bracketed_generics(unused_generics); - let used_generics = variants.used_generics(); - let bracketed_used_generics = emit_bracketed_generics(used_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 ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), &error.error); - - let derive_query = match msg_ty { - MsgType::Query => quote! { #sylvia ::cw_schema::QueryResponses }, - _ => quote! {}, - }; - - let ep_name = msg_ty.emit_ep_name(); - let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span()); - - let phantom_variant = msg_ty.emit_phantom_variant(used_generics); - let phantom_match_arm = match !used_generics.is_empty() { - true => quote! { - _Phantom(_) => Err(#sylvia ::cw_std::StdError::generic_err("Phantom message should not be constructed.")).map_err(Into::into), - }, - false => quote! {}, - }; - let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs); - - quote! { - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, #derive_query )] - #( #[ #msg_attrs_to_forward ] )* - #[serde(rename_all="snake_case")] - pub enum #enum_name #bracketed_used_generics { - #(#variants,)* - #phantom_variant - } - - impl #bracketed_used_generics #enum_name #bracketed_used_generics { - pub fn dispatch #bracketed_unused_generics (self, contract: &#contract, ctx: #ctx_type) -> #ret_type #where_clause { - use #enum_name::*; - - match self { - #(#match_arms,)* - #phantom_match_arm - } - } - - #(#variants_constructors)* - } - - pub const fn #messages_fn_name () -> [&'static str; #variants_cnt] { - [#(#variant_names,)*] - } - } - } -} - -/// Representation of whole message variant -#[derive(Debug)] -pub struct MsgVariant<'a> { - name: Ident, - function_name: &'a Ident, - fields: Vec>, - /// Type extracted only in case of `Query` and used in `cosmwasm_schema::QueryResponses` - /// `returns` attribute. - return_type: Option, - msg_type: MsgType, - attrs_to_forward: Vec, -} - -impl<'a> MsgVariant<'a> { - /// Creates new message variant from trait method - pub fn new( - sig: &'a Signature, - generics_checker: &mut CheckGenerics, - msg_attr: MsgAttr, - attrs_to_forward: Vec, - ) -> MsgVariant<'a> - where - Generic: GetPath + PartialEq, - { - let function_name = &sig.ident; - - let name = function_name.to_case(Case::UpperCamel); - let fields = process_fields(sig, generics_checker); - let msg_type = msg_attr.msg_type(); - - let return_type = if let MsgAttr::Query { resp_type, .. } = msg_attr { - match resp_type { - Some(resp_type) => { - let resp_type = parse_quote! { #resp_type }; - generics_checker.visit_type(&resp_type); - Some(resp_type) - } - None => { - let return_type = extract_return_type(&sig.output); - let stripped_return_type = StripSelfPath.fold_path(return_type.clone()); - generics_checker.visit_path(&stripped_return_type); - Some(parse_quote! { #return_type }) - } - } - } else { - None - }; - - Self { - name, - function_name, - fields, - return_type, - msg_type, - attrs_to_forward, - } - } - - /// Emits message variant - pub fn emit(&self) -> TokenStream { - let Self { - name, - fields, - msg_type, - return_type, - attrs_to_forward, - .. - } = self; - let fields = fields.iter().map(MsgField::emit); - let returns_attribute = msg_type.emit_returns_attribute(return_type); - let attrs_to_forward = attrs_to_forward.iter().map(|attr| &attr.attrs); - - quote! { - #returns_attribute - #( #[ #attrs_to_forward ] )* - #name { - #(#fields,)* - } - } - } - - /// 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) -> TokenStream { - let Self { - name, - fields, - function_name, - msg_type, - .. - } = self; - - let args: Vec<_> = fields - .iter() - .zip(1..) - .map(|(field, num)| Ident::new(&format!("field{}", num), field.name.span())) - .collect(); - - let fields = fields - .iter() - .map(|field| field.name) - .zip(args.clone()) - .map(|(field, num_field)| quote!(#field : #num_field)); - - let method_call = msg_type.emit_dispatch_leg(function_name, &args); - - quote! { - #name { - #(#fields,)* - } => #method_call - } - } - - /// Emits variants constructors. Constructors names are variants names in snake_case. - pub fn emit_variants_constructors(&self) -> TokenStream { - let Self { name, fields, .. } = self; - - let method_name = name.to_case(Case::Snake); - let parameters = fields.iter().map(MsgField::emit_method_field); - let arguments = fields.iter().map(MsgField::name); - - quote! { - pub fn #method_name( #(#parameters),*) -> Self { - Self :: #name { #(#arguments),* } - } - } - } - - 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() - } - - pub fn name(&self) -> &Ident { - &self.name - } - - pub fn fields(&self) -> &Vec { - &self.fields - } - - pub fn msg_type(&self) -> &MsgType { - &self.msg_type - } - - pub fn return_type(&self) -> &Option { - &self.return_type - } -} - -#[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> -where - Generic: GetPath + PartialEq + ToTokens, -{ - pub fn new( - source: VariantDescs<'a>, - msg_ty: MsgType, - all_generics: &'a [&'a Generic], - unfiltered_where_clause: &'a Option, - ) -> Self { - let mut generics_checker = CheckGenerics::new(all_generics); - let variants: Vec<_> = source - .filter_map(|variant_desc| { - let msg_attr: MsgAttr = variant_desc.attr_msg()?; - let attrs_to_forward = variant_desc.attrs_to_forward(); - - if msg_attr.msg_type() != msg_ty { - return None; - } - - Some(MsgVariant::new( - variant_desc.into_sig(), - &mut generics_checker, - msg_attr, - attrs_to_forward, - )) - }) - .collect(); - - 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, - } - } - - 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 - } - } - - pub fn variants(&self) -> impl Iterator { - self.variants.iter() - } - - pub fn used_generics(&self) -> &Vec<&'a Generic> { - &self.used_generics - } - - pub fn unused_generics(&self) -> &Vec<&'a Generic> { - &self.unused_generics - } - - pub fn emit_default_entry_point( - &self, - custom_msg: &Type, - custom_query: &Type, - name: &Type, - error: &Type, - contract_generics: &Option>, - ) -> TokenStream { - let Self { 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 bracketed_generics = match &contract_generics { - Some(generics) => quote! { ::< #generics > }, - None => quote! {}, - }; - let associated_name = msg_ty.as_accessor_wrapper_name(); - - quote! { - #[#sylvia ::cw_std::entry_point] - pub fn #ep_name ( - #params , - msg: < #name < #contract_generics > as #sylvia ::types::ContractApi> :: #associated_name, - ) -> Result<#resp_type, #error> { - msg.dispatch(&#name #bracketed_generics ::new() , ( #values )).map_err(Into::into) - } - } - } - - pub fn emit_phantom_match_arm(&self) -> TokenStream { - let sylvia = crate_module(); - let Self { used_generics, .. } = self; - if used_generics.is_empty() { - return quote! {}; - } - quote! { - _Phantom(_) => Err(#sylvia ::cw_std::StdError::generic_err("Phantom message should not be constructed.")).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(&self) -> impl Iterator + '_ { - self.variants.iter().map(MsgVariant::emit) - } - - pub fn get_only_variant(&self) -> Option<&MsgVariant> { - self.variants.first() - } - - pub fn emit_phantom_variant(&self) -> TokenStream { - let Self { - msg_ty, - used_generics, - .. - } = self; - - if used_generics.is_empty() { - return quote! {}; - } - - let return_attr = match msg_ty { - MsgType::Query => quote! { #[returns((#(#used_generics,)*))] }, - _ => quote! {}, - }; - - quote! { - #[serde(skip)] - #return_attr - _Phantom(std::marker::PhantomData<( #(#used_generics,)* )>), - } - } -} - -/// Representation of single message variant field -#[derive(Debug)] -pub struct MsgField<'a> { - name: &'a Ident, - ty: &'a Type, - stripped_ty: Type, - attrs: &'a Vec, -} - -impl<'a> MsgField<'a> { - /// Creates new field from trait method argument - 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 => { - // TODO: Support pattern arguments, when decorated with argument with item - // name - // - // Eg. - // - // ``` - // fn exec_foo(&self, ctx: Ctx, #[sv::msg(name=metadata)] SomeData { addr, sender }: SomeData); - // ``` - // - // should expand to enum variant: - // - // ``` - // ExecFoo { - // metadata: SomeDaa - // } - // ``` - emit_error!(pat.span(), "Expected argument name, pattern occurred"); - None - } - }?; - - let ty = &item.ty; - let stripped_ty = StripSelfPath.fold_type((*item.ty).clone()); - let attrs = &item.attrs; - generics_checker.visit_type(&stripped_ty); - - Some(Self { - name, - ty, - stripped_ty, - attrs, - }) - } - - /// Emits message field - pub fn emit(&self) -> TokenStream { - let Self { - name, - stripped_ty, - attrs, - .. - } = self; - - quote! { - #(#attrs)* - #name: #stripped_ty - } - } - - /// Emits method field - pub fn emit_method_field(&self) -> TokenStream { - let Self { - name, stripped_ty, .. - } = self; - - quote! { - #name: #stripped_ty - } - } - - pub fn emit_method_field_folded(&self) -> TokenStream { - let Self { name, ty, .. } = self; - - quote! { - #name: #ty - } - } - - pub fn name(&self) -> &'a Ident { - self.name - } -} - -/// Glue message is the message composing Exec/Query messages from several traits -#[derive(Debug)] -pub struct GlueMessage<'a> { - source: &'a ItemImpl, - contract: &'a Type, - msg_ty: MsgType, - error: &'a ContractErrorAttr, - custom: &'a Custom, - interfaces: &'a Interfaces, -} - -impl<'a> GlueMessage<'a> { - pub fn new( - source: &'a ItemImpl, - msg_ty: MsgType, - error: &'a ContractErrorAttr, - custom: &'a Custom, - interfaces: &'a Interfaces, - ) -> Self { - GlueMessage { - source, - contract: &source.self_ty, - msg_ty, - error, - custom, - interfaces, - } - } - - pub fn emit(&self) -> TokenStream { - let sylvia = crate_module(); - let Self { - source, - contract, - msg_ty, - error, - custom, - interfaces, - .. - } = self; - - let generics: Vec<_> = source.generics.params.iter().collect(); - let full_where_clause = &source.generics.where_clause; - let bracketed_wrapper_generics = emit_bracketed_generics(&generics); - - let contract_enum_name = msg_ty.emit_msg_wrapper_name(); - let enum_accessor = msg_ty.as_accessor_name(); - let contract_name = StripGenerics.fold_type((*contract).clone()); - - let variants = interfaces.emit_glue_message_variants(msg_ty, contract); - let types = interfaces.emit_glue_message_types(msg_ty, contract); - - 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 ( <#contract as #sylvia ::types::ContractApi> :: #enum_accessor ) }; - let mut messages_call = interfaces.emit_messages_call(msg_ty); - messages_call.push(quote! { &#messages_fn_name() }); - - let variants_cnt = messages_call.len(); - - let dispatch_arms = interfaces.emit_dispatch_arms(msg_ty); - - let dispatch_arm = - quote! {#contract_enum_name :: #contract_name (msg) => msg.dispatch(contract, ctx)}; - - let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(msg_ty); - - let contract_deserialization_attempt = quote! { - 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)), - Err(err) => return Err(D::Error::custom(err)).map(Self:: #contract_name ) - }; - } - }; - - 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.error); - - let mut response_schemas_calls = interfaces.emit_response_schemas_calls(msg_ty, contract); - response_schemas_calls - .push(quote! {<#contract as #sylvia ::types::ContractApi> :: #enum_accessor ::response_schemas_impl()}); - - let response_schemas = match msg_ty { - MsgType::Query => { - quote! { - #[cfg(not(target_arch = "wasm32"))] - impl #bracketed_wrapper_generics #sylvia ::cw_schema::QueryResponses for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { - fn response_schemas_impl() -> std::collections::BTreeMap { - let responses = [#(#response_schemas_calls),*]; - responses.into_iter().flatten().collect() - } - } - } - } - _ => { - quote! {} - } - }; - - let modules_names = interfaces.variants_modules(); - let variants_names = interfaces.variants_names(); - - quote! { - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq)] - #[serde(rename_all="snake_case", untagged)] - pub enum #contract_enum_name #bracketed_wrapper_generics #full_where_clause { - #(#variants,)* - #contract_variant - } - - // `schemars` v0.8.16 requires every generic type to implement JsonSchema in - // order to use derive JsonSchema macro. The goal of that trait bound is to - // generate schema_name. Currently there's no way to provide such a name in an - // attribute, so Sylvia needs to implement this trait manually: - // - impl #bracketed_wrapper_generics #sylvia ::schemars::JsonSchema - for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { - - fn schema_name() -> std::string::String { - { - let res = format!( - "{0}", - std::any::type_name::() - ); - res - } - } - - fn json_schema( - gen: &mut #sylvia ::schemars::gen::SchemaGenerator, - ) -> #sylvia ::schemars::schema::Schema { - #sylvia ::schemars::schema::Schema::Object( #sylvia ::schemars::schema::SchemaObject { - subschemas: Some( - Box::new( #sylvia ::schemars::schema::SubschemaValidation { - any_of: Some( - <[_]>::into_vec( - Box::new([ - #(gen.subschema_for::<#types>(),)* - gen.subschema_for::< <#contract as #sylvia ::types::ContractApi> :: #enum_accessor >(), - ]), - ), - ), - ..Default::default() - }), - ), - ..Default::default() - }) - } - } - - impl #bracketed_wrapper_generics #contract_enum_name #bracketed_wrapper_generics #full_where_clause { - pub fn dispatch ( - self, - contract: &#contract, - ctx: #ctx_type, - ) -> #ret_type #full_where_clause { - const _: () = { - let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*]; - #sylvia ::utils::assert_no_intersection(msgs); - }; - - match self { - #(#dispatch_arms,)* - #dispatch_arm - } - } - } - - #response_schemas - - impl<'sv_de, #(#generics,)* > serde::Deserialize<'sv_de> for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { - fn deserialize(deserializer: D) -> Result - where D: serde::Deserializer<'sv_de>, - { - use serde::de::Error; - - let val = #sylvia ::serde_value::Value::deserialize(deserializer)?; - let map = match &val { - #sylvia ::serde_value::Value::Map(map) => map, - _ => return Err(D::Error::custom("Wrong message format!")) - }; - if map.len() != 1 { - return Err(D::Error::custom(format!("Expected exactly one message. Received {}", map.len()))) - } - - // Due to earlier size check of map this unwrap is safe - let recv_msg_name = map.into_iter().next().unwrap(); - - if let #sylvia ::serde_value::Value::String(recv_msg_name) = &recv_msg_name .0 { - #(#interfaces_deserialization_attempts)* - #contract_deserialization_attempt - } - - 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. - format!( - "Unsupported message received: {}. Messages supported by this contract: ", - #sylvia ::serde_json::to_string(&val).unwrap_or_else(|_| String::new()) - ), - |mut acc, message| acc + message + ", ", - ); - err_msg.truncate(err_msg.len() - 2); - Err(D::Error::custom(err_msg)) - } - } - - impl #bracketed_wrapper_generics From<<#contract as #sylvia ::types::ContractApi>:: #enum_accessor> - for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { - fn from(a: <#contract as #sylvia ::types::ContractApi>:: #enum_accessor ) -> Self { - Self:: #contract_name (a) - } - } - - #( - impl #bracketed_wrapper_generics From<<#contract as #modules_names ::sv::InterfaceMessagesApi>:: #enum_accessor> - for #contract_enum_name #bracketed_wrapper_generics #full_where_clause { - fn from(a: <#contract as #modules_names ::sv::InterfaceMessagesApi>:: #enum_accessor ) -> Self { - Self:: #variants_names (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>, - sudo_variants: MsgVariants<'a, GenericParam>, - generics: &'a [&'a GenericParam], - custom: &'a Custom, -} - -impl<'a> ContractApi<'a> { - pub fn new(source: &'a ItemImpl, generics: &'a [&'a GenericParam], custom: &'a Custom) -> 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, - ); - - let sudo_variants = MsgVariants::new( - source.as_variants(), - MsgType::Sudo, - generics, - &source.generics.where_clause, - ); - - Self { - source, - exec_variants, - query_variants, - instantiate_variants, - migrate_variants, - sudo_variants, - generics, - custom, - } - } - - pub fn emit(&self) -> TokenStream { - let sylvia = crate_module(); - let Self { - source, - exec_variants, - query_variants, - instantiate_variants, - migrate_variants, - sudo_variants, - generics, - custom, - .. - } = self; - - let where_clause = &source.generics.where_clause; - 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 sudo_generics = &sudo_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 sudo_bracketed_generics = emit_bracketed_generics(sudo_generics); - let instantiate_bracketed_generics = emit_bracketed_generics(instantiate_generics); - let migrate_bracketed_generics = emit_bracketed_generics(migrate_generics); - - let migrate_type = if migrate_variants.variants().count() != 0 { - quote! { type Migrate = MigrateMsg #migrate_bracketed_generics; } - } else { - quote! { type Migrate = #sylvia ::cw_std::Empty; } - }; - let custom_msg = custom.msg_or_default(); - let custom_query = custom.query_or_default(); - - quote! { - impl #bracket_generics #sylvia ::types::ContractApi for #contract_name #where_clause { - type ContractExec = ContractExecMsg #bracket_generics; - type ContractQuery = ContractQueryMsg #bracket_generics; - type ContractSudo = ContractSudoMsg #bracket_generics; - type Exec = ExecMsg #exec_bracketed_generics; - type Query = QueryMsg #query_bracketed_generics; - type Sudo = SudoMsg #sudo_bracketed_generics; - type Instantiate = InstantiateMsg #instantiate_bracketed_generics; - #migrate_type - type Remote<'remote> = #sylvia ::types::Remote<'remote, Self >; - type Querier<'querier> = #sylvia ::types::BoundQuerier<'querier, #custom_query, Self >; - type CustomMsg = #custom_msg; - type CustomQuery = #custom_query; - } - } - } -} - -pub struct InterfaceApi<'a> { - source: &'a ItemTrait, - custom: &'a Custom, - associated_types: &'a AssociatedTypes<'a>, -} - -impl<'a> InterfaceApi<'a> { - pub fn new( - source: &'a ItemTrait, - associated_types: &'a AssociatedTypes<'a>, - custom: &'a Custom, - ) -> Self { - Self { - source, - custom, - associated_types, - } - } - - pub fn emit(&self) -> TokenStream { - let sylvia = crate_module(); - let Self { - source, - custom, - associated_types, - } = self; - - let interface_name = &source.ident; - let generics: Vec<_> = associated_types - .without_error() - .map(ItemType::as_name) - .collect(); - 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 sudo_variants = MsgVariants::new( - source.as_variants(), - MsgType::Sudo, - &generics, - &source.generics.where_clause, - ); - - let where_clause = &self.associated_types.as_where_clause(); - let custom_query = custom.query_or_default(); - let exec_generics = &exec_variants.used_generics; - 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 >; - } - - - pub struct Api < #(#generics,)* > { - #phantom - } - - 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 >; - } - } - } -} - -pub struct EntryPoints<'a> { - source: &'a ItemImpl, - name: Type, - error: Type, - override_entry_points: Vec, - generics: Vec<&'a GenericParam>, - where_clause: &'a Option, - attrs: EntryPointArgs, -} - -impl<'a> EntryPoints<'a> { - pub fn new(source: &'a ItemImpl, attrs: EntryPointArgs) -> Self { - let name = StripGenerics.fold_type(*source.self_ty.clone()); - let parsed_attrs = ParsedSylviaAttributes::new(source.attrs.iter()); - let override_entry_points = parsed_attrs.override_entry_point_attrs; - - let error = parsed_attrs.error_attrs.unwrap_or_default().error; - - let generics: Vec<_> = source.generics.params.iter().collect(); - let where_clause = &source.generics.where_clause; - - Self { - source, - name, - error, - override_entry_points, - generics, - where_clause, - attrs, - } - } - - pub fn emit(&self) -> TokenStream { - let Self { - source, - name, - error, - override_entry_points, - generics, - where_clause, - attrs, - } = self; - let sylvia = crate_module(); - - let bracketed_generics = attrs - .generics - .as_ref() - .map(|generics| match generics.is_empty() { - true => quote! {}, - false => quote! { < #generics > }, - }) - .unwrap_or(quote! {}); - - let custom_msg = parse_quote! { < #name #bracketed_generics as #sylvia ::types::ContractApi > :: CustomMsg }; - let custom_query = parse_quote! { < #name #bracketed_generics as #sylvia ::types::ContractApi > :: CustomQuery }; - - 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() - .map(|variant| variant.function_name.clone()) - .next(); - let sudo_variants = - MsgVariants::new(source.as_variants(), MsgType::Sudo, generics, where_clause); - let contract_generics = match &attrs.generics { - Some(generics) => quote! { ::< #generics > }, - None => quote! {}, - }; - - { - let entry_points = [ - instantiate_variants, - exec_variants, - query_variants, - sudo_variants, - ] - .into_iter() - .map(|variants| { - match override_entry_points.get_entry_point(variants.msg_ty) { - Some(_) => quote! {}, - None => variants.emit_default_entry_point( - &custom_msg, - &custom_query, - name, - error, - &attrs.generics, - ), - } - }); - - let migrate_not_overridden = override_entry_points - .get_entry_point(MsgType::Migrate) - .is_none(); - - 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, - &attrs.generics, - ) - } else { - quote! {} - }; - - let reply_ep = override_entry_points.get_entry_point(MsgType::Reply) - .map(|_| quote! {}) - .unwrap_or_else(|| match reply { - Some(reply) => quote! { - #[#sylvia ::cw_std::entry_point] - pub fn reply( - deps: #sylvia ::cw_std::DepsMut< #custom_query >, - env: #sylvia ::cw_std::Env, - msg: #sylvia ::cw_std::Reply, - ) -> Result<#sylvia ::cw_std::Response < #custom_msg >, #error> { - #name #contract_generics ::new(). #reply((deps, env).into(), msg).map_err(Into::into) - } - }, - _ => quote! {}, - }); - - quote! { - pub mod entry_points { - use super::*; - - #(#entry_points)* - - #migrate - - #reply_ep - } - } - } - } -} From f0be28b38ee09b1ee6775a0fd4515d897ad27e8a Mon Sep 17 00:00:00 2001 From: abefernan <44572727+abefernan@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:15:55 +0200 Subject: [PATCH 13/13] test: Use empty generics --- examples/contracts/cw1-subkeys/src/bin/schema.rs | 7 +++---- examples/contracts/cw1-subkeys/src/contract.rs | 14 +++----------- examples/contracts/cw1-whitelist/src/bin/schema.rs | 6 ++---- examples/contracts/cw1-whitelist/src/contract.rs | 7 +------ examples/contracts/cw20-base/src/bin/schema.rs | 7 +++---- examples/contracts/cw20-base/src/contract.rs | 12 ++---------- 6 files changed, 14 insertions(+), 39 deletions(-) diff --git a/examples/contracts/cw1-subkeys/src/bin/schema.rs b/examples/contracts/cw1-subkeys/src/bin/schema.rs index 7f0173c2..8a4be049 100644 --- a/examples/contracts/cw1-subkeys/src/bin/schema.rs +++ b/examples/contracts/cw1-subkeys/src/bin/schema.rs @@ -1,13 +1,12 @@ use cosmwasm_schema::write_api; - +use cosmwasm_std::Empty; use cw1_subkeys::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; -use cw1_subkeys::contract::{SvCustomMsg, SvCustomQuery}; #[cfg(not(tarpaulin_include))] fn main() { write_api! { instantiate: InstantiateMsg, - execute: ContractExecMsg, - query: ContractQueryMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, } } diff --git a/examples/contracts/cw1-subkeys/src/contract.rs b/examples/contracts/cw1-subkeys/src/contract.rs index c9b79783..8b27b6c1 100644 --- a/examples/contracts/cw1-subkeys/src/contract.rs +++ b/examples/contracts/cw1-subkeys/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - ensure, ensure_ne, Addr, BankMsg, Coin, CosmosMsg, Deps, DistributionMsg, Env, Order, Response, - StakingMsg, StdResult, + ensure, ensure_ne, Addr, BankMsg, Coin, CosmosMsg, Deps, DistributionMsg, Empty, Env, Order, + Response, StakingMsg, StdResult, }; use cw1_whitelist::contract::Cw1WhitelistContract; use cw2::set_contract_version; @@ -25,21 +25,13 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const MAX_LIMIT: u32 = 30; const DEFAULT_LIMIT: u32 = 10; -#[cosmwasm_schema::cw_serde] -pub struct SvCustomMsg; -impl cosmwasm_std::CustomMsg for SvCustomMsg {} - -#[cosmwasm_schema::cw_serde] -pub struct SvCustomQuery; -impl cosmwasm_std::CustomQuery for SvCustomQuery {} - pub struct Cw1SubkeysContract { pub(crate) whitelist: Cw1WhitelistContract, pub(crate) permissions: Map<&'static Addr, Permissions>, pub(crate) allowances: Map<&'static Addr, Allowance>, } -#[cfg_attr(not(feature = "library"), entry_points(generics))] +#[cfg_attr(not(feature = "library"), entry_points(generics))] #[contract] #[sv::error(ContractError)] #[sv::messages(cw1 as Cw1)] diff --git a/examples/contracts/cw1-whitelist/src/bin/schema.rs b/examples/contracts/cw1-whitelist/src/bin/schema.rs index afe477e4..83ca2718 100644 --- a/examples/contracts/cw1-whitelist/src/bin/schema.rs +++ b/examples/contracts/cw1-whitelist/src/bin/schema.rs @@ -1,14 +1,12 @@ use cosmwasm_schema::write_api; use cosmwasm_std::Empty; - use cw1_whitelist::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; -use cw1_whitelist::contract::SvCustomQuery; #[cfg(not(tarpaulin_include))] fn main() { write_api! { instantiate: InstantiateMsg, - execute: ContractExecMsg, - query: ContractQueryMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, } } diff --git a/examples/contracts/cw1-whitelist/src/contract.rs b/examples/contracts/cw1-whitelist/src/contract.rs index db588189..145589a9 100644 --- a/examples/contracts/cw1-whitelist/src/contract.rs +++ b/examples/contracts/cw1-whitelist/src/contract.rs @@ -1,6 +1,5 @@ use crate::error::ContractError; use cosmwasm_std::{Addr, Deps, Empty, Response}; - use cw2::set_contract_version; use cw_storage_plus::{Item, Map}; use sylvia::types::{CustomMsg, CustomQuery, InstantiateCtx}; @@ -12,17 +11,13 @@ use sylvia::entry_points; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -#[cosmwasm_schema::cw_serde] -pub struct SvCustomQuery; -impl cosmwasm_std::CustomQuery for SvCustomQuery {} - pub struct Cw1WhitelistContract { pub(crate) admins: Map<&'static Addr, Empty>, pub(crate) mutable: Item, pub(crate) _phantom: std::marker::PhantomData<(E, Q)>, } -#[cfg_attr(not(feature = "library"), entry_points(generics))] +#[cfg_attr(not(feature = "library"), entry_points(generics))] #[contract] #[sv::error(ContractError)] #[sv::messages(cw1 as Cw1)] diff --git a/examples/contracts/cw20-base/src/bin/schema.rs b/examples/contracts/cw20-base/src/bin/schema.rs index efb14bc2..75d3970f 100644 --- a/examples/contracts/cw20-base/src/bin/schema.rs +++ b/examples/contracts/cw20-base/src/bin/schema.rs @@ -1,13 +1,12 @@ use cosmwasm_schema::write_api; - +use cosmwasm_std::Empty; use cw20_base::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; -use cw20_base::contract::{SvCustomMsg, SvCustomQuery}; #[cfg(not(tarpaulin_include))] fn main() { write_api! { instantiate: InstantiateMsg, - execute: ContractExecMsg, - query: ContractQueryMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, } } diff --git a/examples/contracts/cw20-base/src/contract.rs b/examples/contracts/cw20-base/src/contract.rs index d434e355..7205cb07 100644 --- a/examples/contracts/cw20-base/src/contract.rs +++ b/examples/contracts/cw20-base/src/contract.rs @@ -3,7 +3,7 @@ use crate::responses::{BalanceResponse, Cw20Coin, Cw20ReceiveMsg, TokenInfoRespo use crate::validation::{validate_accounts, validate_msg, verify_logo}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - ensure, Addr, Binary, BlockInfo, DepsMut, Order, Response, StdError, StdResult, Storage, + ensure, Addr, Binary, BlockInfo, DepsMut, Empty, Order, Response, StdError, StdResult, Storage, Uint128, }; use cw2::{ensure_from_older_version, set_contract_version}; @@ -22,14 +22,6 @@ use sylvia::entry_points; const CONTRACT_NAME: &str = "crates.io:cw20-base"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -#[cw_serde] -pub struct SvCustomMsg; -impl cosmwasm_std::CustomMsg for SvCustomMsg {} - -#[cw_serde] -pub struct SvCustomQuery; -impl cosmwasm_std::CustomQuery for SvCustomQuery {} - #[cw_serde] pub struct TokenInfo { pub name: String, @@ -81,7 +73,7 @@ pub struct Cw20Base { pub(crate) _phantom: std::marker::PhantomData<(E, Q)>, } -#[cfg_attr(not(feature = "library"), entry_points(generics))] +#[cfg_attr(not(feature = "library"), entry_points(generics))] #[contract] #[sv::error(ContractError)] #[sv::messages(cw20_allowances as Allowances)]