sylvia_derive/parser/attributes/
msg.rs

1use proc_macro_error::emit_error;
2use syn::parse::{Error, Parse, ParseStream, Parser};
3use syn::{bracketed, Ident, MetaList, Result, Token};
4
5/// Supported message types.
6/// Representation of the first parameter in `#[sv::msg(..)] attribute.
7#[derive(PartialEq, Eq, Debug, Clone, Copy)]
8pub enum MsgType {
9    Exec,
10    Query,
11    Instantiate,
12    Migrate,
13    Reply,
14    Sudo,
15}
16
17/// ArgumentParser holds `resp` parameter parsed from `sv::msg` attribute.
18#[derive(Default)]
19struct ArgumentParser {
20    pub query_resp_type: Option<Ident>,
21    pub reply_handlers: Vec<Ident>,
22    pub reply_on: Option<ReplyOn>,
23}
24
25impl Parse for ArgumentParser {
26    fn parse(input: ParseStream) -> Result<Self> {
27        let mut result = Self::default();
28
29        while input.peek2(Ident) {
30            let _: Token![,] = input.parse()?;
31            let arg_type: Ident = input.parse()?;
32            match arg_type.to_string().as_str() {
33                "resp" => {
34                    let _: Token![=] = input.parse()?;
35                    let resp_type: Ident = input.parse()?;
36                    result.query_resp_type = Some(resp_type);
37                }
38                "handlers" => {
39                    let _: Token![=] = input.parse()?;
40                    let handlers_content;
41                    bracketed!(handlers_content in input);
42
43                    while !handlers_content.is_empty() {
44                        let handler = handlers_content.parse::<Ident>()?;
45                        result.reply_handlers.push(handler);
46                        if !handlers_content.peek(Token![,]) {
47                            break;
48                        }
49                        let _: Token![,] = handlers_content.parse()?;
50                    }
51                }
52                "reply_on" => {
53                    let _: Token![=] = input.parse()?;
54                    let reply_on: Ident = input.parse()?;
55                    let reply_on = ReplyOn::new(reply_on)?;
56                    result.reply_on = Some(reply_on);
57                }
58                _ => {
59                    return Err(Error::new(
60                        arg_type.span(),
61                        "Invalid argument type, expected `resp`, `handlers`, `reply_on` or no argument.",
62                    ))
63                }
64            }
65        }
66        Ok(result)
67    }
68}
69
70/// Representation of `reply_on` parameter in `#[sv::msg(reply(...))]` attribute.
71#[derive(Copy, Debug, Default, Clone, PartialEq)]
72pub enum ReplyOn {
73    Success,
74    Error,
75    #[default]
76    Always,
77}
78
79impl std::fmt::Display for ReplyOn {
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81        match self {
82            ReplyOn::Success => f.write_str("success"),
83            ReplyOn::Error => f.write_str("error"),
84            ReplyOn::Always => f.write_str("always"),
85        }
86    }
87}
88
89impl ReplyOn {
90    pub fn new(reply_on: Ident) -> Result<Self> {
91        match reply_on.to_string().as_str() {
92            "success" => Ok(Self::Success),
93            "error" => Ok(Self::Error),
94            "always" => Ok(Self::Always),
95            _ => Err(Error::new(
96                reply_on.span(),
97                "Invalid argument type, expected one of `success`, `error` or `always`.",
98            )),
99        }
100    }
101
102    /// Checks if two [ReplyOn] values are unique and if [ReplyOn::Always] is not one of them as it is exclusive
103    /// and cannot be paired with any other value.
104    pub fn excludes(&self, other: &Self) -> bool {
105        let are_equal = self == other;
106        let is_any_always = self == &ReplyOn::Always || other == &ReplyOn::Always;
107
108        are_equal || is_any_always
109    }
110}
111
112/// Parsed representation of `#[sv::msg(...)]` attribute.
113#[derive(Debug, Clone)]
114pub struct MsgAttr {
115    msg_type: MsgType,
116    query_resp_type: Option<Ident>,
117    reply_handlers: Vec<Ident>,
118    reply_on: ReplyOn,
119}
120
121impl MsgAttr {
122    pub fn new(attr: &MetaList) -> Result<Self> {
123        MsgAttr::parse.parse2(attr.tokens.clone()).map_err(|err| {
124            emit_error!(err.span(), err);
125            err
126        })
127    }
128
129    pub fn msg_type(&self) -> MsgType {
130        self.msg_type
131    }
132
133    pub fn resp_type(&self) -> &Option<Ident> {
134        &self.query_resp_type
135    }
136
137    pub fn handlers(&self) -> &[Ident] {
138        &self.reply_handlers
139    }
140
141    pub fn reply_on(&self) -> ReplyOn {
142        self.reply_on
143    }
144}
145
146impl PartialEq<MsgType> for MsgAttr {
147    fn eq(&self, other: &MsgType) -> bool {
148        self.msg_type() == *other
149    }
150}
151
152impl Parse for MsgAttr {
153    fn parse(input: ParseStream) -> Result<Self> {
154        let msg_type: Ident = input.parse()?;
155        let msg_type = MsgType::new(&msg_type)?;
156        let ArgumentParser {
157            query_resp_type,
158            reply_handlers,
159            reply_on,
160        } = ArgumentParser::parse(input)?;
161
162        Ok(Self {
163            msg_type,
164            query_resp_type,
165            reply_handlers,
166            reply_on: reply_on.unwrap_or_default(),
167        })
168    }
169}