From eac75348832c51a3d167588985e020ea6ffad28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Fri, 30 Aug 2024 18:25:24 +0200 Subject: [PATCH 1/2] feat: Parse additional parameters for sv::msg(reply) (#426) New optional parameters: - handlers - (handler1, handler2, ..) - reply_on - success|failure|always --- sylvia-derive/src/parser/attributes/msg.rs | 70 ++++++++++++++++--- sylvia/tests/reply.rs | 46 ++++++++++++ .../tests/ui/attributes/msg/invalid_params.rs | 25 +++++++ .../ui/attributes/msg/invalid_params.stderr | 5 ++ 4 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 sylvia/tests/reply.rs create mode 100644 sylvia/tests/ui/attributes/msg/invalid_params.rs create mode 100644 sylvia/tests/ui/attributes/msg/invalid_params.stderr diff --git a/sylvia-derive/src/parser/attributes/msg.rs b/sylvia-derive/src/parser/attributes/msg.rs index 25833ecf..bf6f7511 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::{parenthesized, Ident, MetaList, Result, Token}; /// Type of message to be generated #[derive(PartialEq, Eq, Debug, Clone, Copy)] @@ -17,24 +17,46 @@ pub enum MsgType { #[derive(Default)] struct ArgumentParser { pub resp_type: Option, + pub reply_handlers: Vec, + pub reply_on: Option, } impl Parse for ArgumentParser { fn parse(input: ParseStream) -> Result { 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 handlers_content; + parenthesized!(handlers_content in input); + + while !handlers_content.is_empty() { + let handler = handlers_content.parse::()?; + 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 +65,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 { + 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 }, + Query { + resp_type: Option, + }, Instantiate, Migrate, - Reply, + Reply { + _handlers: Vec, + _reply_on: ReplyOn, + }, Sudo, } @@ -85,14 +135,18 @@ impl MsgAttr { impl Parse for MsgAttr { fn parse(input: ParseStream) -> Result { 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(), diff --git a/sylvia/tests/reply.rs b/sylvia/tests/reply.rs new file mode 100644 index 00000000..574c80de --- /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 { + Ok(Response::new()) + } + + #[sv::msg(reply)] + fn clean(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult { + Ok(Response::new()) + } + + #[allow(dead_code)] + #[sv::msg(reply, handlers(handler_one, handler_two))] + fn custom_handlers(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult { + Ok(Response::new()) + } + + #[allow(dead_code)] + #[sv::msg(reply, reply_on = success)] + fn reply_on(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult { + Ok(Response::new()) + } + + #[allow(dead_code)] + #[sv::msg(reply, reply_on = always)] + fn reply_on_always(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult { + 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 { + 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 00000000..74fb6d4a --- /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 { + Ok(Response::new()) + } + + #[sv::msg(reply, unknown_parameter)] + fn reply(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult { + 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 00000000..a7b6c5b7 --- /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)] + | ^^^^^^^^^^^^^^^^^ From 44ab0c43218bac2188a485e5df669ed705cea090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wo=C5=BAniak?= Date: Mon, 16 Sep 2024 15:34:41 +0200 Subject: [PATCH 2/2] chore: Review fix Pass handlers bracketed instead of parenthesized --- sylvia-derive/src/parser/attributes/msg.rs | 5 +++-- sylvia/tests/reply.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sylvia-derive/src/parser/attributes/msg.rs b/sylvia-derive/src/parser/attributes/msg.rs index bf6f7511..c415f53a 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::{parenthesized, Ident, MetaList, Result, Token}; +use syn::{bracketed, Ident, MetaList, Result, Token}; /// Type of message to be generated #[derive(PartialEq, Eq, Debug, Clone, Copy)] @@ -35,8 +35,9 @@ impl Parse for ArgumentParser { result.resp_type = Some(resp_type); } "handlers" => { + let _: Token![=] = input.parse()?; let handlers_content; - parenthesized!(handlers_content in input); + bracketed!(handlers_content in input); while !handlers_content.is_empty() { let handler = handlers_content.parse::()?; diff --git a/sylvia/tests/reply.rs b/sylvia/tests/reply.rs index 574c80de..64593f29 100644 --- a/sylvia/tests/reply.rs +++ b/sylvia/tests/reply.rs @@ -21,7 +21,7 @@ impl Contract { } #[allow(dead_code)] - #[sv::msg(reply, handlers(handler_one, handler_two))] + #[sv::msg(reply, handlers=[handler_one, handler_two])] fn custom_handlers(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult { Ok(Response::new()) } @@ -39,7 +39,7 @@ impl Contract { } #[allow(dead_code)] - #[sv::msg(reply, handlers(handler_one, handler_two), reply_on = failure)] + #[sv::msg(reply, handlers=[handler_one, handler_two], reply_on = failure)] fn both_parameters(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult { Ok(Response::new()) }