cosmwasm_std/results/
events.rs1use crate::__internal::forward_ref_partial_eq;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use crate::prelude::*;
6
7#[derive(
16    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
17)]
18#[non_exhaustive]
19pub struct Event {
20    #[serde(rename = "type")]
22    pub ty: String,
23    pub attributes: Vec<Attribute>,
29}
30
31forward_ref_partial_eq!(Event, Event);
32
33impl Event {
34    pub fn new(ty: impl Into<String>) -> Self {
36        Event {
37            ty: ty.into(),
38            attributes: Vec::with_capacity(10),
39        }
40    }
41
42    pub fn add_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
44        self.attributes.push(Attribute {
45            key: key.into(),
46            value: value.into(),
47        });
48        self
49    }
50
51    pub fn add_attributes<A: Into<Attribute>>(
56        mut self,
57        attrs: impl IntoIterator<Item = A>,
58    ) -> Self {
59        self.attributes.extend(attrs.into_iter().map(A::into));
60        self
61    }
62}
63
64#[derive(
66    Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
67)]
68pub struct Attribute {
69    pub key: String,
70    pub value: String,
71}
72
73forward_ref_partial_eq!(Attribute, Attribute);
74
75impl Attribute {
76    pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
78        let key = key.into();
79
80        #[cfg(debug_assertions)]
81        if key.starts_with('_') {
82            panic!(
83                "attribute key `{key}` is invalid - keys starting with an underscore are reserved"
84            );
85        }
86
87        Self {
88            key,
89            value: value.into(),
90        }
91    }
92}
93
94impl<K: Into<String>, V: Into<String>> From<(K, V)> for Attribute {
95    fn from((k, v): (K, V)) -> Self {
96        Attribute::new(k, v)
97    }
98}
99
100impl<K: AsRef<str>, V: AsRef<str>> PartialEq<(K, V)> for Attribute {
101    fn eq(&self, (k, v): &(K, V)) -> bool {
102        (self.key.as_str(), self.value.as_str()) == (k.as_ref(), v.as_ref())
103    }
104}
105
106impl<K: AsRef<str>, V: AsRef<str>> PartialEq<Attribute> for (K, V) {
107    fn eq(&self, attr: &Attribute) -> bool {
108        attr == self
109    }
110}
111
112impl<K: AsRef<str>, V: AsRef<str>> PartialEq<(K, V)> for &Attribute {
113    fn eq(&self, (k, v): &(K, V)) -> bool {
114        (self.key.as_str(), self.value.as_str()) == (k.as_ref(), v.as_ref())
115    }
116}
117
118impl<K: AsRef<str>, V: AsRef<str>> PartialEq<&Attribute> for (K, V) {
119    fn eq(&self, attr: &&Attribute) -> bool {
120        attr == self
121    }
122}
123
124#[inline]
126pub fn attr(key: impl Into<String>, value: impl Into<String>) -> Attribute {
127    Attribute::new(key, value)
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use crate::Uint128;
134
135    #[test]
136    fn event_construction() {
137        let event_direct = Event {
138            ty: "test".to_string(),
139            attributes: vec![attr("foo", "bar"), attr("bar", "baz")],
140        };
141        let event_builder = Event::new("test").add_attributes(vec![("foo", "bar"), ("bar", "baz")]);
142
143        assert_eq!(event_direct, event_builder);
144    }
145
146    #[test]
147    #[should_panic]
148    fn attribute_new_reserved_key_panicks() {
149        Attribute::new("_invalid", "value");
150    }
151
152    #[test]
153    #[should_panic]
154    fn attribute_new_reserved_key_panicks2() {
155        Attribute::new("_", "value");
156    }
157
158    #[test]
159    fn attr_works_for_different_types() {
160        let expected = ("foo", "42");
161
162        assert_eq!(attr("foo", "42"), expected);
163        assert_eq!(attr("foo", "42"), expected);
164        assert_eq!(attr("foo", "42"), expected);
165        assert_eq!(attr("foo", Uint128::new(42)), expected);
166    }
167}