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 eac7534
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 8 deletions.
70 changes: 62 additions & 8 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.",
arg_type.span(),
"Invalid argument type, expected `resp`, `handlers`, `reply_on` or no argument.",

Check warning on line 59 in sylvia-derive/src/parser/attributes/msg.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/parser/attributes/msg.rs#L58-L59

Added lines #L58 - L59 were not covered by tests
))
}
}
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`.",

Check warning on line 85 in sylvia-derive/src/parser/attributes/msg.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/parser/attributes/msg.rs#L83-L85

Added lines #L83 - L85 were not covered by tests
)),
}
}
}

/// 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)?;

Check warning on line 142 in sylvia-derive/src/parser/attributes/msg.rs

View check run for this annotation

Codecov / codecov/patch

sylvia-derive/src/parser/attributes/msg.rs#L142

Added line #L142 was not covered by tests

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

Check warning on line 10 in sylvia/tests/reply.rs

View check run for this annotation

Codecov / codecov/patch

sylvia/tests/reply.rs#L9-L10

Added lines #L9 - L10 were not covered by tests
}

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

Check warning on line 15 in sylvia/tests/reply.rs

View check run for this annotation

Codecov / codecov/patch

sylvia/tests/reply.rs#L14-L15

Added lines #L14 - L15 were not covered by tests
}

#[sv::msg(reply)]
fn clean(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
Ok(Response::new())

Check warning on line 20 in sylvia/tests/reply.rs

View check run for this annotation

Codecov / codecov/patch

sylvia/tests/reply.rs#L19-L20

Added lines #L19 - L20 were not covered by tests
}

#[allow(dead_code)]
#[sv::msg(reply, handlers(handler_one, handler_two))]
fn custom_handlers(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
Ok(Response::new())

Check warning on line 26 in sylvia/tests/reply.rs

View check run for this annotation

Codecov / codecov/patch

sylvia/tests/reply.rs#L25-L26

Added lines #L25 - L26 were not covered by tests
}

#[allow(dead_code)]
#[sv::msg(reply, reply_on = success)]
fn reply_on(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
Ok(Response::new())

Check warning on line 32 in sylvia/tests/reply.rs

View check run for this annotation

Codecov / codecov/patch

sylvia/tests/reply.rs#L31-L32

Added lines #L31 - L32 were not covered by tests
}

#[allow(dead_code)]
#[sv::msg(reply, reply_on = always)]
fn reply_on_always(&self, _ctx: ReplyCtx, _reply: Reply) -> StdResult<Response> {
Ok(Response::new())

Check warning on line 38 in sylvia/tests/reply.rs

View check run for this annotation

Codecov / codecov/patch

sylvia/tests/reply.rs#L37-L38

Added lines #L37 - L38 were not covered by tests
}

#[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())

Check warning on line 44 in sylvia/tests/reply.rs

View check run for this annotation

Codecov / codecov/patch

sylvia/tests/reply.rs#L43-L44

Added lines #L43 - L44 were not covered by tests
}
}
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() {}
5 changes: 5 additions & 0 deletions sylvia/tests/ui/attributes/msg/invalid_params.stderr
Original file line number Diff line number Diff line change
@@ -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)]
| ^^^^^^^^^^^^^^^^^

0 comments on commit eac7534

Please sign in to comment.