diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31b3ba7e..8eb303fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,6 +66,7 @@ jobs: run: cargo clippy --all-targets -- -D warnings - name: Fmt check project run: cargo fmt --check + - name: Test examples working-directory: examples run: cargo test --locked @@ -75,6 +76,7 @@ jobs: - name: Fmt check examples working-directory: examples run: cargo fmt --check + - name: Build cw20-base example working-directory: examples/contracts/cw20-base run: cargo build --release --target wasm32-unknown-unknown --locked --lib @@ -90,10 +92,18 @@ jobs: - name: Build custom working-directory: examples/contracts/custom run: cargo build --release --target wasm32-unknown-unknown --locked --lib + - name: Build generic_contract + working-directory: examples/contracts/generic_contract + run: cargo build --release --target wasm32-unknown-unknown --locked --lib + - name: Build generic_iface_on_contract + working-directory: examples/contracts/generic_iface_on_contract + run: cargo build --release --target wasm32-unknown-unknown --locked --lib + - name: Install cosmwasm-check run: cargo install cosmwasm-check --force - name: Check contracts run: find examples/target/wasm32-unknown-unknown/release/ -type f -name "*.wasm" -exec cosmwasm-check {} \; + - name: Cw1-whitelist schema working-directory: examples/contracts/cw1-whitelist/ run: cargo schema @@ -109,6 +119,13 @@ jobs: - name: Custom schema working-directory: examples/contracts/custom run: cargo schema + - name: Generic_contract schema + working-directory: examples/contracts/generic_contract + run: cargo schema + - name: generic_iface_on_contract schema + working-directory: examples/contracts/generic_iface_on_contract + run: cargo schema + - name: Cw1-whitelist ts-codegen working-directory: examples/contracts/cw1-whitelist/ run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name cw1-whitelist --no-bundle @@ -124,6 +141,13 @@ jobs: - name: Custom ts-codegen working-directory: examples/contracts/custom/ run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name custom --no-bundle + - name: Generic_contract ts-codegen + working-directory: examples/contracts/generic_contract/ + run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name custom --no-bundle + - name: Generic_iface_on_contract ts-codegen + working-directory: examples/contracts/generic_iface_on_contract + run: cosmwasm-ts-codegen generate --plugin client --schema ./schema --out ./ts --name custom --no-bundle + - name: Archive schema artifats uses: actions/upload-artifact@v3 with: @@ -134,6 +158,8 @@ jobs: examples/contracts/cw20-base/schema/cw20-base.json examples/contracts/entry-points-overriding/schema/entry-points-overriding.json examples/contracts/custom/schema/custom.json + examples/contracts/generic_contract/schema/generic_contract.json + examples/contracts/generic_iface_on_contract/schema/generic_iface_on_contract.json coverage: name: Code coverage diff --git a/examples/Cargo.lock b/examples/Cargo.lock index ae267fe5..a606c727 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -242,6 +242,18 @@ dependencies = [ "sylvia", ] +[[package]] +name = "custom-and-generic" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "serde", + "sylvia", +] + [[package]] name = "cw-multi-test" version = "0.16.5" @@ -316,6 +328,7 @@ dependencies = [ "serde", "sylvia", "thiserror", + "whitelist", ] [[package]] @@ -334,6 +347,7 @@ dependencies = [ "serde", "sylvia", "thiserror", + "whitelist", ] [[package]] @@ -550,6 +564,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" +[[package]] +name = "generic" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "serde", + "sylvia", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -560,6 +586,40 @@ dependencies = [ "version_check", ] +[[package]] +name = "generic_contract" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "custom-and-generic", + "cw-multi-test", + "cw-storage-plus", + "cw-utils", + "cw1", + "generic", + "serde", + "sylvia", +] + +[[package]] +name = "generic_iface_on_contract" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "custom-and-generic", + "cw-multi-test", + "cw-storage-plus", + "cw-utils", + "cw1", + "generic", + "serde", + "sylvia", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -1160,6 +1220,18 @@ version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +[[package]] +name = "whitelist" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "serde", + "sylvia", +] + [[package]] name = "winnow" version = "0.4.6" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index a8a0e3ad..0becc451 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,18 +1,23 @@ [workspace] members = [ - # Contract intefaces - "interfaces/cw1", - "interfaces/cw4", - "interfaces/cw20-allowances", - "interfaces/cw20-minting", - "interfaces/cw20-marketing", + # Contract intefaces + "interfaces/cw1", + "interfaces/whitelist", + "interfaces/cw4", + "interfaces/cw20-allowances", + "interfaces/cw20-minting", + "interfaces/cw20-marketing", + "interfaces/custom-and-generic", + "interfaces/generic", - # Contracts - "contracts/cw1-whitelist", - "contracts/cw1-subkeys", - "contracts/cw20-base", - "contracts/entry-points-overriding", - "contracts/custom", + # Contracts + "contracts/cw1-whitelist", + "contracts/cw1-subkeys", + "contracts/cw20-base", + "contracts/entry-points-overriding", + "contracts/custom", + "contracts/generic_contract", + "contracts/generic_iface_on_contract", ] resolver = "2" diff --git a/examples/contracts/custom/src/bin/schema.rs b/examples/contracts/custom/src/bin/schema.rs index a60261b8..554d313c 100644 --- a/examples/contracts/custom/src/bin/schema.rs +++ b/examples/contracts/custom/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use custom::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use custom::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/examples/contracts/custom/src/multitest/tests.rs b/examples/contracts/custom/src/multitest/tests.rs index 5b92208a..566ee7ff 100644 --- a/examples/contracts/custom/src/multitest/tests.rs +++ b/examples/contracts/custom/src/multitest/tests.rs @@ -1,6 +1,6 @@ use sylvia::multitest::App; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use super::custom_module::CustomModule; diff --git a/examples/contracts/cw1-subkeys/Cargo.toml b/examples/contracts/cw1-subkeys/Cargo.toml index 12dc5f70..41f8b087 100644 --- a/examples/contracts/cw1-subkeys/Cargo.toml +++ b/examples/contracts/cw1-subkeys/Cargo.toml @@ -18,6 +18,7 @@ cw-multi-test = { version = "0.16.2", optional = true } cw-storage-plus = "1.0" cw-utils = "1.0" cw1 = { path = "../../interfaces/cw1" } +whitelist = { path = "../../interfaces/whitelist" } cw1-whitelist = { path = "../cw1-whitelist", features = ["library"] } cw2 = "1.0" getrandom = { version = "0.2.8", features = ["js"] } diff --git a/examples/contracts/cw1-subkeys/src/bin/schema.rs b/examples/contracts/cw1-subkeys/src/bin/schema.rs index b07cdbaa..c5129235 100644 --- a/examples/contracts/cw1-subkeys/src/bin/schema.rs +++ b/examples/contracts/cw1-subkeys/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use cw1_subkeys::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use cw1_subkeys::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/examples/contracts/cw1-subkeys/src/contract.rs b/examples/contracts/cw1-subkeys/src/contract.rs index cd86a338..5bd9b49e 100644 --- a/examples/contracts/cw1-subkeys/src/contract.rs +++ b/examples/contracts/cw1-subkeys/src/contract.rs @@ -3,7 +3,6 @@ use cosmwasm_std::{ StakingMsg, StdResult, }; use cw1_whitelist::contract::Cw1WhitelistContract; -use cw1_whitelist::whitelist; use cw2::set_contract_version; use cw_storage_plus::{Bound, Map}; use cw_utils::Expiration; diff --git a/examples/contracts/cw1-subkeys/src/multitest/tests.rs b/examples/contracts/cw1-subkeys/src/multitest/tests.rs index e0d6c61b..a92fe845 100644 --- a/examples/contracts/cw1-subkeys/src/multitest/tests.rs +++ b/examples/contracts/cw1-subkeys/src/multitest/tests.rs @@ -3,7 +3,7 @@ use cw2::{query_contract_info, ContractVersion}; use cw_utils::{Expiration, NativeBalance}; use sylvia::multitest::App; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; const ATOM: &str = "atom"; @@ -370,7 +370,7 @@ mod permissions { } mod cw1_execute { - use crate::cw1::test_utils::Cw1; + use crate::cw1::sv::test_utils::Cw1; use cosmwasm_std::BankMsg; use super::*; diff --git a/examples/contracts/cw1-subkeys/src/whitelist.rs b/examples/contracts/cw1-subkeys/src/whitelist.rs index c36556a4..eb29e84c 100644 --- a/examples/contracts/cw1-subkeys/src/whitelist.rs +++ b/examples/contracts/cw1-subkeys/src/whitelist.rs @@ -1,8 +1,8 @@ use cosmwasm_std::{Response, StdResult}; -use cw1_whitelist::responses::AdminListResponse; -use cw1_whitelist::whitelist::{self, Whitelist}; use sylvia::contract; use sylvia::types::{ExecCtx, QueryCtx}; +use whitelist::responses::AdminListResponse; +use whitelist::Whitelist; use crate::contract::Cw1SubkeysContract; use crate::error::ContractError; diff --git a/examples/contracts/cw1-whitelist/Cargo.toml b/examples/contracts/cw1-whitelist/Cargo.toml index c307894e..6718fbec 100644 --- a/examples/contracts/cw1-whitelist/Cargo.toml +++ b/examples/contracts/cw1-whitelist/Cargo.toml @@ -22,6 +22,7 @@ cosmwasm-schema = "1.2" serde = { version = "1.0", default-features = false, features = ["derive"] } sylvia = { path = "../../../sylvia" } cw1 = { path = "../../interfaces/cw1" } +whitelist = { path = "../../interfaces/whitelist" } cw-storage-plus = "1.0" thiserror = { version = "1.0" } cw2 = "1.0" diff --git a/examples/contracts/cw1-whitelist/src/bin/schema.rs b/examples/contracts/cw1-whitelist/src/bin/schema.rs index fa957edb..3096b127 100644 --- a/examples/contracts/cw1-whitelist/src/bin/schema.rs +++ b/examples/contracts/cw1-whitelist/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use cw1_whitelist::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use cw1_whitelist::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/examples/contracts/cw1-whitelist/src/contract.rs b/examples/contracts/cw1-whitelist/src/contract.rs index f27bdd82..2bf302af 100644 --- a/examples/contracts/cw1-whitelist/src/contract.rs +++ b/examples/contracts/cw1-whitelist/src/contract.rs @@ -1,5 +1,4 @@ use crate::error::ContractError; -use crate::whitelist; use cosmwasm_std::{Addr, Deps, Empty, Response}; use cw2::set_contract_version; @@ -57,11 +56,11 @@ impl Cw1WhitelistContract<'_> { #[cfg(test)] mod tests { use super::*; - use crate::responses::AdminListResponse; - use crate::whitelist::{self, Whitelist}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{coin, coins, to_binary, BankMsg, CosmosMsg, StakingMsg, SubMsg, WasmMsg}; use cw1::Cw1; + use whitelist::responses::AdminListResponse; + use whitelist::Whitelist; #[test] fn instantiate_and_modify_config() { @@ -182,7 +181,7 @@ mod tests { ) .unwrap(); - let freeze = whitelist::ExecMsg::Freeze {}; + let freeze = whitelist::sv::ExecMsg::Freeze {}; let msgs = vec![ BankMsg::Send { to_address: bob.to_string(), @@ -289,15 +288,13 @@ mod tests { } mod msgs { - use super::*; - use cosmwasm_std::{from_binary, from_slice, to_binary, BankMsg}; - use crate::contract::{ContractExecMsg, ContractQueryMsg}; + use crate::contract::sv::{ContractExecMsg, ContractQueryMsg}; #[test] fn freeze() { - let original = whitelist::ExecMsg::Freeze {}; + let original = whitelist::sv::ExecMsg::Freeze {}; let serialized = to_binary(&original).unwrap(); let deserialized = from_binary(&serialized).unwrap(); @@ -309,14 +306,14 @@ mod tests { let deserialized = from_slice(json).unwrap(); assert_eq!( - ContractExecMsg::Whitelist(whitelist::ExecMsg::Freeze {}), + ContractExecMsg::Whitelist(whitelist::sv::ExecMsg::Freeze {}), deserialized ); } #[test] fn update_admins() { - let original = whitelist::ExecMsg::UpdateAdmins { + let original = whitelist::sv::ExecMsg::UpdateAdmins { admins: vec!["admin1".to_owned(), "admin2".to_owned()], }; let serialized = to_binary(&original).unwrap(); @@ -332,7 +329,7 @@ mod tests { let deserialized = from_slice(json).unwrap(); assert_eq!( - ContractExecMsg::Whitelist(whitelist::ExecMsg::UpdateAdmins { + ContractExecMsg::Whitelist(whitelist::sv::ExecMsg::UpdateAdmins { admins: vec!["admin1".to_owned(), "admin3".to_owned()] }), deserialized @@ -341,7 +338,7 @@ mod tests { #[test] fn admin_list() { - let original = whitelist::QueryMsg::AdminList {}; + let original = whitelist::sv::QueryMsg::AdminList {}; let serialized = to_binary(&original).unwrap(); let deserialized = from_binary(&serialized).unwrap(); @@ -353,14 +350,14 @@ mod tests { let deserialized = from_slice(json).unwrap(); assert_eq!( - ContractQueryMsg::Whitelist(whitelist::QueryMsg::AdminList {}), + ContractQueryMsg::Whitelist(whitelist::sv::QueryMsg::AdminList {}), deserialized ); } #[test] fn execute() { - let original = cw1::ExecMsg::Execute { + let original = cw1::sv::ExecMsg::Execute { msgs: vec![BankMsg::Send { to_address: "admin1".to_owned(), amount: vec![], @@ -374,7 +371,7 @@ mod tests { #[test] fn can_execute() { - let original = cw1::QueryMsg::CanExecute { + let original = cw1::sv::QueryMsg::CanExecute { sender: "admin".to_owned(), msg: BankMsg::Send { to_address: "admin1".to_owned(), diff --git a/examples/contracts/cw1-whitelist/src/lib.rs b/examples/contracts/cw1-whitelist/src/lib.rs index cc023ca4..52af57a5 100644 --- a/examples/contracts/cw1-whitelist/src/lib.rs +++ b/examples/contracts/cw1-whitelist/src/lib.rs @@ -3,5 +3,4 @@ mod cw1; pub mod error; #[cfg(any(test, feature = "tests"))] pub mod multitest; -pub mod responses; pub mod whitelist; diff --git a/examples/contracts/cw1-whitelist/src/multitest.rs b/examples/contracts/cw1-whitelist/src/multitest.rs index 9859b6cf..a91713d6 100644 --- a/examples/contracts/cw1-whitelist/src/multitest.rs +++ b/examples/contracts/cw1-whitelist/src/multitest.rs @@ -1,13 +1,12 @@ #[cfg(test)] mod test { use cosmwasm_std::{to_binary, WasmMsg}; + use whitelist::responses::AdminListResponse; - use crate::contract::multitest_utils::CodeId; - use crate::cw1::test_utils::Cw1; + use crate::contract::sv::multitest_utils::CodeId; + use crate::cw1::sv::test_utils::Cw1; use crate::error::ContractError; - use crate::responses::AdminListResponse; - use crate::whitelist; - use crate::whitelist::test_utils::Whitelist; + use crate::whitelist::sv::test_utils::Whitelist; use assert_matches::assert_matches; use sylvia::multitest::App; @@ -30,7 +29,7 @@ mod test { .call(owner) .unwrap(); - let freeze = whitelist::ExecMsg::Freeze {}; + let freeze = whitelist::sv::ExecMsg::Freeze {}; let freeze = WasmMsg::Execute { contract_addr: second_contract.contract_addr.to_string(), msg: to_binary(&freeze).unwrap(), diff --git a/examples/contracts/cw1-whitelist/src/whitelist.rs b/examples/contracts/cw1-whitelist/src/whitelist.rs index 68e2a79e..156a4af8 100644 --- a/examples/contracts/cw1-whitelist/src/whitelist.rs +++ b/examples/contracts/cw1-whitelist/src/whitelist.rs @@ -1,26 +1,14 @@ use cosmwasm_std::{Empty, Order, Response, StdResult}; +use sylvia::contract; use sylvia::types::{ExecCtx, QueryCtx}; -use sylvia::{contract, interface, schemars}; +use whitelist::responses::AdminListResponse; +use whitelist::Whitelist; use crate::contract::Cw1WhitelistContract; use crate::error::ContractError; -use crate::responses::AdminListResponse; - -#[interface] -pub trait Whitelist { - type Error: From; - - #[msg(exec)] - fn freeze(&self, ctx: ExecCtx) -> Result; - - #[msg(exec)] - fn update_admins(&self, ctx: ExecCtx, admins: Vec) -> Result; - - #[msg(query)] - fn admin_list(&self, ctx: QueryCtx) -> StdResult; -} #[contract(module=crate::contract)] +#[messages(whitelist as Whitelist)] impl Whitelist for Cw1WhitelistContract<'_> { type Error = ContractError; diff --git a/examples/contracts/cw20-base/src/bin/schema.rs b/examples/contracts/cw20-base/src/bin/schema.rs index 3482c2fb..ee47c95c 100644 --- a/examples/contracts/cw20-base/src/bin/schema.rs +++ b/examples/contracts/cw20-base/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use cw20_base::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; +use cw20_base::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; #[cfg(not(tarpaulin_include))] fn main() { diff --git a/examples/contracts/cw20-base/src/multitest/allowances_tests.rs b/examples/contracts/cw20-base/src/multitest/allowances_tests.rs index 005b0105..29fdf0ad 100644 --- a/examples/contracts/cw20-base/src/multitest/allowances_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/allowances_tests.rs @@ -7,11 +7,11 @@ use cw_multi_test::next_block; use cw_utils::Expiration; use sylvia::multitest::App; -use crate::allowances::test_utils::Cw20Allowances; -use crate::contract::multitest_utils::CodeId; +use crate::allowances::sv::test_utils::Cw20Allowances; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::InstantiateMsgData; use crate::error::ContractError; -use crate::multitest::receiver_contract::multitest_utils::CodeId as ReceiverCodeId; +use crate::multitest::receiver_contract::sv::multitest_utils::CodeId as ReceiverCodeId; use crate::responses::Cw20Coin; #[test] diff --git a/examples/contracts/cw20-base/src/multitest/base_tests.rs b/examples/contracts/cw20-base/src/multitest/base_tests.rs index 30eca4e6..f97a1699 100644 --- a/examples/contracts/cw20-base/src/multitest/base_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/base_tests.rs @@ -3,11 +3,11 @@ use cw20_allowances::responses::{AllAllowancesResponse, SpenderAllowanceInfo}; use cw_utils::Expiration; use sylvia::multitest::App; -use crate::allowances::test_utils::Cw20Allowances; -use crate::contract::multitest_utils::CodeId; +use crate::allowances::sv::test_utils::Cw20Allowances; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::InstantiateMsgData; use crate::error::ContractError; -use crate::multitest::receiver_contract::multitest_utils::CodeId as ReceiverCodeId; +use crate::multitest::receiver_contract::sv::multitest_utils::CodeId as ReceiverCodeId; use crate::responses::{BalanceResponse, Cw20Coin, TokenInfoResponse}; #[test] diff --git a/examples/contracts/cw20-base/src/multitest/marketing_tests.rs b/examples/contracts/cw20-base/src/multitest/marketing_tests.rs index a32fb8d8..c5e22350 100644 --- a/examples/contracts/cw20-base/src/multitest/marketing_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/marketing_tests.rs @@ -3,10 +3,10 @@ use cw20_marketing::responses::{DownloadLogoResponse, LogoInfo, MarketingInfoRes use cw20_marketing::{EmbeddedLogo, Logo}; use sylvia::multitest::App; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::{InstantiateMarketingInfo, InstantiateMsgData}; use crate::error::ContractError; -use crate::marketing::test_utils::Cw20Marketing; +use crate::marketing::sv::test_utils::Cw20Marketing; const PNG_HEADER: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0d, 0x0a, 0x1a, 0x0a]; diff --git a/examples/contracts/cw20-base/src/multitest/minting_tests.rs b/examples/contracts/cw20-base/src/multitest/minting_tests.rs index 9ae818d8..50418d84 100644 --- a/examples/contracts/cw20-base/src/multitest/minting_tests.rs +++ b/examples/contracts/cw20-base/src/multitest/minting_tests.rs @@ -2,10 +2,10 @@ use cosmwasm_std::{StdError, Uint128}; use cw20_minting::responses::MinterResponse; use sylvia::multitest::App; -use crate::contract::multitest_utils::CodeId; +use crate::contract::sv::multitest_utils::CodeId; use crate::contract::InstantiateMsgData; use crate::error::ContractError; -use crate::minting::test_utils::Cw20Minting; +use crate::minting::sv::test_utils::Cw20Minting; use crate::responses::{Cw20Coin, TokenInfoResponse}; #[test] diff --git a/examples/contracts/cw20-base/src/multitest/receiver.rs b/examples/contracts/cw20-base/src/multitest/receiver.rs index b3494612..3a127237 100644 --- a/examples/contracts/cw20-base/src/multitest/receiver.rs +++ b/examples/contracts/cw20-base/src/multitest/receiver.rs @@ -1,8 +1,6 @@ use cosmwasm_std::{Binary, Response, StdError, Uint128}; use sylvia::types::ExecCtx; -use sylvia::{contract, interface, schemars}; - -use super::receiver_contract::ReceiverContract; +use sylvia::{interface, schemars}; #[interface] pub trait Receiver { @@ -18,18 +16,26 @@ pub trait Receiver { ) -> Result; } -#[contract(module=crate::multitest::receiver_contract)] -impl Receiver for ReceiverContract { - type Error = StdError; +pub mod impl_receiver { + use crate::multitest::receiver_contract::ReceiverContract; + use cosmwasm_std::{Response, StdError}; + use sylvia::contract; + use sylvia::types::ExecCtx; - #[msg(exec)] - fn receive( - &self, - _ctx: ExecCtx, - _sender: String, - _amount: cosmwasm_std::Uint128, - _msg: cosmwasm_std::Binary, - ) -> Result { - Ok(Response::default()) + #[contract(module=crate::multitest::receiver_contract)] + #[messages(crate::multitest::receiver as Receiver)] + impl super::Receiver for ReceiverContract { + type Error = StdError; + + #[msg(exec)] + fn receive( + &self, + _ctx: ExecCtx, + _sender: String, + _amount: cosmwasm_std::Uint128, + _msg: cosmwasm_std::Binary, + ) -> Result { + Ok(Response::default()) + } } } diff --git a/examples/contracts/entry-points-overriding/src/bin/schema.rs b/examples/contracts/entry-points-overriding/src/bin/schema.rs index 0c9fdd48..6d5bd4e0 100644 --- a/examples/contracts/entry-points-overriding/src/bin/schema.rs +++ b/examples/contracts/entry-points-overriding/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use entry_points_overriding::contract::{ContractQueryMsg, InstantiateMsg}; +use entry_points_overriding::contract::sv::{ContractQueryMsg, InstantiateMsg}; use entry_points_overriding::messages::CustomExecMsg; use entry_points_overriding::messages::SudoMsg; diff --git a/examples/contracts/entry-points-overriding/src/messages.rs b/examples/contracts/entry-points-overriding/src/messages.rs index c032e2b4..56b372b5 100644 --- a/examples/contracts/entry-points-overriding/src/messages.rs +++ b/examples/contracts/entry-points-overriding/src/messages.rs @@ -2,7 +2,8 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdError, StdResult}; use sylvia::types::ExecCtx; -use crate::contract::{ContractExecMsg, CounterContract}; +use crate::contract::sv::ContractExecMsg; +use crate::contract::CounterContract; #[cw_serde] pub struct CountResponse { diff --git a/examples/contracts/entry-points-overriding/src/multitest.rs b/examples/contracts/entry-points-overriding/src/multitest.rs index d8224516..9efda4e8 100644 --- a/examples/contracts/entry-points-overriding/src/multitest.rs +++ b/examples/contracts/entry-points-overriding/src/multitest.rs @@ -1,9 +1,8 @@ #[cfg(test)] mod test { - use crate::{ - contract::{multitest_utils::CodeId, ContractExecMsg, ExecMsg}, - messages::{CustomExecMsg, SudoMsg, UserExecMsg}, - }; + use crate::contract::sv::multitest_utils::CodeId; + use crate::contract::sv::{ContractExecMsg, ExecMsg}; + use crate::messages::{CustomExecMsg, SudoMsg, UserExecMsg}; use cosmwasm_std::Addr; use cw_multi_test::Executor; use sylvia::multitest::App; diff --git a/examples/contracts/generic_contract/.cargo/config b/examples/contracts/generic_contract/.cargo/config new file mode 100644 index 00000000..d8ab80fe --- /dev/null +++ b/examples/contracts/generic_contract/.cargo/config @@ -0,0 +1,6 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown --lib" +wasm-debug = "build --target wasm32-unknown-unknown --lib" +unit-test = "test --lib" +integration-test = "test --test integration" +schema = "run --bin schema" diff --git a/examples/contracts/generic_contract/Cargo.toml b/examples/contracts/generic_contract/Cargo.toml new file mode 100644 index 00000000..af6f57dd --- /dev/null +++ b/examples/contracts/generic_contract/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "generic_contract" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Example of generic contract" +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +library = [] +tests = ["library", "cw-multi-test", "anyhow"] + +[dependencies] +anyhow = { version = "1.0", optional = true } +cosmwasm-schema = "1.2" +cosmwasm-std = { version = "1.3", features = ["staking"] } +cw-multi-test = { version = "0.16", optional = true } +cw-storage-plus = "1.0" +cw-utils = "1.0" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } +cw1 = { path = "../../interfaces/cw1" } +generic = { path = "../../interfaces/generic" } +custom-and-generic = { path = "../../interfaces/custom-and-generic" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" +sylvia = { path = "../../../sylvia", features = ["mt"] } diff --git a/examples/contracts/generic_contract/src/bin/schema.rs b/examples/contracts/generic_contract/src/bin/schema.rs new file mode 100644 index 00000000..db8d7541 --- /dev/null +++ b/examples/contracts/generic_contract/src/bin/schema.rs @@ -0,0 +1,13 @@ +use cosmwasm_schema::write_api; + +#[cfg(not(tarpaulin_include))] +fn main() { + use generic_contract::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; + use sylvia::types::SvCustomMsg; + + write_api! { + instantiate: InstantiateMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, + } +} diff --git a/examples/contracts/generic_contract/src/contract.rs b/examples/contracts/generic_contract/src/contract.rs new file mode 100644 index 00000000..12f605f9 --- /dev/null +++ b/examples/contracts/generic_contract/src/contract.rs @@ -0,0 +1,132 @@ +use cosmwasm_std::{Reply, Response, StdResult}; +use cw_storage_plus::Item; +use serde::de::DeserializeOwned; +use serde::Deserialize; +use sylvia::types::{ + CustomMsg, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, ReplyCtx, SvCustomMsg, +}; +use sylvia::{contract, schemars}; + +#[cfg(not(feature = "library"))] +use sylvia::entry_points; + +pub struct GenericContract< + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + FieldType, +> { + _field: Item<'static, FieldType>, + _phantom: std::marker::PhantomData<( + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + )>, +} + +#[cfg_attr(not(feature = "library"), entry_points(generics))] +#[contract] +#[messages(cw1 as Cw1: custom(msg))] +#[messages(generic as Generic: custom(msg))] +#[messages(custom_and_generic as CustomAndGeneric)] +#[sv::custom(msg=SvCustomMsg)] +impl + GenericContract +where + for<'msg_de> InstantiateParam: CustomMsg + Deserialize<'msg_de> + 'msg_de, + ExecParam: CustomMsg + DeserializeOwned + 'static, + QueryParam: CustomMsg + DeserializeOwned + 'static, + MigrateParam: CustomMsg + DeserializeOwned + 'static, + RetType: CustomMsg + DeserializeOwned + 'static, + FieldType: 'static, +{ + pub const fn new() -> Self { + Self { + _field: Item::new("field"), + _phantom: std::marker::PhantomData, + } + } + + #[msg(instantiate)] + pub fn instantiate( + &self, + _ctx: InstantiateCtx, + _msg: InstantiateParam, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(exec)] + pub fn contract_execute( + &self, + _ctx: ExecCtx, + _msg: ExecParam, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(query)] + pub fn contract_query( + &self, + _ctx: QueryCtx, + _msg: QueryParam, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(migrate)] + pub fn migrate( + &self, + _ctx: MigrateCtx, + _msg: MigrateParam, + ) -> StdResult> { + Ok(Response::new()) + } + + #[allow(dead_code)] + #[msg(reply)] + fn reply(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult> { + Ok(Response::new()) + } +} + +#[cfg(test)] +mod tests { + use super::sv::multitest_utils::CodeId; + use sylvia::multitest::App; + use sylvia::types::SvCustomMsg; + + #[test] + fn generic_contract() { + let app = App::>::custom(|_, _, _| {}); + let code_id: CodeId< + SvCustomMsg, + SvCustomMsg, + SvCustomMsg, + super::SvCustomMsg, + super::SvCustomMsg, + String, + _, + > = CodeId::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate(SvCustomMsg {}) + .with_label("GenericContract") + .with_admin(owner) + .call(owner) + .unwrap(); + + contract.contract_execute(SvCustomMsg).call(owner).unwrap(); + contract.contract_query(SvCustomMsg).unwrap(); + contract + .migrate(SvCustomMsg) + .call(owner, code_id.code_id()) + .unwrap(); + } +} diff --git a/examples/contracts/generic_contract/src/custom_and_generic.rs b/examples/contracts/generic_contract/src/custom_and_generic.rs new file mode 100644 index 00000000..2dfadcd0 --- /dev/null +++ b/examples/contracts/generic_contract/src/custom_and_generic.rs @@ -0,0 +1,79 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use custom_and_generic::CustomAndGeneric; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; + +#[contract(module = crate::contract)] +#[messages(custom_and_generic as CustomAndGeneric)] +#[sv::custom(msg=sylvia::types::SvCustomMsg)] +impl + CustomAndGeneric + for crate::contract::GenericContract< + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + FieldType, + > +{ + type Error = StdError; + + #[msg(exec)] + fn custom_generic_execute( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(query)] + fn custom_generic_query( + &self, + _ctx: QueryCtx, + _msg: sylvia::types::SvCustomMsg, + ) -> StdResult { + Ok(SvCustomMsg {}) + } +} + +#[cfg(test)] +mod tests { + use super::sv::test_utils::CustomAndGeneric; + use crate::contract::sv::multitest_utils::CodeId; + use sylvia::{multitest::App, types::SvCustomMsg}; + + #[test] + fn proxy_methods() { + let app = App::>::custom(|_, _, _| {}); + let code_id = CodeId::< + SvCustomMsg, + sylvia::types::SvCustomMsg, + SvCustomMsg, + SvCustomMsg, + sylvia::types::SvCustomMsg, + String, + _, + >::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate(SvCustomMsg {}) + .with_label("GenericContract") + .with_admin(owner) + .call(owner) + .unwrap(); + + contract + .custom_and_generic_proxy() + .custom_generic_execute(vec![]) + .call(owner) + .unwrap(); + contract + .custom_and_generic_proxy() + .custom_generic_query(SvCustomMsg {}) + .unwrap(); + } +} diff --git a/examples/contracts/generic_contract/src/cw1.rs b/examples/contracts/generic_contract/src/cw1.rs new file mode 100644 index 00000000..410bd13d --- /dev/null +++ b/examples/contracts/generic_contract/src/cw1.rs @@ -0,0 +1,72 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use cw1::{CanExecuteResp, Cw1}; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx}; + +#[contract(module = crate::contract)] +#[messages(cw1 as Cw1)] +#[sv::custom(msg=sylvia::types::SvCustomMsg)] +impl Cw1 + for crate::contract::GenericContract< + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + FieldType, + > +{ + type Error = StdError; + + #[msg(exec)] + fn execute(&self, _ctx: ExecCtx, _msgs: Vec) -> StdResult { + Ok(Response::new()) + } + + #[msg(query)] + fn can_execute( + &self, + _ctx: QueryCtx, + _sender: String, + _msg: CosmosMsg, + ) -> StdResult { + Ok(CanExecuteResp::default()) + } +} + +#[cfg(test)] +mod tests { + use super::sv::test_utils::Cw1; + use crate::contract::sv::multitest_utils::CodeId; + use cosmwasm_std::{CosmosMsg, Empty}; + use sylvia::{multitest::App, types::SvCustomMsg}; + + #[test] + fn proxy_methods() { + let app = App::>::custom(|_, _, _| {}); + let code_id = CodeId::< + SvCustomMsg, + sylvia::types::SvCustomMsg, + SvCustomMsg, + SvCustomMsg, + sylvia::types::SvCustomMsg, + String, + _, + >::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate(SvCustomMsg {}) + .with_label("GenericContract") + .with_admin(owner) + .call(owner) + .unwrap(); + + contract.cw1_proxy().execute(vec![]).call(owner).unwrap(); + contract + .cw1_proxy() + .can_execute("sender".to_owned(), CosmosMsg::Custom(Empty {})) + .unwrap(); + } +} diff --git a/examples/contracts/generic_contract/src/generic.rs b/examples/contracts/generic_contract/src/generic.rs new file mode 100644 index 00000000..27176d94 --- /dev/null +++ b/examples/contracts/generic_contract/src/generic.rs @@ -0,0 +1,82 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use generic::Generic; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; + +#[contract(module = crate::contract)] +#[messages(generic as Generic)] +#[sv::custom(msg=SvCustomMsg)] +impl + Generic + for crate::contract::GenericContract< + InstantiateParam, + ExecParam, + QueryParam, + MigrateParam, + RetType, + FieldType, + > +{ + type Error = StdError; + + #[msg(exec)] + fn generic_exec( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> StdResult { + Ok(Response::new()) + } + + // Sylvia will fail if single type is used to match against two different generics + // It's because we have to map unique generics used as they can be used multiple times. + // If for some reason like here one type would be used in place of two generics either full + // path or some alias has to be used. + #[msg(query)] + fn generic_query( + &self, + _ctx: QueryCtx, + _msg: sylvia::types::SvCustomMsg, + ) -> StdResult { + Ok(SvCustomMsg {}) + } +} + +#[cfg(test)] +mod tests { + use super::sv::test_utils::Generic; + use crate::contract::sv::multitest_utils::CodeId; + use cosmwasm_std::CosmosMsg; + use sylvia::multitest::App; + use sylvia::types::SvCustomMsg; + + #[test] + fn proxy_methods() { + let app = App::>::custom(|_, _, _| {}); + let code_id: CodeId< + SvCustomMsg, + sylvia::types::SvCustomMsg, + SvCustomMsg, + SvCustomMsg, + sylvia::types::SvCustomMsg, + String, + _, + > = CodeId::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate(SvCustomMsg {}) + .with_label("GenericContract") + .with_admin(owner) + .call(owner) + .unwrap(); + + contract + .generic_proxy() + .generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]) + .call(owner) + .unwrap(); + contract.generic_proxy().generic_query(SvCustomMsg).unwrap(); + } +} diff --git a/examples/contracts/generic_contract/src/lib.rs b/examples/contracts/generic_contract/src/lib.rs new file mode 100644 index 00000000..ea8b3738 --- /dev/null +++ b/examples/contracts/generic_contract/src/lib.rs @@ -0,0 +1,4 @@ +pub mod contract; +pub mod custom_and_generic; +pub mod cw1; +pub mod generic; diff --git a/examples/contracts/generic_iface_on_contract/.cargo/config b/examples/contracts/generic_iface_on_contract/.cargo/config new file mode 100644 index 00000000..d8ab80fe --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/.cargo/config @@ -0,0 +1,6 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown --lib" +wasm-debug = "build --target wasm32-unknown-unknown --lib" +unit-test = "test --lib" +integration-test = "test --test integration" +schema = "run --bin schema" diff --git a/examples/contracts/generic_iface_on_contract/Cargo.toml b/examples/contracts/generic_iface_on_contract/Cargo.toml new file mode 100644 index 00000000..99ad5aa1 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "generic_iface_on_contract" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Generic interfaces implemented on non generic contract" +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +library = [] +tests = ["library", "cw-multi-test", "anyhow"] + +[dependencies] +anyhow = { version = "1.0", optional = true } +cosmwasm-schema = "1.2" +cosmwasm-std = { version = "1.3", features = ["staking"] } +cw-multi-test = { version = "0.16", optional = true } +cw-storage-plus = "1.0" +cw-utils = "1.0" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } +cw1 = { path = "../../interfaces/cw1" } +generic = { path = "../../interfaces/generic" } +custom-and-generic = { path = "../../interfaces/custom-and-generic" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" +sylvia = { path = "../../../sylvia", features = ["mt"] } diff --git a/examples/contracts/generic_iface_on_contract/src/bin/schema.rs b/examples/contracts/generic_iface_on_contract/src/bin/schema.rs new file mode 100644 index 00000000..03983dc6 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/bin/schema.rs @@ -0,0 +1,14 @@ +use cosmwasm_schema::write_api; + +#[cfg(not(tarpaulin_include))] +fn main() { + use generic_iface_on_contract::contract::sv::{ + ContractExecMsg, ContractQueryMsg, InstantiateMsg, + }; + + write_api! { + instantiate: InstantiateMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/contract.rs b/examples/contracts/generic_iface_on_contract/src/contract.rs new file mode 100644 index 00000000..a8f92805 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/contract.rs @@ -0,0 +1,85 @@ +use cosmwasm_std::{Response, StdResult}; +use sylvia::types::{InstantiateCtx, SvCustomMsg}; +use sylvia::{contract, schemars}; + +#[cfg(not(feature = "library"))] +use sylvia::entry_points; + +pub struct NonGenericContract; + +#[cfg_attr(not(feature = "library"), entry_points)] +#[contract] +#[messages(generic as Generic: custom(msg))] +#[messages(custom_and_generic as CustomAndGeneric)] +#[messages(cw1 as Cw1: custom(msg))] +/// Required if interface returns generic `Response` +#[sv::custom(msg=SvCustomMsg)] +impl NonGenericContract { + pub const fn new() -> Self { + Self + } + + #[msg(instantiate)] + pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult> { + Ok(Response::new()) + } +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::{CosmosMsg, Empty}; + use sylvia::{multitest::App, types::SvCustomMsg}; + + use super::NonGenericContract; + use crate::custom_and_generic::sv::test_utils::CustomAndGeneric; + use crate::cw1::sv::test_utils::Cw1; + use crate::generic::sv::test_utils::Generic; + + #[test] + fn mt_helpers() { + let _ = NonGenericContract::new(); + let app = App::>::custom(|_, _, _| {}); + let code_id = super::sv::multitest_utils::CodeId::store_code(&app); + + let owner = "owner"; + + let contract = code_id + .instantiate() + .with_label("Cw1Contract") + .call(owner) + .unwrap(); + + // Non custom non generic interface + contract + .cw1_proxy() + .can_execute("sender".to_owned(), CosmosMsg::Custom(Empty {})) + .unwrap(); + contract + .cw1_proxy() + .execute(vec![CosmosMsg::Custom(Empty {})]) + .call(owner) + .unwrap(); + + // Non-Custom generic Interface + contract + .generic_proxy() + .generic_query(SvCustomMsg {}) + .unwrap(); + contract + .generic_proxy() + .generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]) + .call(owner) + .unwrap(); + + // Custom generic Interface + contract + .custom_and_generic_proxy() + .custom_generic_query(SvCustomMsg {}) + .unwrap(); + contract + .custom_and_generic_proxy() + .custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]) + .call(owner) + .unwrap(); + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs b/examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs new file mode 100644 index 00000000..90052b86 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/custom_and_generic.rs @@ -0,0 +1,31 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use custom_and_generic::CustomAndGeneric; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; + +#[contract(module = crate::contract)] +#[messages(custom_and_generic as CustomAndGeneric)] +#[sv::custom(msg=sylvia::types::SvCustomMsg)] +impl CustomAndGeneric + for crate::contract::NonGenericContract +{ + type Error = StdError; + + #[msg(exec)] + fn custom_generic_execute( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> StdResult> { + Ok(Response::new()) + } + + #[msg(query)] + fn custom_generic_query( + &self, + _ctx: QueryCtx, + _msg: sylvia::types::SvCustomMsg, + ) -> StdResult { + Ok(SvCustomMsg {}) + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/cw1.rs b/examples/contracts/generic_iface_on_contract/src/cw1.rs new file mode 100644 index 00000000..8d45bc0d --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/cw1.rs @@ -0,0 +1,26 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use cw1::{CanExecuteResp, Cw1}; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx}; + +#[contract(module = crate::contract)] +#[messages(cw1 as Cw1)] +#[sv::custom(msg=sylvia::types::SvCustomMsg)] +impl Cw1 for crate::contract::NonGenericContract { + type Error = StdError; + + #[msg(exec)] + fn execute(&self, _ctx: ExecCtx, _msgs: Vec) -> StdResult { + Ok(Response::new()) + } + + #[msg(query)] + fn can_execute( + &self, + _ctx: QueryCtx, + _sender: String, + _msg: CosmosMsg, + ) -> StdResult { + Ok(CanExecuteResp::default()) + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/generic.rs b/examples/contracts/generic_iface_on_contract/src/generic.rs new file mode 100644 index 00000000..d13827a0 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/generic.rs @@ -0,0 +1,35 @@ +use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult}; +use generic::Generic; +use sylvia::contract; +use sylvia::types::{ExecCtx, QueryCtx, SvCustomMsg}; + +#[contract(module = crate::contract)] +#[messages(generic as Generic)] +#[sv::custom(msg = SvCustomMsg)] +impl Generic + for crate::contract::NonGenericContract +{ + type Error = StdError; + + #[msg(exec)] + fn generic_exec( + &self, + _ctx: ExecCtx, + _msgs: Vec>, + ) -> StdResult { + Ok(Response::new()) + } + + // Sylvia will fail if single type is used to match against two different generics + // It's because we have to map unique generics used as they can be used multiple times. + // If for some reason like here one type would be used in place of two generics either full + // path or some alias has to be used. + #[msg(query)] + fn generic_query( + &self, + _ctx: QueryCtx, + _msg: sylvia::types::SvCustomMsg, + ) -> StdResult { + Ok(SvCustomMsg {}) + } +} diff --git a/examples/contracts/generic_iface_on_contract/src/lib.rs b/examples/contracts/generic_iface_on_contract/src/lib.rs new file mode 100644 index 00000000..ea8b3738 --- /dev/null +++ b/examples/contracts/generic_iface_on_contract/src/lib.rs @@ -0,0 +1,4 @@ +pub mod contract; +pub mod custom_and_generic; +pub mod cw1; +pub mod generic; diff --git a/examples/interfaces/custom-and-generic/Cargo.toml b/examples/interfaces/custom-and-generic/Cargo.toml new file mode 100644 index 00000000..c1f38f99 --- /dev/null +++ b/examples/interfaces/custom-and-generic/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "custom-and-generic" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Interface with custom msg and generic support." +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[features] +mt = ["sylvia/mt"] + +[dependencies] +cosmwasm-std = { version = "1.3", features = ["staking"] } +cosmwasm-schema = "1.2" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" diff --git a/examples/interfaces/custom-and-generic/src/lib.rs b/examples/interfaces/custom-and-generic/src/lib.rs new file mode 100644 index 00000000..f56d4503 --- /dev/null +++ b/examples/interfaces/custom-and-generic/src/lib.rs @@ -0,0 +1,69 @@ +use cosmwasm_std::{CosmosMsg, CustomMsg, Response, StdError}; + +use serde::de::DeserializeOwned; +use serde::Deserialize; +use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::{interface, schemars}; + +#[interface] +#[sv::custom(msg=RetType)] +pub trait CustomAndGeneric +where + for<'msg_de> ExecParam: CustomMsg + Deserialize<'msg_de>, + QueryParam: sylvia::types::CustomMsg, + RetType: CustomMsg + DeserializeOwned, +{ + type Error: From; + + #[msg(exec)] + fn custom_generic_execute( + &self, + ctx: ExecCtx, + msgs: Vec>, + ) -> Result, Self::Error>; + + #[msg(query)] + fn custom_generic_query( + &self, + ctx: QueryCtx, + param: QueryParam, + ) -> Result; +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::mock_dependencies; + use cosmwasm_std::{Addr, CosmosMsg, Empty, QuerierWrapper}; + use sylvia::types::{InterfaceApi, SvCustomMsg}; + + use crate::sv::Querier; + + #[test] + fn construct_messages() { + let contract = Addr::unchecked("contract"); + + // Direct message construction + let _ = super::sv::QueryMsg::<_, Empty>::custom_generic_query(SvCustomMsg {}); + let _ = super::sv::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]); + let _ = super::sv::ExecMsg::custom_generic_execute(vec![CosmosMsg::Custom(SvCustomMsg {})]); + + // Querier + let deps = mock_dependencies(); + let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier); + + let querier = super::sv::BoundQuerier::borrowed(&contract, &querier_wrapper); + let _: Result = + super::sv::Querier::custom_generic_query(&querier, SvCustomMsg {}); + let _: Result = querier.custom_generic_query(SvCustomMsg {}); + + // Construct messages with Interface extension + let _ = + as InterfaceApi>::Query::custom_generic_query( + SvCustomMsg {}, + ); + let _= + as InterfaceApi>::Exec::custom_generic_execute( + vec![ CosmosMsg::Custom(SvCustomMsg{}), + ]); + } +} diff --git a/examples/interfaces/cw1/src/lib.rs b/examples/interfaces/cw1/src/lib.rs index 3e12c2e2..46d204b1 100644 --- a/examples/interfaces/cw1/src/lib.rs +++ b/examples/interfaces/cw1/src/lib.rs @@ -36,11 +36,9 @@ pub trait Cw1 { mod tests { use cosmwasm_std::{coins, from_binary, from_slice, to_binary, BankMsg}; - use super::*; - #[test] fn execute() { - let original = ExecMsg::Execute { + let original = super::sv::ExecMsg::Execute { msgs: vec![BankMsg::Send { to_address: "receiver".to_owned(), amount: coins(10, "atom"), @@ -57,12 +55,12 @@ mod tests { #[test] fn execute_from_slice() { let deserialized = from_slice(br#"{"execute": { "msgs": [] }}"#).unwrap(); - assert_eq!(ExecMsg::Execute { msgs: vec![] }, deserialized); + assert_eq!(super::sv::ExecMsg::Execute { msgs: vec![] }, deserialized); } #[test] fn query() { - let original = QueryMsg::CanExecute { + let original = super::sv::QueryMsg::CanExecute { sender: "sender".to_owned(), msg: BankMsg::Send { to_address: "receiver".to_owned(), @@ -99,7 +97,7 @@ mod tests { ) .unwrap(); assert_eq!( - QueryMsg::CanExecute { + super::sv::QueryMsg::CanExecute { sender: "address".to_owned(), msg: BankMsg::Send { to_address: "receiver".to_owned(), diff --git a/examples/interfaces/cw4/src/lib.rs b/examples/interfaces/cw4/src/lib.rs index cc730746..0bb2551a 100644 --- a/examples/interfaces/cw4/src/lib.rs +++ b/examples/interfaces/cw4/src/lib.rs @@ -39,7 +39,7 @@ pub trait Cw4 { mod tests { use cosmwasm_std::{from_binary, from_slice, to_binary}; - use super::*; + use super::sv::*; #[test] fn execute() { @@ -84,7 +84,7 @@ mod tests { #[test] fn exec_msgs() { assert_eq!( - Cw4ExecMsg::messages(), + execute_messages(), ["add_hook", "remove_hook", "update_admin", "update_members"] ); } @@ -92,7 +92,7 @@ mod tests { #[test] fn query_msgs() { assert_eq!( - Cw4QueryMsg::messages(), + query_messages(), ["admin", "hooks", "list_members", "member", "total_weight"] ); } diff --git a/examples/interfaces/generic/Cargo.toml b/examples/interfaces/generic/Cargo.toml new file mode 100644 index 00000000..c5832b18 --- /dev/null +++ b/examples/interfaces/generic/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "generic" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Interface with generic support." +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[features] +mt = ["sylvia/mt"] + +[dependencies] +cosmwasm-std = { version = "1.3", features = ["staking"] } +cosmwasm-schema = "1.2" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" diff --git a/examples/interfaces/generic/src/lib.rs b/examples/interfaces/generic/src/lib.rs new file mode 100644 index 00000000..f6801536 --- /dev/null +++ b/examples/interfaces/generic/src/lib.rs @@ -0,0 +1,60 @@ +use cosmwasm_std::{CosmosMsg, CustomMsg, Response, StdError}; + +use serde::{de::DeserializeOwned, Deserialize}; +use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::{interface, schemars}; + +#[interface] +pub trait Generic +where + for<'msg_de> ExecParam: CustomMsg + Deserialize<'msg_de>, + QueryParam: sylvia::types::CustomMsg, + RetType: CustomMsg + DeserializeOwned, +{ + type Error: From; + + #[msg(exec)] + fn generic_exec( + &self, + ctx: ExecCtx, + msgs: Vec>, + ) -> Result; + + #[msg(query)] + fn generic_query(&self, ctx: QueryCtx, param: QueryParam) -> Result; +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::{testing::mock_dependencies, Addr, CosmosMsg, Empty, QuerierWrapper}; + use sylvia::types::{InterfaceApi, SvCustomMsg}; + + use crate::sv::Querier; + + #[test] + fn construct_messages() { + let contract = Addr::unchecked("contract"); + + // Direct message construction + let _ = super::sv::QueryMsg::<_, Empty>::generic_query(SvCustomMsg {}); + let _ = super::sv::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]); + let _ = super::sv::ExecMsg::generic_exec(vec![CosmosMsg::Custom(SvCustomMsg {})]); + + // Querier + let deps = mock_dependencies(); + let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier); + + let querier = super::sv::BoundQuerier::borrowed(&contract, &querier_wrapper); + let _: Result = super::sv::Querier::generic_query(&querier, SvCustomMsg {}); + let _: Result = querier.generic_query(SvCustomMsg {}); + + // Construct messages with Interface extension + let _ = as InterfaceApi>::Query::generic_query( + SvCustomMsg {}, + ); + let _= + as InterfaceApi>::Exec::generic_exec(vec![ + CosmosMsg::Custom(SvCustomMsg{}), + ]); + } +} diff --git a/examples/interfaces/whitelist/Cargo.toml b/examples/interfaces/whitelist/Cargo.toml new file mode 100644 index 00000000..5ec641e6 --- /dev/null +++ b/examples/interfaces/whitelist/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "whitelist" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Coverage of whitelist sylvia message generation." +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[features] +mt = ["sylvia/mt"] + +[dependencies] +cosmwasm-std = { version = "1.3", features = ["staking"] } +cosmwasm-schema = "1.2" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" diff --git a/examples/interfaces/whitelist/src/lib.rs b/examples/interfaces/whitelist/src/lib.rs new file mode 100644 index 00000000..34daafe2 --- /dev/null +++ b/examples/interfaces/whitelist/src/lib.rs @@ -0,0 +1,76 @@ +use cosmwasm_std::{Response, StdResult}; +use responses::AdminListResponse; +use sylvia::types::{ExecCtx, QueryCtx}; +use sylvia::{interface, schemars}; + +pub mod responses; + +#[interface] +pub trait Whitelist { + type Error: From; + + #[msg(exec)] + fn freeze(&self, ctx: ExecCtx) -> Result; + + #[msg(exec)] + fn update_admins(&self, ctx: ExecCtx, admins: Vec) -> Result; + + #[msg(query)] + fn admin_list(&self, ctx: QueryCtx) -> StdResult; +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::{from_binary, from_slice, to_binary}; + + use super::*; + + #[test] + fn exec_from_binary() { + let original = sv::ExecMsg::Freeze {}; + + let serialized = to_binary(&original).unwrap(); + let deserialized = from_binary(&serialized).unwrap(); + + assert_eq!(original, deserialized); + + let original = sv::ExecMsg::UpdateAdmins { + admins: vec!["new_admin".to_owned()], + }; + + let serialized = to_binary(&original).unwrap(); + let deserialized = from_binary(&serialized).unwrap(); + + assert_eq!(original, deserialized); + } + + #[test] + fn exec_from_slice() { + let deserialized = from_slice(br#"{"freeze": { }}"#).unwrap(); + assert_eq!(sv::ExecMsg::Freeze {}, deserialized); + + let deserialized = + from_slice(br#"{"update_admins": { "admins": ["new_admin"] }}"#).unwrap(); + assert_eq!( + sv::ExecMsg::UpdateAdmins { + admins: vec!["new_admin".to_owned()] + }, + deserialized + ); + } + + #[test] + fn query_from_binary() { + let original = sv::QueryMsg::AdminList {}; + let serialized = to_binary(&original).unwrap(); + let deserialized = from_binary(&serialized).unwrap(); + + assert_eq!(original, deserialized); + } + + #[test] + fn query_from_slice() { + let deserialized = from_slice(br#"{"admin_list": {}}"#).unwrap(); + assert_eq!(sv::QueryMsg::AdminList {}, deserialized); + } +} diff --git a/examples/contracts/cw1-whitelist/src/responses.rs b/examples/interfaces/whitelist/src/responses.rs similarity index 100% rename from examples/contracts/cw1-whitelist/src/responses.rs rename to examples/interfaces/whitelist/src/responses.rs diff --git a/sylvia-derive/src/check_generics.rs b/sylvia-derive/src/check_generics.rs index fbd2188a..c31ca402 100644 --- a/sylvia-derive/src/check_generics.rs +++ b/sylvia-derive/src/check_generics.rs @@ -1,25 +1,57 @@ use syn::visit::Visit; -use syn::GenericParam; +use syn::{parse_quote, GenericArgument, GenericParam, Type}; -pub struct CheckGenerics<'g> { - generics: &'g [&'g GenericParam], - used: Vec<&'g GenericParam>, +pub trait GetPath { + fn get_path(&self) -> Option; } -impl<'g> CheckGenerics<'g> { - pub fn new(generics: &'g [&'g GenericParam]) -> Self { +impl GetPath for GenericParam { + fn get_path(&self) -> Option { + match self { + GenericParam::Type(ty) => { + let ident = &ty.ident; + Some(parse_quote! { #ident }) + } + _ => None, + } + } +} + +impl GetPath for GenericArgument { + fn get_path(&self) -> Option { + match self { + GenericArgument::Type(Type::Path(path)) => { + let path = &path.path; + Some(parse_quote! { #path }) + } + _ => None, + } + } +} + +#[derive(Debug)] +pub struct CheckGenerics<'g, Generic> { + generics: &'g [&'g Generic], + used: Vec<&'g Generic>, +} + +impl<'g, Generic> CheckGenerics<'g, Generic> +where + Generic: GetPath + PartialEq, +{ + pub fn new(generics: &'g [&'g Generic]) -> Self { Self { generics, used: vec![], } } - pub fn used(self) -> Vec<&'g GenericParam> { + pub fn used(self) -> Vec<&'g Generic> { self.used } /// Returns split between used and unused generics - pub fn used_unused(self) -> (Vec<&'g GenericParam>, Vec<&'g GenericParam>) { + pub fn used_unused(self) -> (Vec<&'g Generic>, Vec<&'g Generic>) { let unused = self .generics .iter() @@ -31,17 +63,18 @@ impl<'g> CheckGenerics<'g> { } } -impl<'ast, 'g> Visit<'ast> for CheckGenerics<'g> { +impl<'ast, 'g, Generic> Visit<'ast> for CheckGenerics<'g, Generic> +where + Generic: GetPath + PartialEq, +{ fn visit_path(&mut self, p: &'ast syn::Path) { - if let Some(p) = p.get_ident() { - if let Some(gen) = self - .generics - .iter() - .find(|gen| matches!(gen, GenericParam::Type(ty) if ty.ident == *p)) - { - if !self.used.contains(gen) { - self.used.push(gen); - } + if let Some(gen) = self + .generics + .iter() + .find(|gen| gen.get_path().as_ref() == Some(p)) + { + if !self.used.contains(gen) { + self.used.push(gen); } } diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs index 6fe658c5..839702cc 100644 --- a/sylvia-derive/src/input.rs +++ b/sylvia-derive/src/input.rs @@ -1,13 +1,20 @@ use proc_macro2::{Span, TokenStream}; use proc_macro_error::emit_error; -use quote::quote; +use quote::{quote, ToTokens}; use syn::parse::{Parse, Parser}; use syn::spanned::Spanned; -use syn::{parse_quote, GenericParam, Ident, ItemImpl, ItemTrait, TraitItem, Type}; +use syn::{ + parse_quote, GenericArgument, GenericParam, Ident, ItemImpl, ItemTrait, PathArguments, + TraitItem, Type, +}; +use crate::check_generics::GetPath; use crate::crate_module; use crate::interfaces::Interfaces; -use crate::message::{ContractEnumMessage, EnumMessage, GlueMessage, MsgVariants, StructMessage}; +use crate::message::{ + ContractApi, ContractEnumMessage, EnumMessage, GlueMessage, InterfaceApi, MsgVariants, + StructMessage, +}; use crate::multitest::{MultitestHelpers, TraitMultitestHelpers}; use crate::parser::{ContractArgs, ContractErrorAttr, Custom, MsgType, OverrideEntryPoints}; use crate::remote::Remote; @@ -62,18 +69,32 @@ impl<'a> TraitInput<'a> { let messages = self.emit_messages(); let multitest_helpers = self.emit_helpers(); let remote = Remote::new(&Interfaces::default()).emit(); - let querier = MsgVariants::new(self.item.as_variants(), &self.generics).emit_querier(); + + let querier = MsgVariants::new( + self.item.as_variants(), + MsgType::Query, + &self.generics, + &self.item.generics.where_clause, + ) + .emit_querier(); + + let interface_messages = InterfaceApi::new(self.item, &self.generics).emit(); #[cfg(not(tarpaulin_include))] { quote! { - #messages + pub mod sv { + use super::*; + #messages - #multitest_helpers + #multitest_helpers + + #remote - #remote + #querier - #querier + #interface_messages + } } } } @@ -144,69 +165,70 @@ impl<'a> ImplInput<'a> { pub fn process(&self) -> TokenStream { let is_trait = self.item.trait_.is_some(); - let multitest_helpers = if cfg!(feature = "mt") { - MultitestHelpers::new( - self.item, - is_trait, - &self.error, - &self.generics, - &self.custom, - &self.override_entry_points, - &self.interfaces, - ) - .emit() - } else { - quote! {} - }; - - let interfaces = Interfaces::new(self.item); - let variants = MsgVariants::new(self.item.as_variants(), &self.generics); match is_trait { - true => self.process_interface(&interfaces, variants, multitest_helpers), - false => self.process_contract(&interfaces, variants, multitest_helpers), + true => self.process_interface(), + false => self.process_contract(), } } - fn process_interface( - &self, - interfaces: &Interfaces, - variants: MsgVariants<'a>, - multitest_helpers: TokenStream, - ) -> TokenStream { - let querier_bound_for_impl = self.emit_querier_for_bound_impl(interfaces, variants); + fn process_interface(&self) -> TokenStream { + let interface_generics = self.extract_generic_argument(); + let multitest_helpers = self.emit_multitest_helpers(&interface_generics); + let querier_bound_for_impl = self.emit_querier_for_bound_impl(); #[cfg(not(tarpaulin_include))] quote! { - #multitest_helpers + pub mod sv { + use super::*; - #querier_bound_for_impl + #multitest_helpers + + #querier_bound_for_impl + } } } - fn process_contract( - &self, - interfaces: &Interfaces, - variants: MsgVariants<'a>, - multitest_helpers: TokenStream, - ) -> TokenStream { + fn process_contract(&self) -> TokenStream { + let Self { + item, + generics, + custom, + .. + } = self; + let multitest_helpers = self.emit_multitest_helpers(generics); + let where_clause = &item.generics.where_clause; + + let querier = MsgVariants::new( + self.item.as_variants(), + MsgType::Query, + generics, + where_clause, + ) + .emit_querier(); let messages = self.emit_messages(); - let remote = Remote::new(interfaces).emit(); - let querier = variants.emit_querier(); - let querier_from_impl = interfaces.emit_querier_from_impl(); + let remote = Remote::new(&self.interfaces).emit(); + let querier_from_impl = self.interfaces.emit_querier_from_impl(); + let contract_api = ContractApi::new(item, generics, custom).emit(); #[cfg(not(tarpaulin_include))] { quote! { - #messages + pub mod sv { + use super::*; - #multitest_helpers + #messages + + #multitest_helpers - #remote + #remote - #querier + #querier - #(#querier_from_impl)* + #(#querier_from_impl)* + + #contract_api + } } } } @@ -214,12 +236,10 @@ impl<'a> ImplInput<'a> { fn emit_messages(&self) -> TokenStream { let instantiate = self.emit_struct_msg(MsgType::Instantiate); let migrate = self.emit_struct_msg(MsgType::Migrate); - let exec_impl = - self.emit_enum_msg(&Ident::new("ExecMsg", Span::mixed_site()), MsgType::Exec); - let query_impl = - self.emit_enum_msg(&Ident::new("QueryMsg", Span::mixed_site()), MsgType::Query); - let exec = self.emit_glue_msg(&Ident::new("ExecMsg", Span::mixed_site()), MsgType::Exec); - let query = self.emit_glue_msg(&Ident::new("QueryMsg", Span::mixed_site()), MsgType::Query); + let exec_impl = self.emit_enum_msg(MsgType::Exec); + let query_impl = self.emit_enum_msg(MsgType::Query); + let exec = self.emit_glue_msg(MsgType::Exec); + let query = self.emit_glue_msg(MsgType::Query); #[cfg(not(tarpaulin_include))] { @@ -244,41 +264,83 @@ impl<'a> ImplInput<'a> { .map_or(quote! {}, |msg| msg.emit()) } - fn emit_enum_msg(&self, name: &Ident, msg_ty: MsgType) -> TokenStream { - ContractEnumMessage::new( - name, - self.item, - msg_ty, - &self.generics, - &self.error, - &self.custom, - ) - .emit() + fn emit_enum_msg(&self, msg_ty: MsgType) -> TokenStream { + ContractEnumMessage::new(self.item, msg_ty, &self.generics, &self.error, &self.custom) + .emit() } - fn emit_glue_msg(&self, name: &Ident, msg_ty: MsgType) -> TokenStream { + fn emit_glue_msg(&self, msg_ty: MsgType) -> TokenStream { + let Self { generics, item, .. } = self; + let where_clause = &item.generics.where_clause; + let variants = MsgVariants::new(item.as_variants(), msg_ty, generics, where_clause); GlueMessage::new( - name, self.item, msg_ty, &self.error, &self.custom, &self.interfaces, + variants, ) .emit() } - fn emit_querier_for_bound_impl( - &self, - interfaces: &Interfaces, - variants: MsgVariants<'a>, - ) -> TokenStream { - let trait_module = interfaces - .interfaces() - .first() + /// This method should only be called for trait impl block + fn extract_generic_argument(&self) -> Vec<&GenericArgument> { + let interface_generics = &self.item.trait_.as_ref(); + let args = match interface_generics { + Some((_, path, _)) => path.segments.last().map(|segment| &segment.arguments), + None => None, + }; + + match args { + Some(PathArguments::AngleBracketed(args)) => { + args.args.pairs().map(|pair| *pair.value()).collect() + } + _ => vec![], + } + } + + fn emit_querier_for_bound_impl(&self) -> TokenStream { + let trait_module = self + .interfaces + .get_only_interface() .map(|interface| &interface.module); let contract_module = self.attributes.module.as_ref(); + let generics = self.extract_generic_argument(); + + let variants = MsgVariants::new(self.item.as_variants(), MsgType::Query, &generics, &None); variants.emit_querier_for_bound_impl(trait_module, contract_module) } + + fn emit_multitest_helpers(&self, generics: &[&Generic]) -> TokenStream + where + Generic: ToTokens + PartialEq + GetPath, + { + let Self { + item, + error, + custom, + override_entry_points, + interfaces, + .. + } = self; + + let is_trait = self.item.trait_.is_some(); + + if cfg!(feature = "mt") { + MultitestHelpers::new( + item, + is_trait, + error, + generics, + custom, + override_entry_points, + interfaces, + ) + .emit() + } else { + quote! {} + } + } } diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs index 94f0d575..fbfe9718 100644 --- a/sylvia-derive/src/interfaces.rs +++ b/sylvia-derive/src/interfaces.rs @@ -15,13 +15,6 @@ pub struct Interfaces { } impl Interfaces { - fn merge_module_with_name(message_attr: &ContractMessageAttr, name: &syn::Ident) -> syn::Ident { - // ContractMessageAttr will fail to parse empty `#[messsages()]` attribute so we can safely unwrap here - let syn::PathSegment { ident, .. } = &message_attr.module.segments.last().unwrap(); - let module_name = ident.to_string().to_case(Case::UpperCamel); - syn::Ident::new(&format!("{}{}", module_name, name), name.span()) - } - pub fn new(source: &ItemImpl) -> Self { let interfaces: Vec<_> = source .attrs @@ -53,7 +46,7 @@ impl Interfaces { self.as_modules() .map(|module| { quote! { - impl<'a, C: #sylvia ::cw_std::CustomQuery> From<&'a BoundQuerier<'a, C>> for #module ::BoundQuerier<'a, C> { + impl<'a, C: #sylvia ::cw_std::CustomQuery> From<&'a BoundQuerier<'a, C>> for #module ::sv::BoundQuerier<'a, C> { fn from(querier: &'a BoundQuerier<'a, C>) -> Self { Self::borrowed(querier.contract(), querier.querier()) } @@ -75,64 +68,70 @@ impl Interfaces { ); quote! { - pub fn #method_name (&self) -> #module ::trait_utils:: #proxy_name <'app, #mt_app> { - #module ::trait_utils:: #proxy_name ::new(self.contract_addr.clone(), self.app) + pub fn #method_name (&self) -> #module ::sv::trait_utils:: #proxy_name <'app, #mt_app> { + #module ::sv::trait_utils:: #proxy_name ::new(self.contract_addr.clone(), self.app) } } }) .collect() } - pub fn emit_glue_message_variants( - &self, - msg_ty: &MsgType, - msg_name: &Ident, - ) -> Vec { + pub fn emit_glue_message_variants(&self, msg_ty: &MsgType) -> Vec { + let sylvia = crate_module(); + self.interfaces .iter() .map(|interface| { let ContractMessageAttr { module, - exec_generic_params, - query_generic_params, variant, + generics, .. } = interface; - - let generics = match msg_ty { - MsgType::Exec => exec_generic_params.as_slice(), - MsgType::Query => query_generic_params.as_slice(), - _ => &[], + let generics = if !generics.is_empty() { + quote! { < #generics > } + } else { + quote! {} }; - let enum_name = Self::merge_module_with_name(interface, msg_name); - quote! { #variant(#module :: #enum_name<#(#generics,)*>) } + let interface_enum = + quote! { <#module ::sv::Api #generics as #sylvia ::types::InterfaceApi> }; + if msg_ty == &MsgType::Query { + quote! { #variant ( #interface_enum :: Query) } + } else { + quote! { #variant ( #interface_enum :: Exec)} + } }) .collect() } - pub fn emit_messages_call(&self, msg_name: &Ident) -> Vec { + pub fn emit_messages_call(&self, msg_ty: &MsgType) -> Vec { self.interfaces .iter() .map(|interface| { - let enum_name = Self::merge_module_with_name(interface, msg_name); - let module = &interface.module; - quote! { &#module :: #enum_name :: messages()} + let ContractMessageAttr { module, .. } = interface; + + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), module.span()); + quote! { + &#module ::sv:: #messages_fn_name() + } }) .collect() } - pub fn emit_deserialization_attempts(&self, msg_name: &Ident) -> Vec { + pub fn emit_deserialization_attempts(&self, msg_ty: &MsgType) -> Vec { self.interfaces .iter() .map(|interface| { let ContractMessageAttr { module, variant, .. } = interface; - let enum_name = Self::merge_module_with_name(interface, msg_name); + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), module.span()); quote! { - let msgs = &#module :: #enum_name ::messages(); + let msgs = &#module ::sv:: #messages_fn_name(); if msgs.into_iter().any(|msg| msg == &recv_msg_name) { match val.deserialize_into() { Ok(msg) => return Ok(Self:: #variant (msg)), @@ -144,13 +143,26 @@ impl Interfaces { .collect() } - pub fn emit_response_schemas_calls(&self, msg_name: &Ident) -> Vec { + pub fn emit_response_schemas_calls(&self, msg_ty: &MsgType) -> Vec { + let sylvia = crate_module(); + self.interfaces .iter() .map(|interface| { - let enum_name = Self::merge_module_with_name(interface, msg_name); - let module = &interface.module; - quote! { #module :: #enum_name :: response_schemas_impl()} + let ContractMessageAttr { + module, generics, .. + } = interface; + + let generics = if !generics.is_empty() { + quote! { < #generics > } + } else { + quote! {} + }; + + let type_name = msg_ty.as_accessor_name(false); + quote! { + <#module ::sv::Api #generics as #sylvia ::types::InterfaceApi> :: #type_name :: response_schemas_impl() + } }) .collect() } @@ -158,4 +170,23 @@ impl Interfaces { pub fn as_modules(&self) -> impl Iterator { self.interfaces.iter().map(|interface| &interface.module) } + + pub fn get_only_interface(&self) -> Option<&ContractMessageAttr> { + let interfaces = &self.interfaces; + match interfaces.len() { + 0 => None, + 1 => Some(&interfaces[0]), + _ => { + let first = &interfaces[0]; + for redefined in &interfaces[1..] { + emit_error!( + redefined.module, "The attribute `messages` is redefined"; + note = first.module.span() => "Previous definition of the attribute `messsages`"; + note = "Only one `messages` attribute can exist on an interface implementation on contract" + ); + } + None + } + } + } } diff --git a/sylvia-derive/src/lib.rs b/sylvia-derive/src/lib.rs index a002c044..a5c6ca8e 100644 --- a/sylvia-derive/src/lib.rs +++ b/sylvia-derive/src/lib.rs @@ -258,9 +258,10 @@ pub fn entry_points(attr: TokenStream, item: TokenStream) -> TokenStream { #[cfg(not(tarpaulin_include))] fn entry_points_impl(attr: TokenStream2, item: TokenStream2) -> TokenStream2 { - fn inner(_attr: TokenStream2, item: TokenStream2) -> syn::Result { + fn inner(attr: TokenStream2, item: TokenStream2) -> syn::Result { + let attrs: parser::EntryPointArgs = parse2(attr)?; let input: ItemImpl = parse2(item)?; - let expanded = EntryPoints::new(&input).emit(); + let expanded = EntryPoints::new(&input, attrs).emit(); Ok(quote! { #input diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 3bb14c52..9a67a37f 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -1,24 +1,27 @@ -use crate::check_generics::CheckGenerics; +use crate::check_generics::{CheckGenerics, GetPath}; use crate::crate_module; use crate::interfaces::Interfaces; use crate::parser::{ parse_associated_custom_type, parse_struct_message, ContractErrorAttr, ContractMessageAttr, - Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints, + Custom, EntryPointArgs, MsgAttr, MsgType, OverrideEntryPoints, }; use crate::strip_generics::StripGenerics; -use crate::utils::{extract_return_type, filter_wheres, process_fields}; +use crate::utils::{ + as_where_clause, emit_bracketed_generics, extract_return_type, filter_wheres, process_fields, +}; use crate::variant_descs::{AsVariantDescs, VariantDescs}; use convert_case::{Case, Casing}; use proc_macro2::{Span, TokenStream}; use proc_macro_error::emit_error; -use quote::quote; +use quote::{quote, ToTokens}; use syn::fold::Fold; use syn::parse::{Parse, Parser}; +use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::visit::Visit; use syn::{ - parse_quote, Attribute, GenericParam, Ident, ImplItem, ItemImpl, ItemTrait, Pat, PatType, Path, - ReturnType, Signature, TraitItem, Type, WhereClause, WherePredicate, + parse_quote, Attribute, GenericArgument, GenericParam, Ident, ItemImpl, ItemTrait, Pat, + PatType, Path, ReturnType, Signature, Token, TraitItem, Type, WhereClause, WherePredicate, }; /// Representation of single struct message @@ -100,14 +103,6 @@ impl<'a> StructMessage<'a> { custom, } = self; - let where_clause = if !wheres.is_empty() { - quote! { - where #(#wheres,)* - } - } else { - quote! {} - }; - let ctx_type = msg_attr .msg_type() .emit_ctx_type(&custom.query_or_default()); @@ -119,21 +114,9 @@ impl<'a> StructMessage<'a> { }); let fields = fields.iter().map(MsgField::emit); - let generics = if generics.is_empty() { - quote! {} - } else { - quote! { - <#(#generics,)*> - } - }; - - let unused_generics = if unused_generics.is_empty() { - quote! {} - } else { - quote! { - <#(#unused_generics,)*> - } - }; + let where_clause = as_where_clause(wheres); + let generics = emit_bracketed_generics(generics); + let unused_generics = emit_bracketed_generics(unused_generics); #[cfg(not(tarpaulin_include))] { @@ -141,7 +124,7 @@ impl<'a> StructMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] #[serde(rename_all="snake_case")] - pub struct #name #generics #where_clause { + pub struct #name #generics { #(pub #fields,)* } @@ -260,9 +243,7 @@ impl<'a> EnumMessage<'a> { query_type, } = self; - let match_arms = variants - .iter() - .map(|variant| variant.emit_dispatch_leg(*msg_ty)); + let match_arms = variants.iter().map(|variant| variant.emit_dispatch_leg()); let mut msgs: Vec = variants .iter() .map(|var| var.name.to_string().to_case(Case::Snake)) @@ -282,20 +263,40 @@ impl<'a> EnumMessage<'a> { let ctx_type = msg_ty.emit_ctx_type(query_type); let dispatch_type = msg_ty.emit_result_type(resp_type, &parse_quote!(C::Error)); - let all_generics = if all_generics.is_empty() { + let all_generics = emit_bracketed_generics(all_generics); + let phantom = if generics.is_empty() { quote! {} + } else if MsgType::Query == *msg_ty { + quote! { + #[serde(skip)] + #[returns((#(#generics,)*))] + _Phantom(std::marker::PhantomData<( #(#generics,)* )>), + } } else { - quote! { <#(#all_generics,)*> } + quote! { + #[serde(skip)] + _Phantom(std::marker::PhantomData<( #(#generics,)* )>), + } }; - let generics = if generics.is_empty() { - quote! {} + let match_arms = if !generics.is_empty() { + quote! { + #(#match_arms,)* + _Phantom(_) => Err(#sylvia ::cw_std::StdError::generic_err("Phantom message should not be constructed.")).map_err(Into::into), + } } else { - quote! { <#(#generics,)*> } + quote! { + #(#match_arms,)* + } }; + let generics = emit_bracketed_generics(generics); + let unique_enum_name = Ident::new(&format!("{}{}", trait_name, name), name.span()); + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), name.span()); + #[cfg(not(tarpaulin_include))] let enum_declaration = match name.to_string().as_str() { "QueryMsg" => { @@ -303,8 +304,9 @@ impl<'a> EnumMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, cosmwasm_schema::QueryResponses)] #[serde(rename_all="snake_case")] - pub enum #unique_enum_name #generics #where_clause { + pub enum #unique_enum_name #generics { #(#variants,)* + #phantom } pub type #name #generics = #unique_enum_name #generics; } @@ -314,8 +316,9 @@ impl<'a> EnumMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] #[serde(rename_all="snake_case")] - pub enum #unique_enum_name #generics #where_clause { + pub enum #unique_enum_name #generics { #(#variants,)* + #phantom } pub type #name #generics = #unique_enum_name #generics; } @@ -334,14 +337,15 @@ impl<'a> EnumMessage<'a> { use #unique_enum_name::*; match self { - #(#match_arms,)* + #match_arms } } - pub const fn messages() -> [&'static str; #msgs_cnt] { - [#(#msgs,)*] - } #(#variants_constructors)* } + + pub const fn #messages_fn_name () -> [&'static str; #msgs_cnt] { + [#(#msgs,)*] + } } } } @@ -349,55 +353,32 @@ impl<'a> EnumMessage<'a> { /// Representation of single enum message pub struct ContractEnumMessage<'a> { - name: &'a Ident, - variants: Vec>, + variants: MsgVariants<'a, GenericParam>, msg_ty: MsgType, contract: &'a Type, error: &'a Type, custom: &'a Custom<'a>, + where_clause: &'a Option, } impl<'a> ContractEnumMessage<'a> { pub fn new( - name: &'a Ident, source: &'a ItemImpl, - ty: MsgType, + msg_ty: MsgType, generics: &'a [&'a GenericParam], error: &'a Type, custom: &'a Custom, ) -> Self { - let mut generics_checker = CheckGenerics::new(generics); - let variants: Vec<_> = source - .items - .iter() - .filter_map(|item| match item { - ImplItem::Method(method) => { - let msg_attr = method.attrs.iter().find(|attr| attr.path.is_ident("msg"))?; - let attr = match MsgAttr::parse.parse2(msg_attr.tokens.clone()) { - Ok(attr) => attr, - Err(err) => { - emit_error!(method.span(), err); - return None; - } - }; - - if attr == ty { - Some(MsgVariant::new(&method.sig, &mut generics_checker, attr)) - } else { - None - } - } - _ => None, - }) - .collect(); + let where_clause = &source.generics.where_clause; + let variants = MsgVariants::new(source.as_variants(), msg_ty, generics, where_clause); Self { - name, variants, - msg_ty: ty, + msg_ty, contract: &source.self_ty, error, custom, + where_clause, } } @@ -405,79 +386,71 @@ impl<'a> ContractEnumMessage<'a> { let sylvia = crate_module(); let Self { - name, variants, msg_ty, contract, error, custom, + where_clause, + .. } = self; - let match_arms = variants - .iter() - .map(|variant| variant.emit_dispatch_leg(*msg_ty)); - let mut msgs: Vec = variants - .iter() - .map(|var| var.name.to_string().to_case(Case::Snake)) - .collect(); - msgs.sort(); - let msgs_cnt = msgs.len(); - let variants_constructors = variants.iter().map(MsgVariant::emit_variants_constructors); - let variants = variants.iter().map(MsgVariant::emit); + let enum_name = msg_ty.emit_msg_name(false); + let match_arms = variants.emit_dispatch_legs(); + let unused_generics = variants.unused_generics(); + let unused_generics = emit_bracketed_generics(unused_generics); + let used_generics = variants.used_generics(); + let used_generics = emit_bracketed_generics(used_generics); + + let mut variant_names = variants.as_names_snake_cased(); + variant_names.sort(); + let variants_cnt = variant_names.len(); + let variants_constructors = variants.emit_constructors(); + let variants = variants.emit(); let ctx_type = msg_ty.emit_ctx_type(&custom.query_or_default()); - let contract = StripGenerics.fold_type((*contract).clone()); let ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), error); - #[cfg(not(tarpaulin_include))] - let enum_declaration = match name.to_string().as_str() { - "QueryMsg" => { - quote! { - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, cosmwasm_schema::QueryResponses)] - #[serde(rename_all="snake_case")] - pub enum #name { - #(#variants,)* - } - } - } - _ => { - quote! { - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] - #[serde(rename_all="snake_case")] - pub enum #name { - #(#variants,)* - } - } - } + let derive_query = match msg_ty { + MsgType::Query => quote! { #sylvia ::cw_schema::QueryResponses }, + _ => quote! {}, }; + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span()); + #[cfg(not(tarpaulin_include))] { quote! { - #enum_declaration + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, #derive_query )] + #[serde(rename_all="snake_case")] + pub enum #enum_name #used_generics { + #(#variants,)* + } - impl #name { - pub fn dispatch(self, contract: &#contract, ctx: #ctx_type) -> #ret_type { - use #name::*; + impl #used_generics #enum_name #used_generics { + pub fn dispatch #unused_generics (self, contract: &#contract, ctx: #ctx_type) -> #ret_type #where_clause { + use #enum_name::*; match self { #(#match_arms,)* } } - pub const fn messages() -> [&'static str; #msgs_cnt] { - [#(#msgs,)*] - } #(#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, @@ -490,11 +463,14 @@ pub struct MsgVariant<'a> { impl<'a> MsgVariant<'a> { /// Creates new message variant from trait method - pub fn new( + pub fn new( sig: &'a Signature, - generics_checker: &mut CheckGenerics, + generics_checker: &mut CheckGenerics, msg_attr: MsgAttr, - ) -> MsgVariant<'a> { + ) -> MsgVariant<'a> + where + Generic: GetPath + PartialEq, + { let function_name = &sig.ident; let name = Ident::new( @@ -506,9 +482,13 @@ impl<'a> MsgVariant<'a> { let return_type = if let MsgAttr::Query { resp_type } = msg_attr { match resp_type { - Some(resp_type) => quote! {#resp_type}, + Some(resp_type) => { + generics_checker.visit_path(&parse_quote! { #resp_type }); + quote! {#resp_type} + } None => { let return_type = extract_return_type(&sig.output); + generics_checker.visit_path(return_type); quote! {#return_type} } } @@ -556,13 +536,14 @@ impl<'a> MsgVariant<'a> { /// Emits match leg dispatching against this variant. Assumes enum variants are imported into the /// scope. Dispatching is performed by calling the function this variant is build from on the /// `contract` variable, with `ctx` as its first argument - both of them should be in scope. - pub fn emit_dispatch_leg(&self, msg_type: MsgType) -> TokenStream { + pub fn emit_dispatch_leg(&self) -> TokenStream { use MsgType::*; let Self { name, fields, function_name, + msg_type, .. } = self; @@ -619,7 +600,11 @@ impl<'a> MsgVariant<'a> { } } - pub fn emit_querier_impl(&self, trait_module: Option<&Path>) -> TokenStream { + pub fn emit_querier_impl( + &self, + trait_module: Option<&Path>, + unbonded_generics: &Vec<&Generic>, + ) -> TokenStream { let sylvia = crate_module(); let Self { name, @@ -631,10 +616,21 @@ impl<'a> MsgVariant<'a> { let parameters = fields.iter().map(MsgField::emit_method_field); let fields_names = fields.iter().map(MsgField::name); let variant_name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); + + // This method is called for `impl Contract` and `impl Trait for Contract` + // In case of first one, `trait_module` will always be `None`. + // In case of the second one, `module` on interface is not an `Option` so it will always be + // `Some` or the compilation will fail earlier. let msg = trait_module - .map(|module| quote! { #module ::QueryMsg }) + .map(|module| quote! { #module ::sv::QueryMsg }) .unwrap_or_else(|| quote! { QueryMsg }); + let msg = if !unbonded_generics.is_empty() { + quote! { #msg ::< #(#unbonded_generics,)* > } + } else { + quote! { #msg } + }; + #[cfg(not(tarpaulin_include))] { quote! { @@ -665,14 +661,183 @@ impl<'a> MsgVariant<'a> { } } } -} -pub struct MsgVariants<'a>(Vec>); + pub fn emit_multitest_proxy_methods( + &self, + msg_ty: &MsgType, + custom_msg: &Type, + mt_app: &Type, + error_type: &Type, + generics: &[&Generic], + ) -> TokenStream + where + Generic: ToTokens, + { + let sylvia = crate_module(); + let Self { + name, + fields, + return_type, + .. + } = self; -impl<'a> MsgVariants<'a> { - pub fn new(source: VariantDescs<'a>, generics: &[&'a GenericParam]) -> Self { - let mut generics_checker = CheckGenerics::new(generics); + let params = fields.iter().map(|field| field.emit_method_field()); + let arguments = fields.iter().map(MsgField::name); + let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); + let enum_name = msg_ty.emit_msg_name(false); + let enum_name: Type = if !generics.is_empty() { + parse_quote! { #enum_name ::< #(#generics,)* > } + } else { + parse_quote! { #enum_name } + }; + + match msg_ty { + MsgType::Exec => quote! { + #[track_caller] + pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #enum_name, #mt_app, #custom_msg> { + let msg = #enum_name :: #name ( #(#arguments),* ); + + #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) + } + }, + MsgType::Migrate => quote! { + #[track_caller] + pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::MigrateProxy::<#error_type, #enum_name, #mt_app, #custom_msg> { + let msg = #enum_name ::new( #(#arguments),* ); + + #sylvia ::multitest::MigrateProxy::new(&self.contract_addr, msg, &self.app) + } + }, + MsgType::Query => quote! { + pub fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { + let msg = #enum_name :: #name ( #(#arguments),* ); + + (*self.app) + .app() + .wrap() + .query_wasm_smart(self.contract_addr.clone(), &msg) + .map_err(Into::into) + } + }, + _ => quote! {}, + } + } + + pub fn emit_interface_multitest_proxy_methods( + &self, + msg_ty: &MsgType, + custom_msg: &Type, + mt_app: &Type, + error_type: &Type, + generics: &[&Generics], + module: &TokenStream, + ) -> TokenStream + where + Generics: ToTokens, + { + let sylvia = crate_module(); + let Self { + name, + fields, + return_type, + .. + } = self; + + let params = fields.iter().map(|field| field.emit_method_field()); + let arguments = fields.iter().map(MsgField::name); + let bracketed_generics = emit_bracketed_generics(generics); + let interface_enum = + quote! { < #module sv::Api #bracketed_generics as #sylvia ::types::InterfaceApi> }; + let type_name = msg_ty.as_accessor_name(false); + let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); + + match msg_ty { + MsgType::Exec => quote! { + #[track_caller] + fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, #mt_app, #custom_msg> { + let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* ); + + #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) + } + }, + MsgType::Query => quote! { + fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { + let msg = #interface_enum :: #type_name :: #name ( #(#arguments),* ); + + (*self.app) + .app() + .wrap() + .query_wasm_smart(self.contract_addr.clone(), &msg) + .map_err(Into::into) + } + }, + _ => quote! {}, + } + } + + pub fn emit_proxy_methods_declarations( + &self, + msg_ty: &MsgType, + custom_msg: &Type, + error_type: &Type, + interface_enum: &TokenStream, + ) -> TokenStream { + let sylvia = crate_module(); + let Self { + name, + fields, + return_type, + .. + } = self; + + let params = fields.iter().map(|field| field.emit_method_field()); + let type_name = msg_ty.as_accessor_name(false); + let name = Ident::new(&name.to_string().to_case(Case::Snake), name.span()); + match msg_ty { + MsgType::Exec => quote! { + fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #interface_enum :: #type_name, MtApp, #custom_msg>; + }, + MsgType::Query => quote! { + fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type>; + }, + _ => quote! {}, + } + } + + pub fn as_fields_names(&self) -> Vec<&Ident> { + self.fields.iter().map(MsgField::name).collect() + } + + pub fn emit_fields(&self) -> Vec { + self.fields.iter().map(MsgField::emit).collect() + } + + pub fn name(&self) -> &Ident { + &self.name + } +} + +#[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 = variant_desc.attr_msg()?; @@ -684,6 +849,10 @@ impl<'a> MsgVariants<'a> { } }; + if attr.msg_type() != msg_ty { + return None; + } + Some(MsgVariant::new( variant_desc.into_sig(), &mut generics_checker, @@ -691,23 +860,71 @@ impl<'a> MsgVariants<'a> { )) }) .collect(); - Self(variants) + + 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) -> &Vec> { + &self.variants + } + + pub fn used_generics(&self) -> &Vec<&'a Generic> { + &self.used_generics + } + + pub fn unused_generics(&self) -> &Vec<&'a Generic> { + &self.unused_generics + } + + pub fn as_where_clause(&'a self) -> Option { + let where_predicates = &self.where_predicates; + if !where_predicates.is_empty() { + Some(parse_quote!( where #(#where_predicates,)* )) + } else { + None + } } pub fn emit_querier(&self) -> TokenStream { let sylvia = crate_module(); - let variants = &self.0; + let Self { + variants, + used_generics, + .. + } = self; + let where_clause = self.where_clause(); let methods_impl = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) - .map(|variant| variant.emit_querier_impl(None)); + .map(|variant| variant.emit_querier_impl(None, used_generics)); let methods_declaration = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) .map(MsgVariant::emit_querier_declaration); + let braced_generics = emit_bracketed_generics(used_generics); + let querier = quote! { Querier #braced_generics }; + #[cfg(not(tarpaulin_include))] { quote! { @@ -730,12 +947,11 @@ impl<'a> MsgVariants<'a> { } } - impl <'a, C: #sylvia ::cw_std::CustomQuery> Querier for BoundQuerier<'a, C> { + impl <'a, C: #sylvia ::cw_std::CustomQuery, #(#used_generics,)*> #querier for BoundQuerier<'a, C> #where_clause { #(#methods_impl)* } - - pub trait Querier { + pub trait #querier { #(#methods_declaration)* } } @@ -748,32 +964,189 @@ impl<'a> MsgVariants<'a> { contract_module: Option<&Path>, ) -> TokenStream { let sylvia = crate_module(); - let variants = &self.0; + let Self { + variants, + used_generics, + .. + } = self; + let where_clause = self.where_clause(); let methods_impl = variants .iter() .filter(|variant| variant.msg_type == MsgType::Query) - .map(|variant| variant.emit_querier_impl(trait_module)); + .map(|variant| variant.emit_querier_impl(trait_module, used_generics)); let querier = trait_module - .map(|module| quote! { #module ::Querier }) - .unwrap_or_else(|| quote! { Querier }); + .map(|module| quote! { #module ::sv::Querier }) + .unwrap_or_else(|| quote! { sv::Querier }); let bound_querier = contract_module - .map(|module| quote! { #module ::BoundQuerier}) - .unwrap_or_else(|| quote! { BoundQuerier }); + .map(|module| quote! { #module ::sv::BoundQuerier}) + .unwrap_or_else(|| quote! { sv::BoundQuerier }); + + let querier = if !used_generics.is_empty() { + quote! { #querier < #(#used_generics,)* > } + } else { + quote! { #querier } + }; #[cfg(not(tarpaulin_include))] { quote! { - impl <'a, C: #sylvia ::cw_std::CustomQuery> #querier for #bound_querier<'a, C> { + impl <'a, C: #sylvia ::cw_std::CustomQuery> #querier for #bound_querier<'a, C > #where_clause { #(#methods_impl)* } } } } + + pub fn emit_multitest_default_dispatch(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { + msg_ty, + used_generics, + .. + } = self; + + let values = msg_ty.emit_ctx_values(); + let msg_name = msg_ty.emit_msg_name(true); + let bracketed_generics = emit_bracketed_generics(used_generics); + + quote! { + #sylvia ::cw_std::from_slice::< #msg_name #bracketed_generics >(&msg)? + .dispatch(self, ( #values )) + .map_err(Into::into) + } + } + + pub fn emit_default_entry_point( + &self, + custom_msg: &Type, + custom_query: &Type, + name: &Type, + error: &Type, + 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_name(true); + + 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_multitest_proxy_methods( + &self, + custom_msg: &Type, + mt_app: &Type, + error_type: &Type, + ) -> Vec { + self.variants + .iter() + .map(|variant| { + variant.emit_multitest_proxy_methods( + &self.msg_ty, + custom_msg, + mt_app, + error_type, + &self.used_generics, + ) + }) + .collect() + } + + pub fn emit_interface_multitest_proxy_methods( + &self, + custom_msg: &Type, + mt_app: &Type, + error_type: &Type, + generics: &[&Generics], + module: &TokenStream, + ) -> Vec + where + Generics: ToTokens, + { + self.variants + .iter() + .map(|variant| { + variant.emit_interface_multitest_proxy_methods( + &self.msg_ty, + custom_msg, + mt_app, + error_type, + generics, + module, + ) + }) + .collect() + } + + pub fn emit_proxy_methods_declarations( + &self, + custom_msg: &Type, + error_type: &Type, + interface_enum: &TokenStream, + ) -> Vec { + self.variants + .iter() + .map(|variant| { + variant.emit_proxy_methods_declarations( + &self.msg_ty, + custom_msg, + error_type, + interface_enum, + ) + }) + .collect() + } + + pub fn emit_dispatch_legs(&self) -> impl Iterator + '_ { + self.variants + .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() + } } /// Representation of single message variant field +#[derive(Debug)] pub struct MsgField<'a> { name: &'a Ident, ty: &'a Type, @@ -782,7 +1155,13 @@ pub struct MsgField<'a> { impl<'a> MsgField<'a> { /// Creates new field from trait method argument - pub fn new(item: &'a PatType, generics_checker: &mut CheckGenerics) -> Option> { + pub fn new( + item: &'a PatType, + generics_checker: &mut CheckGenerics, + ) -> Option> + where + Generic: GetPath + PartialEq, + { let name = match &*item.pat { Pat::Ident(p) => Some(&p.ident), pat => { @@ -847,55 +1226,72 @@ impl<'a> MsgField<'a> { /// Glue message is the message composing Exec/Query messages from several traits #[derive(Debug)] pub struct GlueMessage<'a> { - name: &'a Ident, + source: &'a ItemImpl, contract: &'a Type, msg_ty: MsgType, error: &'a Type, custom: &'a Custom<'a>, interfaces: &'a Interfaces, + variants: MsgVariants<'a, GenericParam>, } impl<'a> GlueMessage<'a> { pub fn new( - name: &'a Ident, source: &'a ItemImpl, msg_ty: MsgType, error: &'a Type, custom: &'a Custom, interfaces: &'a Interfaces, + variants: MsgVariants<'a, GenericParam>, ) -> Self { GlueMessage { - name, + source, contract: &source.self_ty, msg_ty, error, custom, interfaces, + variants, } } pub fn emit(&self) -> TokenStream { let sylvia = crate_module(); - let Self { - name, + source, contract, msg_ty, error, custom, interfaces, + variants, } = self; - let contract = StripGenerics.fold_type((*contract).clone()); - let contract_name = Ident::new(&format!("Contract{}", name), name.span()); - - let variants = interfaces.emit_glue_message_variants(msg_ty, name); - let msg_name = quote! {#contract ( #name)}; - let mut messages_call_on_all_variants: Vec = - interfaces.emit_messages_call(name); - messages_call_on_all_variants.push(quote! {&#name :: messages()}); + let used_generics = variants.used_generics(); + let unused_generics = variants.unused_generics(); + let where_clause = variants.as_where_clause(); + let full_where_clause = &source.generics.where_clause; + + let contract_enum_name = msg_ty.emit_msg_name(true); + let enum_name = msg_ty.emit_msg_name(false); + let contract_name = StripGenerics.fold_type((*contract).clone()); + let unused_generics = emit_bracketed_generics(unused_generics); + let bracketed_used_generics = emit_bracketed_generics(used_generics); + + let variants = interfaces.emit_glue_message_variants(msg_ty); + + let ep_name = msg_ty.emit_ep_name(); + let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span()); + let contract_variant = quote! { #contract_name ( #enum_name #bracketed_used_generics ) }; + let mut messages_call = interfaces.emit_messages_call(msg_ty); + let prefixed_used_generics = if !used_generics.is_empty() { + quote! { :: #bracketed_used_generics } + } else { + quote! {} + }; + messages_call.push(quote! { &#messages_fn_name() }); - let variants_cnt = messages_call_on_all_variants.len(); + let variants_cnt = messages_call.len(); let dispatch_arms = interfaces.interfaces().iter().map(|interface| { let ContractMessageAttr { @@ -916,25 +1312,26 @@ impl<'a> GlueMessage<'a> { match (msg_ty, customs.has_msg) { (MsgType::Exec, true) => quote! { - #contract_name :: #variant(msg) => #sylvia ::into_response::IntoResponse::into_response(msg.dispatch(contract, Into::into( #ctx ))?) + #contract_enum_name:: #variant(msg) => #sylvia ::into_response::IntoResponse::into_response(msg.dispatch(contract, Into::into( #ctx ))?) }, _ => quote! { - #contract_name :: #variant(msg) => msg.dispatch(contract, Into::into( #ctx )) + #contract_enum_name :: #variant(msg) => msg.dispatch(contract, Into::into( #ctx )) }, } }); - let dispatch_arm = quote! {#contract_name :: #contract (msg) =>msg.dispatch(contract, ctx)}; + let dispatch_arm = + quote! {#contract_enum_name :: #contract_name (msg) => msg.dispatch(contract, ctx)}; - let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(name); + let interfaces_deserialization_attempts = interfaces.emit_deserialization_attempts(msg_ty); #[cfg(not(tarpaulin_include))] let contract_deserialization_attempt = quote! { - let msgs = &#name :: messages(); + let msgs = &#messages_fn_name(); if msgs.into_iter().any(|msg| msg == &recv_msg_name) { match val.deserialize_into() { - Ok(msg) => return Ok(Self:: #contract (msg)), - Err(err) => return Err(D::Error::custom(err)).map(Self:: #contract) + Ok(msg) => return Ok(Self:: #contract_name (msg)), + Err(err) => return Err(D::Error::custom(err)).map(Self:: #contract_name ) }; } }; @@ -942,16 +1339,17 @@ impl<'a> GlueMessage<'a> { let ctx_type = msg_ty.emit_ctx_type(&custom.query_or_default()); let ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), error); - let mut response_schemas_calls = interfaces.emit_response_schemas_calls(name); - response_schemas_calls.push(quote! {#name :: response_schemas_impl()}); + let mut response_schemas_calls = interfaces.emit_response_schemas_calls(msg_ty); + response_schemas_calls + .push(quote! {#enum_name #prefixed_used_generics :: response_schemas_impl()}); - let response_schemas = match name.to_string().as_str() { - "QueryMsg" => { + let response_schemas = match msg_ty { + MsgType::Query => { #[cfg(not(tarpaulin_include))] { quote! { #[cfg(not(target_arch = "wasm32"))] - impl cosmwasm_schema::QueryResponses for #contract_name { + impl #bracketed_used_generics #sylvia ::cw_schema::QueryResponses for #contract_enum_name #bracketed_used_generics #where_clause { fn response_schemas_impl() -> std::collections::BTreeMap { let responses = [#(#response_schemas_calls),*]; responses.into_iter().flatten().collect() @@ -971,19 +1369,19 @@ impl<'a> GlueMessage<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] #[serde(rename_all="snake_case", untagged)] - pub enum #contract_name { + pub enum #contract_enum_name #bracketed_used_generics { #(#variants,)* - #msg_name + #contract_variant } - impl #contract_name { - pub fn dispatch( + impl #bracketed_used_generics #contract_enum_name #bracketed_used_generics { + pub fn dispatch #unused_generics ( self, contract: &#contract, ctx: #ctx_type, - ) -> #ret_type { + ) -> #ret_type #full_where_clause { const _: () = { - let msgs: [&[&str]; #variants_cnt] = [#(#messages_call_on_all_variants),*]; + let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*]; #sylvia ::utils::assert_no_intersection(msgs); }; @@ -996,7 +1394,7 @@ impl<'a> GlueMessage<'a> { #response_schemas - impl<'de> serde::Deserialize<'de> for #contract_name { + impl<'de, #(#used_generics,)* > serde::Deserialize<'de> for #contract_enum_name #bracketed_used_generics #where_clause { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { @@ -1019,7 +1417,7 @@ impl<'a> GlueMessage<'a> { #contract_deserialization_attempt } - let msgs: [&[&str]; #variants_cnt] = [#(#messages_call_on_all_variants),*]; + let msgs: [&[&str]; #variants_cnt] = [#(#messages_call),*]; let mut err_msg = msgs.into_iter().flatten().fold( // It might be better to forward the error or serialization, but we just // deserialized it from JSON, not reason to expect failure here. @@ -1038,16 +1436,184 @@ impl<'a> GlueMessage<'a> { } } +pub struct ContractApi<'a> { + source: &'a ItemImpl, + exec_variants: MsgVariants<'a, GenericParam>, + query_variants: MsgVariants<'a, GenericParam>, + instantiate_variants: MsgVariants<'a, GenericParam>, + migrate_variants: MsgVariants<'a, GenericParam>, + generics: &'a [&'a GenericParam], + custom: &'a Custom<'a>, +} + +impl<'a> ContractApi<'a> { + pub fn new( + source: &'a ItemImpl, + generics: &'a [&'a GenericParam], + custom: &'a Custom<'a>, + ) -> Self { + let exec_variants = MsgVariants::new( + source.as_variants(), + MsgType::Exec, + generics, + &source.generics.where_clause, + ); + + let query_variants = MsgVariants::new( + source.as_variants(), + MsgType::Query, + generics, + &source.generics.where_clause, + ); + + let instantiate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Instantiate, + generics, + &source.generics.where_clause, + ); + + let migrate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Migrate, + generics, + &source.generics.where_clause, + ); + + Self { + source, + exec_variants, + query_variants, + instantiate_variants, + migrate_variants, + generics, + custom, + } + } + + pub fn emit(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { + source, + exec_variants, + query_variants, + instantiate_variants, + migrate_variants, + generics, + custom, + } = self; + + let contract_name = &source.self_ty; + let exec_generics = &exec_variants.used_generics; + let query_generics = &query_variants.used_generics; + let instantiate_generics = &instantiate_variants.used_generics; + let migrate_generics = &migrate_variants.used_generics; + + let bracket_generics = emit_bracketed_generics(generics); + let exec_bracketed_generics = emit_bracketed_generics(exec_generics); + let query_bracketed_generics = emit_bracketed_generics(query_generics); + let instantiate_bracketed_generics = emit_bracketed_generics(instantiate_generics); + let migrate_bracketed_generics = emit_bracketed_generics(migrate_generics); + + let migrate_type = match !migrate_variants.variants().is_empty() { + true => quote! { type Migrate = MigrateMsg #migrate_bracketed_generics; }, + false => quote! { type Migrate = #sylvia ::cw_std::Empty; }, + }; + let custom_query = custom.query_or_default(); + + quote! { + impl #bracket_generics #sylvia ::types::ContractApi for #contract_name { + type ContractExec = ContractExecMsg #exec_bracketed_generics; + type ContractQuery = ContractQueryMsg #query_bracketed_generics; + type Exec = ExecMsg #exec_bracketed_generics; + type Query = QueryMsg #query_bracketed_generics; + type Instantiate = InstantiateMsg #instantiate_bracketed_generics; + #migrate_type + type Remote<'remote> = Remote<'remote>; + type Querier<'querier> = BoundQuerier<'querier, #custom_query >; + } + } + } +} + +pub struct InterfaceApi<'a> { + exec_variants: MsgVariants<'a, GenericParam>, + query_variants: MsgVariants<'a, GenericParam>, + generics: &'a [&'a GenericParam], +} + +impl<'a> InterfaceApi<'a> { + pub fn new(source: &'a ItemTrait, generics: &'a [&'a GenericParam]) -> Self { + let exec_variants = MsgVariants::new( + source.as_variants(), + MsgType::Exec, + generics, + &source.generics.where_clause, + ); + + let query_variants = MsgVariants::new( + source.as_variants(), + MsgType::Query, + generics, + &source.generics.where_clause, + ); + + Self { + exec_variants, + query_variants, + generics, + } + } + + pub fn emit(&self) -> TokenStream { + let sylvia = crate_module(); + let Self { + exec_variants, + query_variants, + generics, + } = self; + + let exec_generics = &exec_variants.used_generics; + let query_generics = &query_variants.used_generics; + + let bracket_generics = emit_bracketed_generics(generics); + let exec_bracketed_generics = emit_bracketed_generics(exec_generics); + let query_bracketed_generics = emit_bracketed_generics(query_generics); + + let phantom = if !generics.is_empty() { + quote! { + _phantom: std::marker::PhantomData<( #(#generics,)* )>, + } + } else { + quote! {} + }; + + quote! { + pub struct Api #bracket_generics { + #phantom + } + + impl #bracket_generics #sylvia ::types::InterfaceApi for Api #bracket_generics { + type Exec = ExecMsg #exec_bracketed_generics; + type Query = QueryMsg #query_bracketed_generics; + } + } + } +} + pub struct EntryPoints<'a> { + source: &'a ItemImpl, name: Type, error: Type, custom: Custom<'a>, override_entry_points: OverrideEntryPoints, - variants: MsgVariants<'a>, + generics: Vec<&'a GenericParam>, + where_clause: &'a Option, + attrs: EntryPointArgs, } impl<'a> EntryPoints<'a> { - pub fn new(source: &'a ItemImpl) -> Self { + pub fn new(source: &'a ItemImpl, attrs: EntryPointArgs) -> Self { let sylvia = crate_module(); let name = StripGenerics.fold_type(*source.self_ty.clone()); let override_entry_points = OverrideEntryPoints::new(&source.attrs); @@ -1068,50 +1634,77 @@ impl<'a> EntryPoints<'a> { .unwrap_or_else(|| parse_quote! { #sylvia ::cw_std::StdError }); let generics: Vec<_> = source.generics.params.iter().collect(); - - let variants = MsgVariants::new(source.as_variants(), &generics); + let where_clause = &source.generics.where_clause; let custom = Custom::new(&source.attrs); Self { + source, name, error, custom, override_entry_points, - variants, + generics, + where_clause, + attrs, } } pub fn emit(&self) -> TokenStream { let Self { + source, name, error, custom, override_entry_points, - variants, + generics, + where_clause, + attrs, } = self; let sylvia = crate_module(); let custom_msg = custom.msg_or_default(); let custom_query = custom.query_or_default(); - let reply = variants - .0 - .iter() - .find(|variant| variant.msg_type == MsgType::Reply) - .map(|variant| variant.function_name.clone()); + + let instantiate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Instantiate, + generics, + where_clause, + ); + let exec_variants = + MsgVariants::new(source.as_variants(), MsgType::Exec, generics, where_clause); + let query_variants = + MsgVariants::new(source.as_variants(), MsgType::Query, generics, where_clause); + let migrate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Migrate, + generics, + where_clause, + ); + let reply = + MsgVariants::::new(source.as_variants(), MsgType::Reply, &[], &None) + .variants() + .iter() + .map(|variant| variant.function_name.clone()) + .next(); + let contract_generics = match &attrs.generics { + Some(generics) => quote! { ::< #generics > }, + None => quote! {}, + }; #[cfg(not(tarpaulin_include))] { - let entry_points = [MsgType::Instantiate, MsgType::Exec, MsgType::Query] + let entry_points = [instantiate_variants, exec_variants, query_variants] .into_iter() .map( - |msg_type| match override_entry_points.get_entry_point(msg_type) { + |variants| match override_entry_points.get_entry_point(variants.msg_ty) { Some(_) => quote! {}, - None => OverrideEntryPoint::emit_default_entry_point( + None => variants.emit_default_entry_point( &custom_msg, &custom_query, name, error, - msg_type, + &attrs.generics, ), }, ); @@ -1119,18 +1712,15 @@ impl<'a> EntryPoints<'a> { let migrate_not_overridden = override_entry_points .get_entry_point(MsgType::Migrate) .is_none(); - let migrate_msg_defined = variants - .0 - .iter() - .any(|variant| variant.msg_type == MsgType::Migrate); - let migrate = if migrate_not_overridden && migrate_msg_defined { - OverrideEntryPoint::emit_default_entry_point( + 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, - MsgType::Migrate, + &attrs.generics, ) } else { quote! {} @@ -1147,7 +1737,7 @@ impl<'a> EntryPoints<'a> { env: #sylvia ::cw_std::Env, msg: #sylvia ::cw_std::Reply, ) -> Result<#sylvia ::cw_std::Response < #custom_msg >, #error> { - #name ::new(). #reply((deps, env).into(), msg).map_err(Into::into) + #name #contract_generics ::new(). #reply((deps, env).into(), msg).map_err(Into::into) } }, _ => quote! {}, diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs index 8b871d36..15e788fe 100644 --- a/sylvia-derive/src/multitest.rs +++ b/sylvia-derive/src/multitest.rs @@ -1,161 +1,92 @@ +use convert_case::{Case, Casing}; use proc_macro2::{Ident, TokenStream}; -use proc_macro_error::emit_error; -use quote::quote; -use syn::parse::{Parse, Parser}; -use syn::spanned::Spanned; -use syn::{ - parse_quote, FnArg, GenericParam, ImplItem, ItemImpl, ItemTrait, Pat, PatType, Path, Type, -}; - -use crate::check_generics::CheckGenerics; +use quote::{quote, ToTokens}; +use syn::{parse_quote, ImplItem, ItemImpl, ItemTrait, Path, Type}; + +use crate::check_generics::GetPath; use crate::crate_module; use crate::interfaces::Interfaces; -use crate::message::MsgField; -use crate::parser::{ - parse_struct_message, Custom, MsgAttr, MsgType, OverrideEntryPoint, OverrideEntryPoints, -}; -use crate::utils::{extract_return_type, process_fields}; - -struct MessageSignature<'a> { - pub name: &'a Ident, - pub params: Vec, - pub arguments: Vec<&'a Ident>, - pub msg_ty: MsgType, - pub return_type: TokenStream, -} - -pub struct MultitestHelpers<'a> { - messages: Vec>, - error_type: TokenStream, - contract: &'a Type, - is_trait: bool, - is_migrate: bool, - reply: Option, - source: &'a ItemImpl, - generics: &'a [&'a GenericParam], - contract_name: &'a Ident, - proxy_name: Ident, - custom: &'a Custom<'a>, - override_entry_points: &'a OverrideEntryPoints, - interfaces: &'a Interfaces, -} +use crate::message::{MsgVariant, MsgVariants}; +use crate::parser::{Custom, MsgType, OverrideEntryPoint, OverrideEntryPoints}; +use crate::utils::emit_bracketed_generics; +use crate::variant_descs::AsVariantDescs; fn interface_name(source: &ItemImpl) -> &Ident { let trait_name = &source.trait_; - let Some(trait_name) = trait_name else {unreachable!()}; + let Some(trait_name) = trait_name else { + unreachable!() + }; let (_, Path { segments, .. }, _) = &trait_name; - assert!(!segments.is_empty()); - &segments[0].ident + match segments.last() { + Some(segment) => &segment.ident, + None => unreachable!(), + } } fn extract_contract_name(contract: &Type) -> &Ident { - let Type::Path(type_path) = contract else { - unreachable!() - }; + let Type::Path(type_path) = contract else { + unreachable!() + }; let segments = &type_path.path.segments; assert!(!segments.is_empty()); let segment = &segments[0]; &segment.ident } -impl<'a> MultitestHelpers<'a> { +pub struct MultitestHelpers<'a, Generics> { + error_type: Type, + contract: &'a Type, + is_trait: bool, + source: &'a ItemImpl, + generics: &'a [&'a Generics], + where_clause: &'a Option, + contract_name: &'a Ident, + proxy_name: Ident, + custom: &'a Custom<'a>, + override_entry_points: &'a OverrideEntryPoints, + interfaces: &'a Interfaces, + instantiate_variants: MsgVariants<'a, Generics>, + exec_variants: MsgVariants<'a, Generics>, + query_variants: MsgVariants<'a, Generics>, + migrate_variants: MsgVariants<'a, Generics>, + reply_variants: MsgVariants<'a, Generics>, +} + +impl<'a, Generics> MultitestHelpers<'a, Generics> +where + Generics: ToTokens + PartialEq + GetPath, +{ pub fn new( source: &'a ItemImpl, is_trait: bool, contract_error: &'a Type, - generics: &'a [&'a GenericParam], + generics: &'a [&'a Generics], custom: &'a Custom, override_entry_points: &'a OverrideEntryPoints, interfaces: &'a Interfaces, ) -> Self { - let mut is_migrate = false; - let mut reply = None; - let sylvia = crate_module(); - - let messages: Vec<_> = source - .items - .iter() - .filter_map(|item| match item { - ImplItem::Method(method) => { - let msg_attr = method.attrs.iter().find(|attr| attr.path.is_ident("msg"))?; - let attr = match MsgAttr::parse.parse2(msg_attr.tokens.clone()) { - Ok(attr) => attr, - Err(err) => { - emit_error!(method.span(), err); - return None; - } - }; - let msg_ty = attr.msg_type(); - - if msg_ty == MsgType::Migrate { - is_migrate = true; - } else if msg_ty == MsgType::Reply { - reply = Some(method.sig.ident.clone()); - return None; - } else if msg_ty != MsgType::Query && msg_ty != MsgType::Exec { - return None; - } - - let sig = &method.sig; - let return_type = if let MsgAttr::Query { resp_type } = attr { - match resp_type { - Some(resp_type) => quote! {#resp_type}, - None => { - let return_type = extract_return_type(&sig.output); - quote! {#return_type} - } - } - } else { - quote! { #sylvia ::cw_multi_test::AppResponse } - }; - - let name = &sig.ident; - let params: Vec<_> = sig - .inputs - .iter() - .skip(2) - .filter_map(|arg| match arg { - FnArg::Typed(ty) => { - let name = match ty.pat.as_ref() { - Pat::Ident(ident) => &ident.ident, - _ => return None, - }; - let ty = &ty.ty; - Some(quote! {#name : #ty}) - } - _ => None, - }) - .collect(); - let arguments: Vec<_> = sig - .inputs - .iter() - .skip(2) - .filter_map(|arg| match arg { - FnArg::Typed(item) => { - let PatType { pat, .. } = item; - let Pat::Ident(ident) = pat.as_ref() else { - unreachable!() - }; - Some(&ident.ident) - } - _ => None, - }) - .collect(); - - Some(MessageSignature { - name, - params, - arguments, - msg_ty, - return_type, - }) - } - _ => None, - }) - .collect(); + let where_clause = &source.generics.where_clause; + let instantiate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Instantiate, + generics, + where_clause, + ); + let exec_variants = + MsgVariants::new(source.as_variants(), MsgType::Exec, generics, where_clause); + let query_variants = + MsgVariants::new(source.as_variants(), MsgType::Query, generics, where_clause); + let migrate_variants = MsgVariants::new( + source.as_variants(), + MsgType::Migrate, + generics, + where_clause, + ); + let reply_variants = + MsgVariants::new(source.as_variants(), MsgType::Reply, generics, where_clause); - let error_type = if is_trait { + let error_type: Type = if is_trait { let error_type: Vec<_> = source .items .iter() @@ -178,9 +109,9 @@ impl<'a> MultitestHelpers<'a> { assert!(!error_type.is_empty()); let error_type = error_type[0]; - quote! {#error_type} + parse_quote! {#error_type} } else { - quote! {#contract_error} + parse_quote! {#contract_error} }; let contract = &source.self_ty; @@ -194,30 +125,37 @@ impl<'a> MultitestHelpers<'a> { }; Self { - messages, error_type, contract, is_trait, - is_migrate, - reply, source, generics, + where_clause, contract_name, proxy_name, custom, override_entry_points, interfaces, + instantiate_variants, + exec_variants, + query_variants, + migrate_variants, + reply_variants, } } pub fn emit(&self) -> TokenStream { let Self { - messages, error_type, proxy_name, is_trait, custom, interfaces, + exec_variants, + query_variants, + migrate_variants, + generics, + where_clause, .. } = self; let sylvia = crate_module(); @@ -242,49 +180,15 @@ impl<'a> MultitestHelpers<'a> { > }; - #[cfg(not(tarpaulin_include))] - let messages = messages.iter().map(|msg| { - let MessageSignature { - name, - params, - arguments, - msg_ty, - return_type, - } = msg; - if msg_ty == &MsgType::Exec { - quote! { - #[track_caller] - pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, ExecMsg, #mt_app, #custom_msg> { - let msg = ExecMsg:: #name ( #(#arguments),* ); - - #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) - } - } - } else if msg_ty == &MsgType::Migrate { - quote! { - #[track_caller] - pub fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::MigrateProxy::<#error_type, MigrateMsg, #mt_app, #custom_msg> { - let msg = MigrateMsg::new( #(#arguments),* ); - - #sylvia ::multitest::MigrateProxy::new(&self.contract_addr, msg, &self.app) - } - } - } else if msg_ty == &MsgType::Query { - quote! { - pub fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { - let msg = QueryMsg:: #name ( #(#arguments),* ); - - (*self.app) - .app() - .wrap() - .query_wasm_smart(self.contract_addr.clone(), &msg) - .map_err(Into::into) - } - } - } else { - quote! {} - } - }); + let exec_methods = + exec_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type); + let query_methods = + query_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type); + let migrate_methods = + migrate_variants.emit_multitest_proxy_methods(&custom_msg, &mt_app, error_type); + let where_predicates = where_clause + .as_ref() + .map(|where_clause| &where_clause.predicates); let contract_block = self.generate_contract_helpers(); @@ -300,13 +204,14 @@ impl<'a> MultitestHelpers<'a> { #[derive(Derivative)] #[derivative(Debug)] - pub struct #proxy_name <'app, MtApp> { + pub struct #proxy_name <'app, MtApp, #(#generics,)* > { pub contract_addr: #sylvia ::cw_std::Addr, #[derivative(Debug="ignore")] pub app: &'app #sylvia ::multitest::App, + _phantom: std::marker::PhantomData<( #(#generics,)* )>, } - impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> #proxy_name <'app, #mt_app > + impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > #proxy_name <'app, #mt_app, #(#generics,)* > where CustomT: #sylvia ::cw_multi_test::Module, CustomT::ExecT: std::fmt::Debug @@ -324,23 +229,25 @@ impl<'a> MultitestHelpers<'a> { DistrT: #sylvia ::cw_multi_test::Distribution, IbcT: #sylvia ::cw_multi_test::Ibc, GovT: #sylvia ::cw_multi_test::Gov, - #mt_app : Executor< #custom_msg > + #mt_app : Executor< #custom_msg >, + #where_predicates { pub fn new(contract_addr: #sylvia ::cw_std::Addr, app: &'app #sylvia ::multitest::App< #mt_app >) -> Self { - #proxy_name{ contract_addr, app } + #proxy_name { contract_addr, app, _phantom: std::marker::PhantomData::default() } } - #(#messages)* - - #(#proxy_accessors)* + #( #exec_methods )* + #( #migrate_methods )* + #( #query_methods )* + #( #proxy_accessors )* } - impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT> + impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > From<( #sylvia ::cw_std::Addr, &'app #sylvia ::multitest::App<#mt_app>, )> - for #proxy_name <'app, #mt_app > + for #proxy_name <'app, #mt_app, #(#generics,)* > where CustomT: #sylvia ::cw_multi_test::Module, CustomT::ExecT: std::fmt::Debug @@ -359,9 +266,10 @@ impl<'a> MultitestHelpers<'a> { IbcT: #sylvia ::cw_multi_test::Ibc, GovT: #sylvia ::cw_multi_test::Gov, #mt_app : Executor< #custom_msg >, + #where_predicates { fn from(input: (#sylvia ::cw_std::Addr, &'app #sylvia ::multitest::App< #mt_app >)) - -> #proxy_name<'app, #mt_app > { + -> #proxy_name<'app, #mt_app, #(#generics,)* > { #proxy_name::new(input.0, input.1) } } @@ -374,10 +282,12 @@ impl<'a> MultitestHelpers<'a> { fn impl_trait_on_proxy(&self) -> TokenStream { let Self { - messages, error_type, custom, interfaces, + generics, + exec_variants, + query_variants, .. } = self; @@ -387,34 +297,18 @@ impl<'a> MultitestHelpers<'a> { let proxy_name = &self.proxy_name; let trait_name = Ident::new(&format!("{}", interface_name), interface_name.span()); - let modules: Vec<&Path> = interfaces.as_modules().collect(); - - #[cfg(not(tarpaulin_include))] - let module = match modules.len() { - 0 => { - quote! {} - } - 1 => { - let module = &modules[0]; - quote! {#module ::} - } - _ => { - let first = &modules[0]; - for redefined in &modules[1..] { - emit_error!( - redefined, "The attribute `messages` is redefined"; - note = first.span() => "Previous definition of the attribute `messsages`"; - note = "Only one `messages` attribute can exist on an interface implementation on contract" - ); - } - quote! {} - } - }; + let module = interfaces + .get_only_interface() + .map(|interface| { + let module = &interface.module; + quote! { #module :: } + }) + .unwrap_or(quote! {}); let custom_msg = custom.msg_or_default(); #[cfg(not(tarpaulin_include))] - let mt_app = quote! { + let mt_app = parse_quote! { #sylvia ::cw_multi_test::App< BankT, ApiT, @@ -428,58 +322,31 @@ impl<'a> MultitestHelpers<'a> { > }; - #[cfg(not(tarpaulin_include))] - let methods_definitions = messages.iter().map(|msg| { - let MessageSignature { - name, - params, - arguments, - msg_ty, - return_type, - } = msg; - if msg_ty == &MsgType::Exec { - quote! { - #[track_caller] - fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #module ExecMsg, #mt_app, #custom_msg> { - let msg = #module ExecMsg:: #name ( #(#arguments),* ); - - #sylvia ::multitest::ExecProxy::new(&self.contract_addr, msg, &self.app) - } - } - } else { - quote! { - fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type> { - let msg = #module QueryMsg:: #name ( #(#arguments),* ); - - (*self.app) - .app() - .wrap() - .query_wasm_smart(self.contract_addr.clone(), &msg) - .map_err(Into::into) - } - } - } - }); + let bracketed_generics = emit_bracketed_generics(generics); + let interface_enum = + quote! { < #module sv::Api #bracketed_generics as #sylvia ::types::InterfaceApi> }; - #[cfg(not(tarpaulin_include))] - let methods_declarations = messages.iter().map(|msg| { - let MessageSignature { - name, - params, - msg_ty, - return_type, - .. - } = msg; - if msg_ty == &MsgType::Exec { - quote! { - fn #name (&self, #(#params,)* ) -> #sylvia ::multitest::ExecProxy::<#error_type, #module ExecMsg, MtApp, #custom_msg>; - } - } else { - quote! { - fn #name (&self, #(#params,)* ) -> Result<#return_type, #error_type>; - } - } - }); + let exec_methods = exec_variants.emit_interface_multitest_proxy_methods( + &custom_msg, + &mt_app, + error_type, + generics, + &module, + ); + let query_methods = query_variants.emit_interface_multitest_proxy_methods( + &custom_msg, + &mt_app, + error_type, + generics, + &module, + ); + let exec_methods_declarations = + exec_variants.emit_proxy_methods_declarations(&custom_msg, error_type, &interface_enum); + let query_methods_declarations = query_variants.emit_proxy_methods_declarations( + &custom_msg, + error_type, + &interface_enum, + ); #[cfg(not(tarpaulin_include))] { @@ -488,10 +355,11 @@ impl<'a> MultitestHelpers<'a> { use super::*; pub trait #trait_name { - #(#methods_declarations)* + #(#query_methods_declarations)* + #(#exec_methods_declarations)* } - impl #trait_name< #mt_app > for #module trait_utils:: #proxy_name<'_, #mt_app > + impl #trait_name< #mt_app > for #module sv::trait_utils:: #proxy_name<'_, #mt_app > where CustomT: #sylvia ::cw_multi_test::Module, WasmT: #sylvia ::cw_multi_test::Wasm, @@ -512,8 +380,8 @@ impl<'a> MultitestHelpers<'a> { CustomT::QueryT: #sylvia:: cw_std::CustomQuery + #sylvia ::serde::de::DeserializeOwned + 'static, #mt_app : #sylvia ::cw_multi_test::Executor< #custom_msg > { - - #(#methods_definitions)* + #(#query_methods)* + #(#exec_methods)* } } } @@ -521,13 +389,15 @@ impl<'a> MultitestHelpers<'a> { } fn generate_contract_helpers(&self) -> TokenStream { + let sylvia = crate_module(); let Self { error_type, is_trait, - source, generics, + where_clause, contract_name, proxy_name, + instantiate_variants, .. } = self; @@ -535,18 +405,33 @@ impl<'a> MultitestHelpers<'a> { return quote! {}; } - let sylvia = crate_module(); + let fields_names = instantiate_variants + .get_only_variant() + .map(MsgVariant::as_fields_names) + .unwrap_or(vec![]); - let mut generics_checker = CheckGenerics::new(generics); + let fields = instantiate_variants + .get_only_variant() + .map(MsgVariant::emit_fields) + .unwrap_or(vec![]); - let parsed = parse_struct_message(source, MsgType::Instantiate); - let Some((method,_)) = parsed else { - return quote! {}; + let used_generics = instantiate_variants.used_generics(); + let bracketed_used_generics = emit_bracketed_generics(used_generics); + + let where_predicates = where_clause + .as_ref() + .map(|where_clause| &where_clause.predicates); + let contract = if !generics.is_empty() { + quote! { #contract_name ::< #(#generics,)* > } + } else { + quote! { #contract_name } }; - let instantiate_fields = process_fields(&method.sig, &mut generics_checker); - let fields_names: Vec<_> = instantiate_fields.iter().map(MsgField::name).collect(); - let fields = instantiate_fields.iter().map(MsgField::emit); + let instantiate_msg = if !used_generics.is_empty() { + quote! { InstantiateMsg::< #(#used_generics,)* > } + } else { + quote! { InstantiateMsg } + }; let impl_contract = self.generate_impl_contract(); @@ -573,12 +458,14 @@ impl<'a> MultitestHelpers<'a> { quote! { #impl_contract - pub struct CodeId<'app, MtApp> { + pub struct CodeId<'app, #(#generics,)* MtApp> { code_id: u64, app: &'app #sylvia ::multitest::App, + _phantom: std::marker::PhantomData<( #(#generics,)* )>, + } - impl<'app, BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT> CodeId<'app, #mt_app> + impl<'app, BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT, #(#generics,)* > CodeId<'app, #(#generics,)* #mt_app > where BankT: #sylvia ::cw_multi_test::Bank, ApiT: #sylvia ::cw_std::Api, @@ -588,12 +475,13 @@ impl<'a> MultitestHelpers<'a> { DistrT: #sylvia ::cw_multi_test::Distribution, IbcT: #sylvia ::cw_multi_test::Ibc, GovT: #sylvia ::cw_multi_test::Gov, + #where_predicates { pub fn store_code(app: &'app #sylvia ::multitest::App< #mt_app >) -> Self { let code_id = app .app_mut() - .store_code(Box::new(#contract_name ::new())); - Self { code_id, app } + .store_code(Box::new(#contract ::new())); + Self { code_id, app, _phantom: std::marker::PhantomData::default() } } pub fn code_id(&self) -> u64 { @@ -602,9 +490,9 @@ impl<'a> MultitestHelpers<'a> { pub fn instantiate( &self,#(#fields,)* - ) -> InstantiateProxy<'_, 'app, #mt_app > { - let msg = InstantiateMsg {#(#fields_names,)*}; - InstantiateProxy { + ) -> InstantiateProxy<'_, 'app, #(#generics,)* #mt_app > { + let msg = #instantiate_msg {#(#fields_names,)*}; + InstantiateProxy::< #(#generics,)* _> { code_id: self, funds: &[], label: "Contract", @@ -614,23 +502,24 @@ impl<'a> MultitestHelpers<'a> { } } - pub struct InstantiateProxy<'a, 'app, MtApp> { - code_id: &'a CodeId <'app, MtApp>, - funds: &'a [#sylvia ::cw_std::Coin], - label: &'a str, + pub struct InstantiateProxy<'proxy, 'app, #(#generics,)* MtApp> { + code_id: &'proxy CodeId <'app, #(#generics,)* MtApp>, + funds: &'proxy [#sylvia ::cw_std::Coin], + label: &'proxy str, admin: Option, - msg: InstantiateMsg, + msg: InstantiateMsg #bracketed_used_generics, } - impl<'a, 'app, MtApp> InstantiateProxy<'a, 'app, MtApp> + impl<'proxy, 'app, #(#generics,)* MtApp> InstantiateProxy<'proxy, 'app, #(#generics,)* MtApp> where MtApp: Executor< #custom_msg >, + #where_predicates { - pub fn with_funds(self, funds: &'a [#sylvia ::cw_std::Coin]) -> Self { + pub fn with_funds(self, funds: &'proxy [#sylvia ::cw_std::Coin]) -> Self { Self { funds, ..self } } - pub fn with_label(self, label: &'a str) -> Self { + pub fn with_label(self, label: &'proxy str) -> Self { Self { label, ..self } } @@ -640,7 +529,7 @@ impl<'a> MultitestHelpers<'a> { } #[track_caller] - pub fn call(self, sender: &str) -> Result<#proxy_name<'app, MtApp>, #error_type> { + pub fn call(self, sender: &str) -> Result<#proxy_name<'app, MtApp, #(#generics,)* >, #error_type> { (*self.code_id.app) .app_mut() .instantiate_contract( @@ -655,6 +544,7 @@ impl<'a> MultitestHelpers<'a> { .map(|addr| #proxy_name { contract_addr: addr, app: self.code_id.app, + _phantom: std::marker::PhantomData::default(), }) } } @@ -664,29 +554,36 @@ impl<'a> MultitestHelpers<'a> { fn generate_impl_contract(&self) -> TokenStream { let Self { + source, contract, custom, override_entry_points, + generics, + instantiate_variants, + exec_variants, + query_variants, + migrate_variants, + reply_variants, .. } = self; let sylvia = crate_module(); + let bracketed_generics = emit_bracketed_generics(generics); + let full_where_clause = &source.generics.where_clause; let instantiate_body = override_entry_points .get_entry_point(MsgType::Instantiate) .map(OverrideEntryPoint::emit_multitest_dispatch) - .unwrap_or_else(|| { - OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Instantiate) - }); + .unwrap_or_else(|| instantiate_variants.emit_multitest_default_dispatch()); let exec_body = override_entry_points .get_entry_point(MsgType::Exec) .map(OverrideEntryPoint::emit_multitest_dispatch) - .unwrap_or_else(|| OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Exec)); + .unwrap_or_else(|| exec_variants.emit_multitest_default_dispatch()); let query_body = override_entry_points .get_entry_point(MsgType::Query) .map(OverrideEntryPoint::emit_multitest_dispatch) - .unwrap_or_else(|| OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Query)); + .unwrap_or_else(|| query_variants.emit_multitest_default_dispatch()); let sudo_body = override_entry_points .get_entry_point(MsgType::Sudo) @@ -699,8 +596,8 @@ impl<'a> MultitestHelpers<'a> { let migrate_body = match override_entry_points.get_entry_point(MsgType::Migrate) { Some(entry_point) => entry_point.emit_multitest_dispatch(), - None if self.is_migrate => { - OverrideEntryPoint::emit_multitest_default_dispatch(MsgType::Migrate) + None if migrate_variants.get_only_variant().is_some() => { + migrate_variants.emit_multitest_default_dispatch() } None => quote! { #sylvia ::anyhow::bail!("migrate not implemented for contract") @@ -709,12 +606,17 @@ impl<'a> MultitestHelpers<'a> { let reply_body = match override_entry_points.get_entry_point(MsgType::Reply) { Some(entry_point) => entry_point.emit_multitest_dispatch(), - None => self - .reply + None => reply_variants + .get_only_variant() .as_ref() .map(|reply| { + let reply_name = reply.name(); + let reply_name = Ident::new( + &reply_name.to_string().to_case(Case::Snake), + reply_name.span(), + ); quote! { - self. #reply((deps, env).into(), msg).map_err(Into::into) + self. #reply_name ((deps, env).into(), msg).map_err(Into::into) } }) .unwrap_or_else(|| { @@ -730,7 +632,7 @@ impl<'a> MultitestHelpers<'a> { #[cfg(not(tarpaulin_include))] { quote! { - impl #sylvia ::cw_multi_test::Contract<#custom_msg, #custom_query> for #contract { + impl #bracketed_generics #sylvia ::cw_multi_test::Contract<#custom_msg, #custom_query> for #contract #full_where_clause { fn execute( &self, deps: #sylvia ::cw_std::DepsMut< #custom_query >, diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs index dbf6440f..569e9f83 100644 --- a/sylvia-derive/src/parser.rs +++ b/sylvia-derive/src/parser.rs @@ -1,18 +1,22 @@ use proc_macro2::{Punct, TokenStream}; use proc_macro_error::emit_error; use quote::quote; +use syn::fold::Fold; use syn::parse::{Error, Nothing, Parse, ParseBuffer, ParseStream, Parser}; +use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ - parenthesized, parse_quote, Attribute, Ident, ImplItem, ImplItemMethod, ItemImpl, ItemTrait, - Path, Result, Token, TraitItem, Type, + parenthesized, parse_quote, Attribute, GenericArgument, Ident, ImplItem, ImplItemMethod, + ItemImpl, ItemTrait, Path, PathArguments, Result, Token, TraitItem, Type, }; use crate::crate_module; +use crate::strip_generics::StripGenerics; -/// Parser arguments for `contract` macro +/// Parsed arguments for `contract` macro pub struct ContractArgs { - /// Module name wrapping generated messages, by default no additional module is created + /// Module in which contract impl block is defined. + /// Used only while implementing `Interface` on `Contract`. pub module: Option, } @@ -43,6 +47,31 @@ impl Parse for ContractArgs { } } +/// Parsed arguments for `entry_points` macro +pub struct EntryPointArgs { + /// Types used in place of contracts generics. + pub generics: Option>, +} + +impl Parse for EntryPointArgs { + fn parse(input: ParseStream) -> Result { + if input.is_empty() { + return Ok(Self { generics: None }); + } + + let path: Path = input.parse()?; + + let generics = match path.segments.last() { + Some(segment) if segment.ident == "generics" => Some(extract_generics_from_path(&path)), + _ => return Err(Error::new(path.span(), "Expected `generics`")), + }; + + let _: Nothing = input.parse()?; + + Ok(Self { generics }) + } +} + /// Type of message to be generated #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum MsgType { @@ -102,16 +131,16 @@ impl MsgType { } #[cfg(not(tarpaulin_include))] - pub fn emit_ep_name(self) -> TokenStream { + pub fn emit_ep_name(self) -> Ident { use MsgType::*; match self { - Exec => quote! { execute }, - Instantiate => quote! { instantiate }, - Migrate => quote! { migrate }, - Sudo => quote! { sudo }, - Reply => quote! { reply }, - Query => quote! { query }, + Exec => parse_quote! { execute }, + Instantiate => parse_quote! { instantiate }, + Migrate => parse_quote! { migrate }, + Sudo => parse_quote! { sudo }, + Reply => parse_quote! { reply }, + Query => parse_quote! { query }, } } @@ -142,16 +171,31 @@ impl MsgType { } } - pub fn emit_msg_name(&self) -> Type { + pub fn emit_msg_name(&self, is_wrapper: bool) -> Type { match self { - MsgType::Exec => parse_quote! { ContractExecMsg }, - MsgType::Query => parse_quote! { ContractQueryMsg }, + MsgType::Exec if is_wrapper => parse_quote! { ContractExecMsg }, + MsgType::Query if is_wrapper => parse_quote! { ContractQueryMsg }, + MsgType::Exec => parse_quote! { ExecMsg }, + MsgType::Query => parse_quote! { QueryMsg }, MsgType::Instantiate => parse_quote! { InstantiateMsg }, MsgType::Migrate => parse_quote! { MigrateMsg }, MsgType::Reply => parse_quote! { ReplyMsg }, MsgType::Sudo => todo!(), } } + + pub fn as_accessor_name(&self, is_wrapper: bool) -> Option { + match self { + MsgType::Exec if is_wrapper => Some(parse_quote! { ContractExec }), + MsgType::Query if is_wrapper => Some(parse_quote! { ContractQuery }), + MsgType::Instantiate => Some(parse_quote! { Instantiate }), + MsgType::Exec => Some(parse_quote! { Exec }), + MsgType::Query => Some(parse_quote! { Query }), + MsgType::Migrate => Some(parse_quote! { Migrate }), + MsgType::Sudo => Some(parse_quote! { Sudo }), + MsgType::Reply => Some(parse_quote! { Reply }), + } + } } impl PartialEq for MsgAttr { @@ -238,34 +282,9 @@ pub struct Customs { #[derive(Debug)] pub struct ContractMessageAttr { pub module: Path, - pub exec_generic_params: Vec, - pub query_generic_params: Vec, pub variant: Ident, pub customs: Customs, -} - -#[cfg(not(tarpaulin_include))] -// False negative. Called in function below -fn parse_generics(content: &ParseBuffer) -> Result> { - let _: Token![<] = content.parse()?; - let mut params = vec![]; - - loop { - let param: Path = content.parse()?; - params.push(param); - - let generics_close: Option]> = content.parse()?; - if generics_close.is_some() { - break; - } - - let comma: Option = content.parse()?; - if comma.is_none() { - return Err(Error::new(content.span(), "Expected comma or `>`")); - } - } - - Ok(params) + pub generics: Punctuated, } fn interface_has_custom(content: ParseStream) -> Result { @@ -307,6 +326,26 @@ fn interface_has_custom(content: ParseStream) -> Result { Ok(customs) } +fn extract_generics_from_path(module: &Path) -> Punctuated { + let generics = module.segments.last().map(|segment| { + match segment.arguments.clone(){ + PathArguments::AngleBracketed(generics) => { + generics.args + }, + PathArguments::None => Default::default(), + PathArguments::Parenthesized(generics) => { + emit_error!( + generics.span(), "Found paranthesis wrapping generics in `messages` attribute."; + note = "Expected `messages` attribute to be in form `#[messages(Path as Type)]`" + ); + Default::default() + } + } + }).unwrap_or_default(); + + generics +} + #[cfg(not(tarpaulin_include))] // False negative. It is being called in closure impl Parse for ContractMessageAttr { @@ -315,31 +354,8 @@ impl Parse for ContractMessageAttr { parenthesized!(content in input); let module = content.parse()?; - - let generics_open: Option = content.parse()?; - let mut exec_generic_params = vec![]; - let mut query_generic_params = vec![]; - - if generics_open.is_some() { - loop { - let ty: Ident = content.parse()?; - let params = if ty == "exec" { - &mut exec_generic_params - } else if ty == "query" { - &mut query_generic_params - } else { - return Err(Error::new(ty.span(), "Invalid message type")); - }; - - *params = parse_generics(&content)?; - - if content.peek(Token![as]) { - break; - } - - let _: Token![,] = content.parse()?; - } - } + let generics = extract_generics_from_path(&module); + let module = StripGenerics.fold_path(module); let _: Token![as] = content.parse()?; let variant = content.parse()?; @@ -355,10 +371,9 @@ impl Parse for ContractMessageAttr { Ok(Self { module, - exec_generic_params, - query_generic_params, variant, customs, + generics, }) } } @@ -523,6 +538,7 @@ impl OverrideEntryPoint { entry_point, msg_name, msg_type, + .. } = self; let sylvia = crate_module(); @@ -533,49 +549,6 @@ impl OverrideEntryPoint { .map_err(Into::into) } } - - pub fn emit_multitest_default_dispatch(ty: MsgType) -> TokenStream { - let sylvia = crate_module(); - - let values = ty.emit_ctx_values(); - let msg_name = ty.emit_msg_name(); - - quote! { - #sylvia ::cw_std::from_slice::< #msg_name >(&msg)? - .dispatch(self, ( #values )) - .map_err(Into::into) - } - } - - #[cfg(not(tarpaulin_include))] - pub fn emit_default_entry_point( - custom_msg: &Type, - custom_query: &Type, - name: &Type, - error: &Type, - msg_type: MsgType, - ) -> TokenStream { - let sylvia = crate_module(); - - let resp_type = match msg_type { - MsgType::Query => quote! { #sylvia ::cw_std::Binary }, - _ => quote! { #sylvia ::cw_std::Response < #custom_msg > }, - }; - let params = msg_type.emit_ctx_params(custom_query); - let values = msg_type.emit_ctx_values(); - let ep_name = msg_type.emit_ep_name(); - let msg_name = msg_type.emit_msg_name(); - - quote! { - #[#sylvia ::cw_std::entry_point] - pub fn #ep_name ( - #params , - msg: #msg_name, - ) -> Result<#resp_type, #error> { - msg.dispatch(&#name ::new() , ( #values )).map_err(Into::into) - } - } - } } #[derive(Debug)] diff --git a/sylvia-derive/src/remote.rs b/sylvia-derive/src/remote.rs index 4d673f1f..76fcdf1a 100644 --- a/sylvia-derive/src/remote.rs +++ b/sylvia-derive/src/remote.rs @@ -21,9 +21,9 @@ impl<'a> Remote<'a> { let ContractMessageAttr { module, .. } = interface; quote! { - impl<'a> From<&'a Remote<'a>> for #module ::Remote<'a> { + impl<'a> From<&'a Remote<'a>> for #module ::sv::Remote<'a> { fn from(remote: &'a Remote) -> Self { - #module ::Remote::borrowed(remote.as_ref()) + #module ::sv::Remote::borrowed(remote.as_ref()) } } } @@ -33,13 +33,11 @@ impl<'a> Remote<'a> { #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] pub struct Remote<'a>(std::borrow::Cow<'a, #sylvia ::cw_std::Addr>); - impl Remote<'static> { + impl<'a> Remote<'a> { pub fn new(addr: #sylvia ::cw_std::Addr) -> Self { Self(std::borrow::Cow::Owned(addr)) } - } - impl<'a> Remote<'a> { pub fn borrowed(addr: &'a #sylvia ::cw_std::Addr) -> Self { Self(std::borrow::Cow::Borrowed(addr)) } diff --git a/sylvia-derive/src/utils.rs b/sylvia-derive/src/utils.rs index 108e6db4..276deb3d 100644 --- a/sylvia-derive/src/utils.rs +++ b/sylvia-derive/src/utils.rs @@ -1,19 +1,21 @@ +use proc_macro2::TokenStream; use proc_macro_error::emit_error; +use quote::{quote, ToTokens}; use syn::spanned::Spanned; use syn::visit::Visit; use syn::{ - FnArg, GenericArgument, GenericParam, PathArguments, PathSegment, ReturnType, Signature, Type, + parse_quote, FnArg, GenericArgument, Path, PathArguments, ReturnType, Signature, Type, WhereClause, WherePredicate, }; -use crate::check_generics::CheckGenerics; +use crate::check_generics::{CheckGenerics, GetPath}; use crate::message::MsgField; #[cfg(not(tarpaulin_include))] -pub fn filter_wheres<'a>( +pub fn filter_wheres<'a, Generic: GetPath + PartialEq>( clause: &'a Option, - generics: &[&GenericParam], - used_generics: &[&GenericParam], + generics: &[&Generic], + used_generics: &[&Generic], ) -> Vec<&'a WherePredicate> { clause .as_ref() @@ -34,10 +36,13 @@ pub fn filter_wheres<'a>( .unwrap_or_default() } -pub fn process_fields<'s>( +pub fn process_fields<'s, Generic>( sig: &'s Signature, - generics_checker: &mut CheckGenerics, -) -> Vec> { + generics_checker: &mut CheckGenerics, +) -> Vec> +where + Generic: GetPath + PartialEq, +{ sig.inputs .iter() .skip(2) @@ -52,14 +57,14 @@ pub fn process_fields<'s>( .collect() } -pub fn extract_return_type(ret_type: &ReturnType) -> &PathSegment { - let ReturnType::Type(_, ty) = ret_type else { - unreachable!() - }; +pub fn extract_return_type(ret_type: &ReturnType) -> &Path { + let ReturnType::Type(_, ty) = ret_type else { + unreachable!() + }; - let Type::Path(type_path) = ty.as_ref() else { - unreachable!() - }; + let Type::Path(type_path) = ty.as_ref() else { + unreachable!() + }; let segments = &type_path.path.segments; assert!(!segments.is_empty()); let segment = &segments[0]; @@ -73,16 +78,28 @@ pub fn extract_return_type(ret_type: &ReturnType) -> &PathSegment { Please use #[msg(return_type=)]" ); } - let PathArguments::AngleBracketed(args) = &segments[0].arguments else{ - unreachable!() - }; + let PathArguments::AngleBracketed(args) = &segments[0].arguments else { + unreachable!() + }; let args = &args.args; assert!(!args.is_empty()); - let GenericArgument::Type(Type::Path(type_path)) = &args[0] else{ - unreachable!() - }; - let segments = &type_path.path.segments; - assert!(!segments.is_empty()); + let GenericArgument::Type(Type::Path(type_path)) = &args[0] else { + unreachable!() + }; + + &type_path.path +} - &segments[0] +pub fn as_where_clause(where_predicates: &[&WherePredicate]) -> Option { + match where_predicates.is_empty() { + true => None, + false => Some(parse_quote! { where #(#where_predicates),* }), + } +} + +pub fn emit_bracketed_generics(unbonded_generics: &[&Generic]) -> TokenStream { + match unbonded_generics.is_empty() { + true => quote! {}, + false => quote! { < #(#unbonded_generics,)* > }, + } } diff --git a/sylvia/examples/basic.rs b/sylvia/examples/basic.rs index 07f04d7a..404aa660 100644 --- a/sylvia/examples/basic.rs +++ b/sylvia/examples/basic.rs @@ -32,12 +32,11 @@ pub struct MemberResp { } mod group { - use anyhow::Error; use cosmwasm_std::{Response, StdError}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; - use crate::{GroupContract, Member, MemberResp}; + use crate::{Member, MemberResp}; #[interface] pub trait Group { @@ -61,9 +60,19 @@ mod group { #[msg(query)] fn member(&self, ctx: QueryCtx, addr: String) -> Result; } +} + +mod impl_group { + use anyhow::Error; + use cosmwasm_std::Response; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; + + use crate::{GroupContract, MemberResp}; - #[contract(module=super)] - impl Group for GroupContract { + #[contract(module=crate)] + #[messages(crate::group as Group)] + impl crate::group::Group for GroupContract { type Error = Error; #[msg(exec)] @@ -80,7 +89,7 @@ mod group { &self, _ctx: ExecCtx, _remove: Vec, - _add: Vec, + _add: Vec, ) -> Result { todo!() } diff --git a/sylvia/examples/generics.rs b/sylvia/examples/generics.rs deleted file mode 100644 index 75acbed5..00000000 --- a/sylvia/examples/generics.rs +++ /dev/null @@ -1,17 +0,0 @@ -use cosmwasm_std::{CosmosMsg, Response}; - -use sylvia::types::ExecCtx; -use sylvia_derive::interface; - -#[interface(module=msg)] -pub trait Cw1 -where - Msg: std::fmt::Debug + PartialEq + Clone + schemars::JsonSchema, -{ - type Error; - - #[msg(exec)] - fn execute(&self, ctx: ExecCtx, msgs: Vec>) -> Result; -} - -fn main() {} diff --git a/sylvia/src/into_response.rs b/sylvia/src/into_response.rs index b3a3e73d..06f974f3 100644 --- a/sylvia/src/into_response.rs +++ b/sylvia/src/into_response.rs @@ -52,7 +52,7 @@ impl IntoResponse for Response { .map(|msg| msg.into_msg()) .collect::>()?; let mut resp = Response::new() - .add_submessages(messages.into_iter()) + .add_submessages(messages) .add_events(self.events) .add_attributes(self.attributes); resp.data = self.data; diff --git a/sylvia/src/lib.rs b/sylvia/src/lib.rs index f3502c47..66afeb6f 100644 --- a/sylvia/src/lib.rs +++ b/sylvia/src/lib.rs @@ -10,6 +10,7 @@ pub mod utils; #[cfg(feature = "mt")] pub use anyhow; +pub use cosmwasm_schema as cw_schema; pub use cosmwasm_std as cw_std; #[cfg(feature = "mt")] pub use cw_multi_test; diff --git a/sylvia/src/types.rs b/sylvia/src/types.rs index 725d57d3..d5c3d330 100644 --- a/sylvia/src/types.rs +++ b/sylvia/src/types.rs @@ -1,4 +1,6 @@ +use cosmwasm_schema::cw_serde; use cosmwasm_std::{CustomQuery, Deps, DepsMut, Empty, Env, MessageInfo}; +use serde::de::DeserializeOwned; pub struct ReplyCtx<'a, C: CustomQuery = Empty> { pub deps: DepsMut<'a, C>, @@ -93,3 +95,28 @@ impl<'a, C: CustomQuery> From<(Deps<'a, C>, Env)> for QueryCtx<'a, C> { Self { deps, env } } } + +pub trait CustomMsg: cosmwasm_std::CustomMsg + DeserializeOwned {} + +impl CustomMsg for T where T: cosmwasm_std::CustomMsg + DeserializeOwned {} + +#[cw_serde] +pub struct SvCustomMsg; + +impl cosmwasm_std::CustomMsg for SvCustomMsg {} + +pub trait InterfaceApi { + type Exec; + type Query; +} + +pub trait ContractApi { + type Instantiate; + type Query; + type Exec; + type ContractQuery; + type ContractExec; + type Migrate; + type Querier<'querier>; + type Remote<'remote>; +} diff --git a/sylvia/tests/api.rs b/sylvia/tests/api.rs new file mode 100644 index 00000000..e9c39a68 --- /dev/null +++ b/sylvia/tests/api.rs @@ -0,0 +1,127 @@ +use cosmwasm_std::{Response, StdResult}; +use std::marker::PhantomData; + +use sylvia::types::{CustomMsg, ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx}; +use sylvia_derive::contract; + +pub struct SomeContract { + _phantom: PhantomData<(Instantiate, Query, Exec, Migrate, Ret)>, +} + +#[contract] +impl SomeContract +where + Instantiate: CustomMsg + 'static, + Query: CustomMsg + 'static, + Exec: CustomMsg + 'static, + Migrate: CustomMsg + 'static, + Ret: CustomMsg + 'static, +{ + pub const fn new() -> Self { + Self { + _phantom: PhantomData, + } + } + + #[msg(instantiate)] + pub fn instantiate(&self, _ctx: InstantiateCtx, _param: Instantiate) -> StdResult { + Ok(Response::new()) + } + + #[msg(exec)] + pub fn exec(&self, _ctx: ExecCtx, _param: Exec) -> StdResult { + Ok(Response::new()) + } + + #[msg(query)] + pub fn query(&self, _ctx: QueryCtx, _param: Query) -> StdResult { + Ok(Response::new()) + } + + #[msg(migrate)] + pub fn migrate(&self, _ctx: MigrateCtx, _param: Migrate) -> StdResult { + Ok(Response::new()) + } +} + +#[cfg(test)] +mod tests { + use crate::SomeContract; + use cosmwasm_std::{testing::mock_dependencies, Addr, QuerierWrapper}; + use sylvia::types::{ContractApi, SvCustomMsg}; + + #[test] + fn api() { + let owner = Addr::unchecked("owner"); + + let _: crate::sv::InstantiateMsg = as ContractApi>::Instantiate::new( + SvCustomMsg + ); + + let exec: crate::sv::ExecMsg = as ContractApi>::Exec::exec( + SvCustomMsg + ); + + let query: crate::sv::QueryMsg = as ContractApi>::Query::query( + SvCustomMsg + ); + + let _: crate::sv::ContractExecMsg = as ContractApi>::ContractExec::SomeContract( + exec + ); + + let _: crate::sv::ContractQueryMsg = as ContractApi>::ContractQuery::SomeContract( + query + ); + + let _: crate::sv::Remote<'_> = as ContractApi>::Remote::new(owner.clone()); + + let deps = mock_dependencies(); + let querier_wrapper: QuerierWrapper = QuerierWrapper::new(&deps.querier); + let _: crate::sv::BoundQuerier<'_, cosmwasm_std::Empty> = as ContractApi>::Querier::borrowed( + &owner, &querier_wrapper + ); + } +} diff --git a/sylvia/tests/custom_msg.rs b/sylvia/tests/custom_msg.rs index 9365ba44..55e6693c 100644 --- a/sylvia/tests/custom_msg.rs +++ b/sylvia/tests/custom_msg.rs @@ -21,8 +21,8 @@ pub struct SomeResponse; mod some_interface { use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; use crate::{MyMsg, SomeResponse}; @@ -39,8 +39,18 @@ mod some_interface { #[msg(exec)] fn interface_exec(&self, ctx: ExecCtx) -> StdResult>; } +} + +mod impl_some_interface { + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::{ExecCtx, QueryCtx}; + + use crate::some_interface::SomeInterface; + use crate::{MyMsg, SomeResponse}; - #[contract(module=super)] + #[contract(module=crate)] + #[messages(crate::some_interface as SomeInterface)] #[sv::custom(msg=MyMsg)] impl SomeInterface for crate::MyContract { type Error = StdError; @@ -59,10 +69,10 @@ mod some_interface { // Use `#[sv::custom(..)]` if both it and associated type defined mod interface { - use crate::{MyMsg, OtherMsg}; + use crate::MyMsg; use cosmwasm_std::{CustomMsg, Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::ExecCtx; - use sylvia::{contract, interface}; #[interface] #[sv::custom(msg=MyMsg)] @@ -74,8 +84,16 @@ mod interface { #[msg(exec)] fn exec(&self, ctx: ExecCtx) -> StdResult>; } +} +mod impl_interface { + use crate::interface::Interface; + use crate::{MyMsg, OtherMsg}; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::ExecCtx; - #[contract(module=super)] + #[contract(module=crate)] + #[messages(crate::interface as Interface)] #[sv::custom(msg=MyMsg)] impl Interface for crate::MyContract { type Error = StdError; @@ -90,8 +108,8 @@ mod interface { mod other_interface { use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::ExecCtx; - use sylvia::{contract, interface}; #[interface] pub trait OtherInterface { @@ -101,8 +119,15 @@ mod other_interface { #[msg(exec)] fn other_interface_exec(&self, ctx: ExecCtx) -> StdResult; } +} +mod impl_other_interface { + use crate::other_interface::OtherInterface; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::ExecCtx; - #[contract(module=super)] + #[contract(module=crate)] + #[messages(crate::other_interface as OtherInterface)] #[sv::custom(msg=crate::MyMsg)] impl OtherInterface for crate::MyContract { type Error = StdError; @@ -115,10 +140,9 @@ mod other_interface { } mod associated_interface { - use crate::MyMsg; use cosmwasm_std::{CustomMsg, Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::ExecCtx; - use sylvia::{contract, interface}; #[interface] pub trait AssociatedInterface { @@ -129,8 +153,16 @@ mod associated_interface { #[msg(exec)] fn associated_exec(&self, ctx: ExecCtx) -> StdResult>; } +} +mod impl_associated_interface { + use crate::associated_interface::AssociatedInterface; + use crate::MyMsg; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::ExecCtx; - #[contract(module=super)] + #[contract(module=crate)] + #[messages(crate::associated_interface as AssociatedInterface)] #[sv::custom(msg=MyMsg)] impl AssociatedInterface for crate::MyContract { type Error = StdError; @@ -179,20 +211,19 @@ impl MyContract { #[cfg(all(test, feature = "mt"))] mod tests { - use crate::interface::test_utils::Interface; + use crate::impl_associated_interface::sv::test_utils::AssociatedInterface; + use crate::impl_interface::sv::test_utils::Interface; + use crate::impl_other_interface::sv::test_utils::OtherInterface; + use crate::impl_some_interface::sv::test_utils::SomeInterface; use crate::MyContract; - use sylvia::multitest::App; - - use crate::associated_interface::test_utils::AssociatedInterface; - use crate::other_interface::test_utils::OtherInterface; - use crate::some_interface::test_utils::SomeInterface; use crate::MyMsg; + use sylvia::multitest::App; #[test] fn test_custom() { let _ = MyContract::new(); let app = App::>::custom(|_, _, _| {}); - let code_id = crate::multitest_utils::CodeId::store_code(&app); + let code_id = crate::sv::multitest_utils::CodeId::store_code(&app); let owner = "owner"; diff --git a/sylvia/tests/custom_query.rs b/sylvia/tests/custom_query.rs index cb92e811..00c33d34 100644 --- a/sylvia/tests/custom_query.rs +++ b/sylvia/tests/custom_query.rs @@ -20,10 +20,10 @@ pub struct SomeResponse; mod interface { use cosmwasm_std::{CustomQuery, Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; - use crate::{MyQuery, OtherQuery, SomeResponse}; + use crate::{MyQuery, SomeResponse}; #[interface] #[sv::custom(query=MyQuery)] @@ -39,10 +39,19 @@ mod interface { #[msg(exec)] fn interface_exec(&self, ctx: ExecCtx) -> StdResult; } +} + +mod impl_interface { + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; - #[contract(module=super)] + use crate::{MyQuery, OtherQuery, SomeResponse}; + + #[contract(module=crate)] + #[messages(crate::interface as Interface)] #[sv::custom(query=MyQuery)] - impl Interface for crate::MyContract { + impl crate::interface::Interface for crate::MyContract { type Error = StdError; type QueryC = OtherQuery; @@ -60,8 +69,8 @@ mod interface { mod some_interface { use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; use crate::{MyQuery, SomeResponse}; @@ -78,10 +87,19 @@ mod some_interface { #[msg(exec)] fn some_interface_exec(&self, ctx: ExecCtx) -> StdResult; } +} + +mod impl_some_interface { + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; - #[contract(module=super)] + use crate::{MyQuery, SomeResponse}; + + #[contract(module=crate)] + #[messages(crate::some_interface as SomeInterface)] #[sv::custom(query=MyQuery)] - impl SomeInterface for crate::MyContract { + impl super::some_interface::SomeInterface for crate::MyContract { type Error = StdError; #[msg(query)] @@ -98,10 +116,10 @@ mod some_interface { mod associated_type_interface { use cosmwasm_std::{CustomQuery, Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; - use crate::{MyQuery, SomeResponse}; + use crate::SomeResponse; #[interface] pub trait AssociatedTypeInterface { @@ -116,8 +134,16 @@ mod associated_type_interface { #[msg(exec)] fn associated_exec(&self, ctx: ExecCtx) -> StdResult; } +} + +mod impl_associated_type_interface { + use crate::{associated_type_interface::AssociatedTypeInterface, MyQuery, SomeResponse}; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; - #[contract(module=super)] + #[contract(module=crate)] + #[messages(crate::associated_type_interface as AssociatedTypeInterface)] impl AssociatedTypeInterface for crate::MyContract { type Error = StdError; type QueryC = MyQuery; @@ -136,8 +162,8 @@ mod associated_type_interface { mod default_query_interface { use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; use crate::SomeResponse; @@ -153,8 +179,16 @@ mod default_query_interface { #[msg(exec)] fn default_exec(&self, ctx: ExecCtx) -> StdResult; } +} + +mod impl_default_query_interface { + use crate::{default_query_interface::DefaultQueryInterface, SomeResponse}; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::types::{ExecCtx, QueryCtx}; + use sylvia_derive::contract; - #[contract(module=super)] + #[contract(module=crate)] + #[messages(crate::default_query_interface as DefaultQueryInterface)] #[sv::custom(query=MyQuery)] impl DefaultQueryInterface for crate::MyContract { type Error = StdError; @@ -207,10 +241,11 @@ impl MyContract { #[cfg(all(test, feature = "mt"))] mod tests { - use crate::associated_type_interface::test_utils::AssociatedTypeInterface; - use crate::default_query_interface::test_utils::DefaultQueryInterface; - use crate::some_interface::test_utils::SomeInterface; - use crate::{interface::test_utils::Interface, MyContract, MyQuery}; + use crate::impl_associated_type_interface::sv::test_utils::AssociatedTypeInterface; + use crate::impl_default_query_interface::sv::test_utils::DefaultQueryInterface; + use crate::impl_interface::sv::test_utils::Interface; + use crate::impl_some_interface::sv::test_utils::SomeInterface; + use crate::{MyContract, MyQuery}; use cosmwasm_std::Empty; use sylvia::multitest::App; @@ -219,7 +254,7 @@ mod tests { fn test_custom() { let _ = MyContract::new(); let app = App::>::custom(|_, _, _| {}); - let code_id = crate::multitest_utils::CodeId::store_code(&app); + let code_id = crate::sv::multitest_utils::CodeId::store_code(&app); let owner = "owner"; diff --git a/sylvia/tests/dispatching.rs b/sylvia/tests/dispatching.rs index 6858b551..155354cf 100644 --- a/sylvia/tests/dispatching.rs +++ b/sylvia/tests/dispatching.rs @@ -1,12 +1,20 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; -use cosmwasm_std::{from_binary, Addr, Decimal, Response, StdError, StdResult}; -use interface::Interface; +use cosmwasm_std::{from_binary, Addr, Decimal, Response}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::cell::RefCell; -use std::collections::HashMap; -use sylvia::contract; -use sylvia::types::{ExecCtx, QueryCtx}; + +use crate::contract::Contract; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct EmptyQueryResponse {} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct QueryResponse { + coef: Decimal, + desc: String, +} mod interface { use cosmwasm_std::{Addr, Decimal, Response, StdError}; @@ -40,77 +48,83 @@ mod interface { } } -#[derive(Default)] -pub struct Contract { - execs: RefCell, - queries: RefCell, +mod impl_interface { + use cosmwasm_std::{Addr, Decimal, Response, StdError}; + use sylvia::types::{ExecCtx, QueryCtx}; - data: RefCell>, -} + use crate::{EmptyQueryResponse, QueryResponse}; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct EmptyQueryResponse {} + #[sylvia::contract(module = crate::contract)] + #[messages(crate::interface as Interface)] + impl crate::interface::Interface for crate::contract::Contract { + type Error = StdError; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct QueryResponse { - coef: Decimal, - desc: String, -} - -#[contract] -#[messages(interface as Interface)] -impl Interface for Contract { - type Error = StdError; + #[msg(exec)] + fn no_args_execution(&self, _: ExecCtx) -> Result { + *self.execs.borrow_mut() += 1; + Ok(Response::new()) + } - #[msg(exec)] - fn no_args_execution(&self, _: ExecCtx) -> Result { - *self.execs.borrow_mut() += 1; - Ok(Response::new()) - } + #[msg(exec)] + fn argumented_execution( + &self, + _: ExecCtx, + addr: Addr, + coef: Decimal, + desc: String, + ) -> Result { + *self.execs.borrow_mut() += 1; - #[msg(exec)] - fn argumented_execution( - &self, - _: ExecCtx, - addr: Addr, - coef: Decimal, - desc: String, - ) -> Result { - *self.execs.borrow_mut() += 1; - - self.data - .borrow_mut() - .insert(addr, QueryResponse { coef, desc }); - Ok(Response::new()) - } + self.data + .borrow_mut() + .insert(addr, QueryResponse { coef, desc }); + Ok(Response::new()) + } - #[msg(query)] - fn no_args_query(&self, _: QueryCtx) -> Result { - *self.queries.borrow_mut() += 1; - Ok(dbg!(EmptyQueryResponse {})) - } + #[msg(query)] + fn no_args_query(&self, _: QueryCtx) -> Result { + *self.queries.borrow_mut() += 1; + Ok(dbg!(EmptyQueryResponse {})) + } - #[msg(query)] - fn argumented_query(&self, _: QueryCtx, user: Addr) -> Result { - *self.queries.borrow_mut() += 1; - Ok(self.data.borrow().get(&user).unwrap().clone()) + #[msg(query)] + fn argumented_query(&self, _: QueryCtx, user: Addr) -> Result { + *self.queries.borrow_mut() += 1; + Ok(self.data.borrow().get(&user).unwrap().clone()) + } } } -#[allow(dead_code)] -#[cfg(not(tarpaulin_include))] -#[contract] -#[messages(interface as Interface)] -impl Contract { - fn new() -> Self { - Self::default() +mod contract { + use std::{cell::RefCell, collections::HashMap}; + + use cosmwasm_std::{Addr, Response, StdResult}; + use sylvia::types::ExecCtx; + use sylvia_derive::contract; + + use crate::QueryResponse; + + #[derive(Default)] + pub struct Contract { + pub(crate) execs: RefCell, + pub(crate) queries: RefCell, + + pub(crate) data: RefCell>, } - #[msg(instantiate)] - fn instanciate(&self, _: ExecCtx) -> StdResult { - Ok(Response::new()) + #[allow(dead_code)] + #[cfg(not(tarpaulin_include))] + #[contract] + #[messages(crate::interface as Interface)] + impl Contract { + fn new() -> Self { + Self::default() + } + + #[msg(instantiate)] + fn instanciate(&self, _: ExecCtx) -> StdResult { + Ok(Response::new()) + } } } @@ -122,12 +136,12 @@ fn dispatch() { let env = mock_env(); let info = mock_info("owner", &[]); - let resp = interface::ExecMsg::NoArgsExecution {} + let resp = interface::sv::ExecMsg::NoArgsExecution {} .dispatch(&contract, (deps.as_mut(), env.clone(), info.clone())) .unwrap(); assert_eq!(resp, Response::new()); - let resp = interface::ExecMsg::ArgumentedExecution { + let resp = interface::sv::ExecMsg::ArgumentedExecution { addr: Addr::unchecked("addr1"), coef: Decimal::percent(30), desc: "True".to_owned(), @@ -136,7 +150,7 @@ fn dispatch() { .unwrap(); assert_eq!(resp, Response::new()); - let resp = interface::ExecMsg::ArgumentedExecution { + let resp = interface::sv::ExecMsg::ArgumentedExecution { addr: Addr::unchecked("addr2"), coef: Decimal::percent(70), desc: "False".to_owned(), @@ -145,12 +159,12 @@ fn dispatch() { .unwrap(); assert_eq!(resp, Response::new()); - let resp = interface::QueryMsg::NoArgsQuery {} + let resp = interface::sv::QueryMsg::NoArgsQuery {} .dispatch(&contract, (deps.as_ref(), env.clone())) .unwrap(); let _resp: EmptyQueryResponse = from_binary(&resp).unwrap(); - let resp = interface::QueryMsg::ArgumentedQuery { + let resp = interface::sv::QueryMsg::ArgumentedQuery { user: Addr::unchecked("addr2"), } .dispatch(&contract, (deps.as_ref(), env)) diff --git a/sylvia/tests/entry_points.rs b/sylvia/tests/entry_points.rs index d7bbb2e4..951dfee6 100644 --- a/sylvia/tests/entry_points.rs +++ b/sylvia/tests/entry_points.rs @@ -1,4 +1,4 @@ -use contract::ContractExecMsg; +use contract::sv::ContractExecMsg; use cosmwasm_schema::cw_serde; use cosmwasm_std::Coin; use cw_storage_plus::Item; @@ -145,8 +145,8 @@ mod tests { use cw_multi_test::Executor; use sylvia::multitest::App; - use crate::contract::multitest_utils::CodeId; - use crate::contract::{ContractExecMsg, ExecMsg}; + use crate::contract::sv::multitest_utils::CodeId; + use crate::contract::sv::{ContractExecMsg, ExecMsg}; use crate::exec::{CustomExecMsg, UserExecMsg}; use crate::SudoMsg; diff --git a/sylvia/tests/messages_generation.rs b/sylvia/tests/messages_generation.rs index 2495fc69..6fe0b947 100644 --- a/sylvia/tests/messages_generation.rs +++ b/sylvia/tests/messages_generation.rs @@ -112,53 +112,53 @@ mod contract { #[test] fn interface_messages_constructible() { - let no_args_exec = interface::ExecMsg::NoArgsExecution {}; - let _argumented_exec = interface::ExecMsg::ArgumentedExecution { + let no_args_exec = interface::sv::ExecMsg::NoArgsExecution {}; + let _argumented_exec = interface::sv::ExecMsg::ArgumentedExecution { addr: Addr::unchecked("owner"), coef: Decimal::percent(10), desc: "Some description".to_owned(), }; - let no_args_query = interface::QueryMsg::NoArgsQuery {}; - let _argumented_query = interface::QueryMsg::ArgumentedQuery { + let no_args_query = interface::sv::QueryMsg::NoArgsQuery {}; + let _argumented_query = interface::sv::QueryMsg::ArgumentedQuery { user: Addr::unchecked("owner"), }; // Ensure no extra variants are generated match no_args_exec { - interface::ExecMsg::NoArgsExecution {} => (), - interface::ExecMsg::ArgumentedExecution { .. } => (), + interface::sv::ExecMsg::NoArgsExecution {} => (), + interface::sv::ExecMsg::ArgumentedExecution { .. } => (), } match no_args_query { - interface::QueryMsg::NoArgsQuery {} => (), - interface::QueryMsg::ArgumentedQuery { .. } => (), + interface::sv::QueryMsg::NoArgsQuery {} => (), + interface::sv::QueryMsg::ArgumentedQuery { .. } => (), } } #[test] fn contract_messages_constructible() { - let no_args_exec = contract::ExecMsg::NoArgsExecution {}; - let _argumented_exec = contract::ExecMsg::ArgumentedExecution { + let no_args_exec = contract::sv::ExecMsg::NoArgsExecution {}; + let _argumented_exec = contract::sv::ExecMsg::ArgumentedExecution { _addr: Addr::unchecked("owner"), _coef: Decimal::percent(10), _desc: "Some description".to_owned(), }; - let no_args_query = contract::QueryMsg::NoArgsQuery {}; - let _argumented_query = contract::QueryMsg::ArgumentedQuery { + let no_args_query = contract::sv::QueryMsg::NoArgsQuery {}; + let _argumented_query = contract::sv::QueryMsg::ArgumentedQuery { _user: Addr::unchecked("owner"), }; - let _ = contract::InstantiateMsg {}; - let _ = contract::MigrateMsg {}; + let _ = contract::sv::InstantiateMsg {}; + let _ = contract::sv::MigrateMsg {}; // Ensure no extra variants are generated match no_args_exec { - contract::ExecMsg::NoArgsExecution {} => (), - contract::ExecMsg::ArgumentedExecution { .. } => (), + contract::sv::ExecMsg::NoArgsExecution {} => (), + contract::sv::ExecMsg::ArgumentedExecution { .. } => (), } match no_args_query { - contract::QueryMsg::NoArgsQuery {} => (), - contract::QueryMsg::ArgumentedQuery { .. } => (), + contract::sv::QueryMsg::NoArgsQuery {} => (), + contract::sv::QueryMsg::ArgumentedQuery { .. } => (), } } diff --git a/sylvia/tests/querier.rs b/sylvia/tests/querier.rs index 191e8800..9a78ed3b 100644 --- a/sylvia/tests/querier.rs +++ b/sylvia/tests/querier.rs @@ -14,8 +14,8 @@ pub struct CountResponse { pub mod counter { use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx}; - use sylvia::{contract, interface}; use crate::CountResponse; @@ -35,8 +35,17 @@ pub mod counter { #[msg(exec)] fn decrease_by_count(&self, ctx: ExecCtx) -> StdResult; } +} - #[contract(module=super)] +pub mod impl_counter { + use crate::counter::sv::Querier; + use crate::counter::Counter; + use crate::CountResponse; + use cosmwasm_std::{Response, StdError, StdResult}; + use sylvia::contract; + use sylvia::types::{ExecCtx, QueryCtx}; + + #[contract(module=crate)] #[messages(crate::counter as Counter)] impl Counter for super::CounterContract<'_> { type Error = StdError; @@ -69,9 +78,10 @@ pub mod counter { #[msg(exec)] fn decrease_by_count(&self, ctx: ExecCtx) -> StdResult { let remote = self.remote.load(ctx.deps.storage)?; - let other_count = BoundQuerier::borrowed(&remote.0, &ctx.deps.querier) - .count()? - .count; + let other_count = + crate::counter::sv::BoundQuerier::borrowed(remote.as_ref(), &ctx.deps.querier) + .count()? + .count; self.count.update(ctx.deps.storage, |count| { let count = count.saturating_sub(other_count); Ok::<_, StdError>(count) @@ -84,7 +94,7 @@ pub mod counter { pub struct CounterContract<'a> { pub count: Item<'static, u64>, - pub remote: Item<'static, Remote<'a>>, + pub remote: Item<'static, sv::Remote<'a>>, } #[contract] @@ -102,7 +112,7 @@ impl CounterContract<'_> { fn instantiate(&self, ctx: InstantiateCtx, remote_addr: Addr) -> StdResult { self.count.save(ctx.deps.storage, &0)?; self.remote - .save(ctx.deps.storage, &Remote::new(remote_addr))?; + .save(ctx.deps.storage, &sv::Remote::new(remote_addr))?; Ok(Response::new()) } } @@ -114,8 +124,8 @@ mod tests { use cosmwasm_std::{Addr, Empty, QuerierWrapper}; use sylvia::multitest::App; - use crate::counter::test_utils::Counter; - use crate::multitest_utils::CodeId; + use crate::impl_counter::sv::test_utils::Counter; + use crate::sv::multitest_utils::CodeId; #[test] fn querier_generation() { @@ -124,16 +134,16 @@ mod tests { let remote_addr = Addr::unchecked("remote"); // Remote generation - let remote = super::counter::Remote::new(remote_addr.clone()); - let _: super::counter::BoundQuerier<_> = remote.querier(&querier_wrapper); - let remote = super::Remote::new(remote_addr.clone()); - let _: super::BoundQuerier<_> = remote.querier(&querier_wrapper); + let remote = super::counter::sv::Remote::new(remote_addr.clone()); + let _: super::counter::sv::BoundQuerier<_> = remote.querier(&querier_wrapper); + let remote = super::sv::Remote::new(remote_addr.clone()); + let _: super::sv::BoundQuerier<_> = remote.querier(&querier_wrapper); // Querier generation - let _ = super::counter::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); - let querier = super::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); + let _ = super::counter::sv::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); + let querier = super::sv::BoundQuerier::borrowed(&remote_addr, &querier_wrapper); - let _ = super::counter::BoundQuerier::from(&querier); + let _ = super::counter::sv::BoundQuerier::from(&querier); } #[test] diff --git a/sylvia/tests/query_returns.rs b/sylvia/tests/query_returns.rs index f4dd6d08..b85dd6a1 100644 --- a/sylvia/tests/query_returns.rs +++ b/sylvia/tests/query_returns.rs @@ -62,14 +62,14 @@ mod tests { #[test] fn generate_interface_query() { - let _ = msg::InterfaceQueryMsg::Query { + let _ = msg::sv::InterfaceQueryMsg::Query { name: "some_name".to_owned(), }; } #[test] fn generate_contract_query() { - let _ = QueryMsg::ContractQuery { + let _ = sv::QueryMsg::ContractQuery { _name: "some_name".to_owned(), }; } diff --git a/sylvia/tests/remote.rs b/sylvia/tests/remote.rs index 0b61719b..4c937085 100644 --- a/sylvia/tests/remote.rs +++ b/sylvia/tests/remote.rs @@ -37,7 +37,7 @@ impl SomeContract { #[cw_serde] #[allow(dead_code)] struct CustomStorage { - remote: crate::Remote<'static>, + remote: crate::sv::Remote<'static>, } #[cfg(test)] @@ -49,17 +49,17 @@ mod tests { #[test] fn remote_generation() { // interface - let _ = some_interface::Remote::new(Addr::unchecked("some_interface")); + let _ = some_interface::sv::Remote::new(Addr::unchecked("some_interface")); let addr = Addr::unchecked("some_interface"); - let _ = some_interface::Remote::borrowed(&addr); + let _ = some_interface::sv::Remote::borrowed(&addr); // contract - let new_remote = crate::Remote::new(Addr::unchecked("some_contract")); + let new_remote = crate::sv::Remote::new(Addr::unchecked("some_contract")); let addr = Addr::unchecked("some_contract"); - let borrowed_remote = crate::Remote::borrowed(&addr); + let borrowed_remote = crate::sv::Remote::borrowed(&addr); assert_eq!(&Addr::unchecked("some_contract"), borrowed_remote.as_ref()); - let _ = some_interface::Remote::from(&borrowed_remote); - let _ = some_interface::Remote::from(&new_remote); + let _ = some_interface::sv::Remote::from(&borrowed_remote); + let _ = some_interface::sv::Remote::from(&new_remote); } } diff --git a/sylvia/tests/replies.rs b/sylvia/tests/replies.rs index a7bf5021..979db3cc 100644 --- a/sylvia/tests/replies.rs +++ b/sylvia/tests/replies.rs @@ -55,7 +55,7 @@ mod reply_contract { #[msg(exec)] fn poke(&self, _ctx: ExecCtx, noop: String) -> StdResult { - let msg = noop_contract::ExecMsg::Noop {}; + let msg = noop_contract::sv::ExecMsg::Noop {}; let msg = WasmMsg::Execute { contract_addr: noop, msg: to_binary(&msg)?, @@ -93,16 +93,16 @@ fn entry_point_generation() { assert_eq!(data, "data"); } -#[cfg(feature = "mt")] +#[cfg(all(test, feature = "mt"))] #[test] fn mt_helper_generation() { let app = sylvia::multitest::App::default(); let owner = "owner"; - let noop_contract_code = noop_contract::multitest_utils::CodeId::store_code(&app); + let noop_contract_code = noop_contract::sv::multitest_utils::CodeId::store_code(&app); let noop_contract = noop_contract_code.instantiate().call(owner).unwrap(); - let reply_contract_code = reply_contract::multitest_utils::CodeId::store_code(&app); + let reply_contract_code = reply_contract::sv::multitest_utils::CodeId::store_code(&app); let reply_contract = reply_contract_code.instantiate().call(owner).unwrap(); let resp = reply_contract