ink_ir/ir/event/
signature_topic.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 core::fmt::{
16    Display,
17    Formatter,
18};
19
20use impl_serde::serialize as serde_hex;
21use syn::spanned::Spanned;
22
23use crate::ast;
24
25/// The signature topic argument of an event variant.
26///
27/// Used as part of `ink::event` macro.
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct SignatureTopic {
30    topic: [u8; 32],
31}
32
33impl SignatureTopic {
34    /// Returns a 32-byte array representation of the signature topic.
35    pub fn to_bytes(&self) -> [u8; 32] {
36        self.topic
37    }
38
39    /// Returns a 32 byte hex-string representation of the signature topic.
40    pub fn to_hex(&self) -> String {
41        serde_hex::to_hex(self.topic.as_slice(), false)
42    }
43}
44
45impl Display for SignatureTopic {
46    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
47        write!(f, "{}", self.to_hex())
48    }
49}
50
51impl From<[u8; 32]> for SignatureTopic {
52    fn from(value: [u8; 32]) -> Self {
53        Self { topic: value }
54    }
55}
56
57impl TryFrom<&str> for SignatureTopic {
58    type Error = String;
59
60    fn try_from(value: &str) -> Result<Self, String> {
61        let bytes: [u8; 32] = serde_hex::from_hex(value)
62            .map_err(|_| "`signature_topic` has invalid hex string".to_string())?
63            .try_into()
64            .map_err(|e: Vec<u8>| {
65                format!(
66                    "`signature_topic` is expected to be 32-byte hex string. \
67                    Found {} bytes",
68                    e.len()
69                )
70            })?;
71
72        Ok(Self { topic: bytes })
73    }
74}
75
76impl TryFrom<&syn::LitStr> for SignatureTopic {
77    type Error = syn::Error;
78
79    fn try_from(lit: &syn::LitStr) -> Result<Self, Self::Error> {
80        Self::try_from(lit.value().as_str())
81            .map_err(|err| syn::Error::new_spanned(lit, err))
82    }
83}
84
85impl TryFrom<&syn::Lit> for SignatureTopic {
86    type Error = syn::Error;
87
88    fn try_from(lit: &syn::Lit) -> Result<Self, Self::Error> {
89        if let syn::Lit::Str(s) = lit {
90            Self::try_from(s)
91        } else {
92            Err(format_err_spanned!(
93                lit,
94                "Expected string literal argument for the `signature_topic`"
95            ))
96        }
97    }
98}
99
100impl TryFrom<&ast::MetaValue> for SignatureTopic {
101    type Error = syn::Error;
102
103    fn try_from(value: &ast::MetaValue) -> Result<Self, Self::Error> {
104        if let ast::MetaValue::Lit(lit) = value {
105            Self::try_from(lit)
106        } else {
107            Err(format_err_spanned!(
108                value,
109                "Expected string argument for the `signature_topic`"
110            ))
111        }
112    }
113}
114
115impl TryFrom<ast::AttributeArgs> for Option<SignatureTopic> {
116    type Error = syn::Error;
117
118    fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> {
119        let mut signature_topic: Option<SignatureTopic> = None;
120        for arg in args.into_iter() {
121            if arg.name().is_ident("signature_topic") {
122                if signature_topic.is_some() {
123                    return Err(format_err!(
124                        arg.span(),
125                        "encountered duplicate ink! event configuration argument"
126                    ));
127                }
128                signature_topic =
129                    arg.value().map(SignatureTopic::try_from).transpose()?;
130            } else {
131                return Err(format_err_spanned!(
132                    arg,
133                    "encountered unknown or unsupported ink! event item configuration argument",
134                ));
135            }
136        }
137        Ok(signature_topic)
138    }
139}
140
141impl TryFrom<&syn::MetaNameValue> for SignatureTopic {
142    type Error = syn::Error;
143
144    fn try_from(nv: &syn::MetaNameValue) -> Result<Self, Self::Error> {
145        if nv.path.is_ident("signature_topic") {
146            if let syn::Expr::Lit(lit_expr) = &nv.value {
147                Self::try_from(&lit_expr.lit)
148            } else {
149                Err(format_err_spanned!(&nv.value, "Expected literal argument"))
150            }
151        } else {
152            Err(format_err_spanned!(nv, "Expected `signature_topic` ident"))
153        }
154    }
155}