ink_ir/ir/event/
config.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use syn::spanned::Spanned;
16
17use super::SignatureTopic;
18use crate::{
19    ast,
20    utils::{
21        duplicate_config_err,
22        extract_name_override,
23    },
24};
25
26/// The configuration arguments to the `#[ink::event(..)]` attribute macro.
27#[derive(Debug, PartialEq, Eq)]
28pub struct EventConfig {
29    /// If set to `false`, a signature topic is generated and emitted for this event.
30    /// If set to `true`, **no** signature topic is generated or emitted for this event.,
31    /// This is the default value.
32    anonymous: bool,
33    /// Manually specified signature topic hash.
34    signature_topic: Option<SignatureTopic>,
35    /// An optional event name override.
36    ///
37    /// # Note
38    ///
39    /// - Useful for defining overloaded interfaces.
40    /// - If provided, the name must be a valid "identifier-like" string.
41    name: Option<String>,
42}
43
44impl EventConfig {
45    /// Parse a new [`EventConfig`] from a list of attribute meta items.
46    pub fn parse<I>(args: I) -> Result<Self, syn::Error>
47    where
48        I: Iterator<Item = ast::Meta>,
49    {
50        let mut anonymous: Option<syn::Path> = None;
51        let mut signature_topic: Option<syn::LitStr> = None;
52        let mut name: Option<syn::LitStr> = None;
53        for arg in args {
54            if arg.name().is_ident("anonymous") {
55                if let Some(lit_bool) = anonymous {
56                    return Err(duplicate_config_err(lit_bool, arg, "anonymous", "event"));
57                }
58                if let ast::Meta::Path(path) = arg {
59                    anonymous = Some(path)
60                } else {
61                    return Err(format_err_spanned!(
62                        arg,
63                        "encountered an unexpected value for `anonymous` ink! event item configuration argument. \
64                        Did you mean #[ink::event(anonymous)] ?",
65                    ));
66                }
67            } else if arg.name().is_ident("signature_topic") {
68                if anonymous.is_some() {
69                    return Err(format_err_spanned!(
70                        arg,
71                        "cannot specify `signature_topic` with `anonymous` in ink! event item configuration argument",
72                    ));
73                }
74
75                if let Some(lit_str) = signature_topic {
76                    return Err(duplicate_config_err(
77                        lit_str,
78                        arg,
79                        "signature_topic",
80                        "event",
81                    ));
82                }
83                if let Some(lit_str) = arg.value().and_then(ast::MetaValue::as_lit_string)
84                {
85                    signature_topic = Some(lit_str.clone())
86                } else {
87                    return Err(format_err_spanned!(
88                        arg,
89                        "expected a string literal value for `signature_topic` ink! event item configuration argument",
90                    ));
91                }
92            } else if arg.name().is_ident("name") {
93                if let Some(lit_str) = name {
94                    return Err(duplicate_config_err(lit_str, arg, "name", "event"));
95                }
96
97                if let Some(value) = arg.value() {
98                    name = Some(extract_name_override(value, arg.span())?);
99                } else {
100                    return Err(format_err_spanned!(
101                        arg,
102                        "expected a string literal value for `name` attribute argument"
103                    ));
104                }
105            } else {
106                return Err(format_err_spanned!(
107                    arg,
108                    "encountered unknown or unsupported ink! event item configuration argument",
109                ));
110            }
111        }
112
113        Ok(EventConfig::new(
114            anonymous.is_some(),
115            signature_topic
116                .as_ref()
117                .map(SignatureTopic::try_from)
118                .transpose()?,
119            name.map(|lit_str| lit_str.value()),
120        ))
121    }
122}
123
124impl TryFrom<ast::AttributeArgs> for EventConfig {
125    type Error = syn::Error;
126
127    fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> {
128        Self::parse(args.into_iter())
129    }
130}
131
132impl TryFrom<&[syn::Attribute]> for EventConfig {
133    type Error = syn::Error;
134
135    fn try_from(attrs: &[syn::Attribute]) -> Result<Self, Self::Error> {
136        let mut ink_attrs = Vec::new();
137        for attr in attrs {
138            if !attr.path().is_ident("ink") {
139                continue;
140            }
141            let nested = attr.parse_args_with(
142                syn::punctuated::Punctuated::<ast::Meta, syn::Token![,]>::parse_separated_nonempty,
143            )?;
144            ink_attrs.extend(nested);
145        }
146        Self::parse(ink_attrs.into_iter())
147    }
148}
149
150impl EventConfig {
151    /// Construct a new [`EventConfig`].
152    pub fn new(
153        anonymous: bool,
154        signature_topic: Option<SignatureTopic>,
155        name: Option<String>,
156    ) -> Self {
157        Self {
158            anonymous,
159            signature_topic,
160            name,
161        }
162    }
163
164    /// Returns the anonymous configuration argument.
165    pub fn anonymous(&self) -> bool {
166        self.anonymous
167    }
168
169    /// Returns the manually specified signature topic.
170    pub fn signature_topic(&self) -> Option<SignatureTopic> {
171        self.signature_topic
172    }
173
174    /// Returns the event name override (if any).
175    pub fn name(&self) -> Option<&str> {
176        self.name.as_deref()
177    }
178}