Repository: CosmWasm/sylvia
Pull request #433 (2024-08-30 16:31:19)

feat: Parse additional parameters for sv::msg(reply) (#426)
New optional parameters:
    - handlers - (handler1, handler2, ..)
    - reply_on - success|failure|always

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/CosmWasm/sylvia/pull/433?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) Report
Attention: Patch coverage is `51.21951%` with `20 lines` in your changes missing coverage. Please review.
> Project coverage is 69.83%. Comparing base [(`10e8eb9`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/10e8eb9ce722c4e2f2bed6bce950acbcefd2b937?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) to head [(`eac7534`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/eac75348832c51a3d167588985e020ea6ffad28c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).

| [Files with missing lines](https://app.codecov.io/gh/CosmWasm/sylvia/pull/433?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) | Patch % | Lines |
|---|---|---|
| [sylvia/tests/reply.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/433?src=pr&el=tree&filepath=sylvia%2Ftests%2Freply.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhL3Rlc3RzL3JlcGx5LnJz) | 0.00% | [14 Missing :warning: ](https://app.codecov.io/gh/CosmWasm/sylvia/pull/433?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) |
| [sylvia-derive/src/parser/attributes/msg.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/433?src=pr&el=tree&filepath=sylvia-derive%2Fsrc%2Fparser%2Fattributes%2Fmsg.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhLWRlcml2ZS9zcmMvcGFyc2VyL2F0dHJpYnV0ZXMvbXNnLnJz) | 77.77% | [6 Missing :warning: ](https://app.codecov.io/gh/CosmWasm/sylvia/pull/433?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) |

<details><summary>Additional details and impacted files</summary>


```diff
@@               Coverage Diff                @@
##           feat/replies     #433      +/-   ##
================================================
- Coverage         70.08%   69.83%   -0.25%     
================================================
  Files                53       54       +1     
  Lines              3085     3120      +35     
================================================
+ Hits               2162     2179      +17     
- Misses              923      941      +18     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/CosmWasm/sylvia/pull/433?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).

jawoznia: closes #426 

Patch:
diff --git a/sylvia-derive/src/parser/attributes/msg.rs b/sylvia-derive/src/parser/attributes/msg.rs
index 25833ec..c415f53 100644
--- a/sylvia-derive/src/parser/attributes/msg.rs
+++ b/sylvia-derive/src/parser/attributes/msg.rs
@@ -1,6 +1,6 @@
 use proc_macro_error::emit_error;
 use syn::parse::{Error, Parse, ParseStream, Parser};
-use syn::{Ident, MetaList, Result, Token};
+use syn::{bracketed, Ident, MetaList, Result, Token};
 
 /// Type of message to be generated
 #[derive(PartialEq, Eq, Debug, Clone, Copy)]
@@ -17,24 +17,47 @@ pub enum MsgType {
 #[derive(Default)]
 struct ArgumentParser {
     pub resp_type: Option<Ident>,
+    pub reply_handlers: Vec<Ident>,
+    pub reply_on: Option<ReplyOn>,
 }
 
 impl Parse for ArgumentParser {
     fn parse(input: ParseStream) -> Result<Self> {
         let mut result = Self::default();
+
         while input.peek2(Ident) {
             let _: Token![,] = input.parse()?;
             let arg_type: Ident = input.parse()?;
-            let _: Token![=] = input.parse()?;
             match arg_type.to_string().as_str() {
                 "resp" => {
+                    let _: Token![=] = input.parse()?;
                     let resp_type: Ident = input.parse()?;
                     result.resp_type = Some(resp_type);
                 }
+                "handlers" => {
+                    let _: Token![=] = input.parse()?;
+                    let handlers_content;
+                    bracketed!(handlers_content in input);
+
+                    while !handlers_content.is_empty() {
+                        let handler = handlers_content.parse::<Ident>()?;
+                        result.reply_handlers.push(handler);
+                        if !handlers_content.peek(Token![,]) {
+                            break;
+                        }
+                        let _: Token![,] = handlers_content.parse()?;
+                    }
+                }
+                "reply_on" => {
+                    let _: Token![=] = input.parse()?;
+                    let reply_on: Ident = input.parse()?;
+                    let reply_on = ReplyOn::new(reply_on)?;
+                    result.reply_on = Some(reply_on);
+                }
                 _ => {
                     return Err(Error::new(
-                        input.span(),
-                        "Invalid argument type, expected `resp` or no argument.",
+                        arg_type.span(),
+                        "Invalid argument type, expected `resp`, `handlers`, `reply_on` or no argument.",
                     ))
                 }
             }
@@ -43,14 +66,42 @@ impl Parse for ArgumentParser {
     }
 }
 
+/// Representation of `reply_on` parameter in `#[sv::msg(reply(...))]` attribute.
+#[derive(Default, Clone)]
+pub enum ReplyOn {
+    Success,
+    Failure,
+    #[default]
+    Always,
+}
+
+impl ReplyOn {
+    pub fn new(reply_on: Ident) -> Result<Self> {
+        match reply_on.to_string().as_str() {
+            "success" => Ok(Self::Success),
+            "failure" => Ok(Self::Failure),
+            "always" => Ok(Self::Always),
+            _ => Err(Error::new(
+                reply_on.span(),
+                "Invalid argument type, expected one of `success`, `failure` or `always`.",
+            )),
+        }
+    }
+}
+
 /// Parsed representation of `#[sv::msg(...)]` attribute.
 #[derive(Clone)]
 pub enum MsgAttr {
     Exec,
-    Query { resp_type: Option<Ident> },
+    Query {
+        resp_type: Option<Ident>,
+    },
     Instantiate,
     Migrate,
-    Reply,
+    Reply {
+        _handlers: Vec<Ident>,
+        _reply_on: ReplyOn,
+    },
     Sudo,
 }
 
@@ -85,14 +136,18 @@ impl MsgAttr {
 impl Parse for MsgAttr {
     fn parse(input: ParseStream) -> Result<Self> {
         let ty: Ident = input.parse()?;
-        let ArgumentParser { resp_type } = ArgumentParser::parse(input)?;
+        let ArgumentParser {
+            resp_type,
+            reply_handlers,
+            reply_on,
+        } = ArgumentParser::parse(input)?;
 
         let result = match ty.to_string().as_str() {
             "exec" => Self::Exec,
             "query" => Self::Query { resp_type },
             "instantiate" => Self::Instantiate,
             "migrate" => Self::Migrate,
-            "reply" => Self::Reply,
+            "reply" => Self::Reply {_handlers: reply_handlers, _reply_on: reply_on.unwrap_or_default()},
             "sudo" => Self::Sudo,
             _ => return Err(Error::new(
                 input.span(),


Test patch:
diff --git a/sylvia/tests/reply.rs b/sylvia/tests/reply.rs
new file mode 100644
index 0000000..64593f2
--- /dev/null
+++ b/sylvia/tests/reply.rs
@@ -0,0 +1,46 @@
+use sylvia::contract;
+use sylvia::cw_std::{Reply, Response, StdResult};
+use sylvia::types::{InstantiateCtx, ReplyCtx};
+
+pub struct Contract;
+
+#[contract]
+impl Contract {
+    pub fn new() -> Self {
+        Self
+    }
+
+    #[sv::msg(instantiate)]
+    pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[sv::msg(reply)]
+    fn clean(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[allow(dead_code)]
+    #[sv::msg(reply, handlers=[handler_one, handler_two])]
+    fn custom_handlers(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[allow(dead_code)]
+    #[sv::msg(reply, reply_on = success)]
+    fn reply_on(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[allow(dead_code)]
+    #[sv::msg(reply, reply_on = always)]
+    fn reply_on_always(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[allow(dead_code)]
+    #[sv::msg(reply, handlers=[handler_one, handler_two], reply_on = failure)]
+    fn both_parameters(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+}
diff --git a/sylvia/tests/ui/attributes/msg/invalid_params.rs b/sylvia/tests/ui/attributes/msg/invalid_params.rs
new file mode 100644
index 0000000..74fb6d4
--- /dev/null
+++ b/sylvia/tests/ui/attributes/msg/invalid_params.rs
@@ -0,0 +1,25 @@
+#![allow(unused_imports)]
+use sylvia::contract;
+use sylvia::cw_std::{Reply, Response, StdResult};
+use sylvia::types::{InstantiateCtx, ReplyCtx};
+
+pub struct Contract;
+
+#[contract]
+impl Contract {
+    pub fn new() -> Self {
+        Self
+    }
+
+    #[sv::msg(instantiate)]
+    pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+
+    #[sv::msg(reply, unknown_parameter)]
+    fn reply(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
+        Ok(Response::new())
+    }
+}
+
+fn main() {}
diff --git a/sylvia/tests/ui/attributes/msg/invalid_params.stderr b/sylvia/tests/ui/attributes/msg/invalid_params.stderr
new file mode 100644
index 0000000..a7b6c5b
--- /dev/null
+++ b/sylvia/tests/ui/attributes/msg/invalid_params.stderr
@@ -0,0 +1,5 @@
+error: Invalid argument type, expected `resp`, `handlers`, `reply_on` or no argument.
+  --> tests/ui/attributes/msg/invalid_params.rs:19:22
+   |
+19 |     #[sv::msg(reply, unknown_parameter)]
+   |                      ^^^^^^^^^^^^^^^^^