Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Review changes to feature merge #456

Merged
merged 5 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions sylvia-derive/src/contract/communication/reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,8 @@ impl<'a> ReplyData<'a> {
quote! { #sylvia ::cw_std::ReplyOn::Always }
} else if is_success {
quote! { #sylvia ::cw_std::ReplyOn::Success }
} else if is_error {
quote! { #sylvia ::cw_std::ReplyOn::Error }
} else {
// This should never happen.
// We parse only the `Success`, `Error` and `Always` values which are covered above.
// Handling the `Never` value wouldn't make sense as we would create a dead handler.
quote! { #sylvia ::cw_std::ReplyOn::Never }
quote! { #sylvia ::cw_std::ReplyOn::Error }
}
}

Expand Down
62 changes: 61 additions & 1 deletion sylvia-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ pub(crate) fn crate_module() -> Path {
/// generated message type. It can be used along with `sv::msg(...)`
/// and only for message types variants that resolves in an enum field,
/// i.e. `exec`, `query` and `sudo`.
///
#[proc_macro_error]
#[proc_macro_attribute]
pub fn interface(attr: TokenStream, item: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -640,6 +639,67 @@ fn interface_impl(_attr: TokenStream2, item: TokenStream2) -> TokenStream2 {
///
/// User can also specify custom `data` and `payload` types that will be auto
/// deserialized from the `cosmwasm_std::Binary` type.
///
/// ### `sv::payload(raw)`
///
/// Requires contract to be marked with the `sv::features(replies)`.
///
/// Used next to the reply method argument. It disables auto deserialization
/// of the payload argument.
///
/// ### `sv::data(...)`
///
/// Requires contract to be marked with the `sv::features(replies)`.
///
/// Used next to the reply method argument. Based on the passed parameters
/// it enables different behavior:
///
/// * `#[sv::data(raw)]` - Returns error if the data is `None`, extracts and forwards
/// `Binary` if it's `Some`.
///
/// * `#[sv::data(raw, opt)]` - Forwards `data` as `Option<Binary>`.
///
/// * `#[sv::data(opt)]` - Expects type of `data` in the method signature to be
/// `data: Option<T> where T: Deserialize`.
/// Tries to deserialize `data` to type defined in the method signature
/// and forwards it wrapped in the `Option`.
/// Requires `data` to be JSON serialized.
///
/// If `data` is:
/// | `Some(valid)` | `Some(invalid)` | `None` |
/// |---|---|---|
/// | forwards `Some(valid)` | early returns an error specifying what went wrong with `serde` error attached | `None` - forwards `None` |
///
/// * `#[sv::data]` - Expects data in the method signature to be `data: T where T: Deserialize`.
/// Tries to deserialize data to type defined in the method signature
/// and forwards it or returns early an error.
/// Requires `data` to be JSON serialized.
///
/// If `data` is:
/// | `Some(valid)` | `Some(invalid)` | `None` |
/// |---|---|---|
/// | forwards `valid` | early returns error specifying what went wrong with `serde` error attached | early returns error specifying the `data` is missing |
///
/// * `#[sv::data(instantiate)]` - special case for reply to `WasmMsg::instantiate` and
/// `WasmMsg::instantiate2`. Tries to deserialize data using
/// [`parse_instantiate_response_data`](https://docs.rs/cw-utils/latest/cw_utils/fn.parse_instantiate_response_data.html).
///
/// If `data` is:
/// | `Some(valid)` | `Some(invalid)` | `None` |
/// |---|---|---|
/// | extracts and forwards `valid` | early returns error specifying what went wrong with `serde` error attached | early returns error specifying the `data` is missing |
///
/// * `#[sv::data(instantiate, opt)]` - special case for reply to `WasmMsg::instantiate` and
/// `WasmMsg::instantiate2`. tries to deserialize data using
/// [`parse_instantiate_response_data`](https://docs.rs/cw-utils/latest/cw_utils/fn.parse_instantiate_response_data.html).
///
/// if `data` is:
/// | `Some(valid)` | `Some(invalid)` | `None` |
/// |---|---|---|
/// | forwards `Some(valid)` | early returns error specifying what went wrong with `serde` error attached | Forwards `None` |
///
/// * Missing `#[sv::data(...)]` - In case `sv::data` is not found Sylvia won't forward the `data` argument
/// so the `data` should be omited in the method signature.
#[proc_macro_error]
#[proc_macro_attribute]
pub fn contract(attr: TokenStream, item: TokenStream) -> TokenStream {
Expand Down
84 changes: 76 additions & 8 deletions sylvia/tests/reply_data.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::to_json_binary;
use cw_storage_plus::Item;
use cw_utils::{MsgInstantiateContractResponse, ParseReplyError};
use noop_contract::sv::{Executor, NoopContractInstantiateBuilder};
Expand Down Expand Up @@ -224,23 +225,37 @@ impl Contract {
}
}

use crate::noop_contract::sv::mt::CodeId as NoopCodeId;
use crate::sv::mt::{CodeId, ContractProxy};
use crate::sv::{DATA_OPT_REPLY_ID, DATA_RAW_OPT_REPLY_ID, DATA_RAW_REPLY_ID, DATA_REPLY_ID};

use sylvia::cw_multi_test::IntoBech32;
use sylvia::multitest::App;

#[test]
fn dispatch_replies() {
use crate::noop_contract::sv::mt::CodeId as NoopCodeId;
use crate::sv::mt::{CodeId, ContractProxy};
use crate::sv::{DATA_OPT_REPLY_ID, DATA_RAW_OPT_REPLY_ID, DATA_RAW_REPLY_ID, DATA_REPLY_ID};
fn data_instantiate() {
let app = App::default();
let code_id = CodeId::store_code(&app);
let noop_code_id = NoopCodeId::store_code(&app);

use cosmwasm_std::{to_json_binary, Binary, StdError};
use sylvia::cw_multi_test::IntoBech32;
use sylvia::multitest::App;
let owner = "owner".into_bech32();

// Trigger remote instantiation reply
let _ = code_id
.instantiate(noop_code_id.code_id())
.with_label("Contract")
.call(&owner)
.unwrap();
}

#[test]
fn data_raw_opt() {
let app = App::default();
let code_id = CodeId::store_code(&app);
let noop_code_id = NoopCodeId::store_code(&app);

let owner = "owner".into_bech32();
let data = Some(to_json_binary(&String::from("some_data")).unwrap());
let invalid_data = Some(Binary::from("InvalidData".as_bytes()));

// Trigger remote instantiation reply
let contract = code_id
Expand All @@ -259,6 +274,23 @@ fn dispatch_replies() {
.send_message_expecting_data(data.clone(), DATA_RAW_OPT_REPLY_ID)
.call(&owner)
.unwrap();
}

#[test]
fn data_raw() {
let app = App::default();
let code_id = CodeId::store_code(&app);
let noop_code_id = NoopCodeId::store_code(&app);

let owner = "owner".into_bech32();
let data = Some(to_json_binary(&String::from("some_data")).unwrap());

// Trigger remote instantiation reply
let contract = code_id
.instantiate(noop_code_id.code_id())
.with_label("Contract")
.call(&owner)
.unwrap();

// Should forward `data` if `Some` and return error if `None`
let err = contract
Expand All @@ -274,6 +306,24 @@ fn dispatch_replies() {
.send_message_expecting_data(data.clone(), DATA_RAW_REPLY_ID)
.call(&owner)
.unwrap();
}

#[test]
fn data_opt() {
let app = App::default();
let code_id = CodeId::store_code(&app);
let noop_code_id = NoopCodeId::store_code(&app);

let owner = "owner".into_bech32();
let data = Some(to_json_binary(&String::from("some_data")).unwrap());
let invalid_data = Some(Binary::from("InvalidData".as_bytes()));

// Trigger remote instantiation reply
let contract = code_id
.instantiate(noop_code_id.code_id())
.with_label("Contract")
.call(&owner)
.unwrap();

// Should forward deserialized `data` if `Some` or None and return error if deserialization fails
contract
Expand All @@ -294,6 +344,24 @@ fn dispatch_replies() {
.send_message_expecting_data(data.clone(), DATA_OPT_REPLY_ID)
.call(&owner)
.unwrap();
}

#[test]
fn data() {
let app = App::default();
let code_id = CodeId::store_code(&app);
let noop_code_id = NoopCodeId::store_code(&app);

let owner = "owner".into_bech32();
let data = Some(to_json_binary(&String::from("some_data")).unwrap());
let invalid_data = Some(Binary::from("InvalidData".as_bytes()));

// Trigger remote instantiation reply
let contract = code_id
.instantiate(noop_code_id.code_id())
.with_label("Contract")
.call(&owner)
.unwrap();

// Should forward deserialized `data` if `Some` and return error if `None` or if deserialization fails
let err = contract
Expand Down
69 changes: 51 additions & 18 deletions sylvia/tests/ui/attributes/data/invalid_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,61 @@ use sylvia::contract;
use sylvia::ctx::{InstantiateCtx, ReplyCtx};
use sylvia::cw_std::{Binary, Reply, Response, StdResult};

pub struct Contract;
pub mod invalid_data {
use super::*;

#[contract]
#[sv::features(replies)]
impl Contract {
pub fn new() -> Self {
Self
}
pub struct Contract;

#[contract]
#[sv::features(replies)]
impl Contract {
pub fn new() -> Self {
Self
}

#[sv::msg(instantiate)]
pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response> {
Ok(Response::new())
#[sv::msg(instantiate)]
pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response> {
Ok(Response::new())
}

#[sv::msg(reply, reply_on=success)]
fn reply(
&self,
_ctx: ReplyCtx,
#[sv::data(invalid)] _data: Option<Binary>,
_param: String,
) -> StdResult<Response> {
Ok(Response::new())
}
}
}

pub mod instantiate_and_raw {
use super::*;

pub struct Contract;

#[contract]
#[sv::features(replies)]
impl Contract {
pub fn new() -> Self {
Self
}

#[sv::msg(instantiate)]
pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response> {
Ok(Response::new())
}

#[sv::msg(reply, reply_on=success)]
fn reply(
&self,
_ctx: ReplyCtx,
#[sv::data(invalid)] _data: Option<Binary>,
_param: String,
) -> StdResult<Response> {
Ok(Response::new())
#[sv::msg(reply, reply_on=success)]
fn reply(
&self,
_ctx: ReplyCtx,
#[sv::data(instantiate, raw)] _data: Option<Binary>,
_param: String,
) -> StdResult<Response> {
Ok(Response::new())
}
}
}

Expand Down
15 changes: 12 additions & 3 deletions sylvia/tests/ui/attributes/data/invalid_params.stderr
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
error: Invalid data parameter.

= note: Expected one of [`raw`, `opt`, `instantiate`] comma separated.
--> tests/ui/attributes/data/invalid_params.rs:25:20
--> tests/ui/attributes/data/invalid_params.rs:28:24
|
25 | #[sv::data(invalid)] _data: Option<Binary>,
| ^^^^^^^
28 | #[sv::data(invalid)] _data: Option<Binary>,
| ^^^^^^^

error: The `instantiate` cannot be used in pair with `raw` parameter.

= note: Use any combination of [`raw`, `opt`] or [`instantiate`, `opt`] pairs.

--> tests/ui/attributes/data/invalid_params.rs:57:24
|
57 | #[sv::data(instantiate, raw)] _data: Option<Binary>,
| ^^^^^^^^^^^
Loading