Skip to content

Commit

Permalink
feat: Parse additional parameters for sv::msg(reply) (#426)
Browse files Browse the repository at this point in the history
New optional parameters:
    - handlers - (handler1, handler2, ..)
    - reply_on - success|failure|always
  • Loading branch information
jawoznia committed Aug 30, 2024
1 parent 10e8eb9 commit 37573f1
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 7 deletions.
68 changes: 61 additions & 7 deletions sylvia-derive/src/parser/attributes/msg.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -17,24 +17,46 @@ 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 handlers_content;
parenthesized!(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.",
"Invalid argument type, expected `resp`, `handlers`, `reply_on` or no argument.",
))
}
}
Expand All @@ -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<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,
}

Expand Down Expand Up @@ -85,14 +135,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(),
Expand Down
46 changes: 46 additions & 0 deletions sylvia/tests/reply.rs
Original file line number Diff line number Diff line change
@@ -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())
}
}
25 changes: 25 additions & 0 deletions sylvia/tests/ui/attributes/msg/invalid_params.rs
Original file line number Diff line number Diff line change
@@ -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() {}

0 comments on commit 37573f1

Please sign in to comment.