relay_event_schema/protocol/
attributes.rs

1use relay_protocol::{Annotated, Empty, FromValue, IntoValue, Object, SkipSerialization, Value};
2use std::{borrow::Borrow, fmt};
3
4use crate::processor::ProcessValue;
5
6#[derive(Clone, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
7pub struct Attribute {
8    #[metastructure(flatten)]
9    pub value: AttributeValue,
10
11    /// Additional arbitrary fields for forwards compatibility.
12    #[metastructure(additional_properties)]
13    pub other: Object<Value>,
14}
15
16impl Attribute {
17    pub fn new(attribute_type: AttributeType, value: Value) -> Self {
18        Self {
19            value: AttributeValue {
20                ty: Annotated::new(attribute_type),
21                value: Annotated::new(value),
22            },
23            other: Object::new(),
24        }
25    }
26}
27
28impl fmt::Debug for Attribute {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.debug_struct("Attribute")
31            .field("value", &self.value.value)
32            .field("type", &self.value.ty)
33            .field("other", &self.other)
34            .finish()
35    }
36}
37
38#[derive(Debug, Clone, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
39pub struct AttributeValue {
40    #[metastructure(field = "type", required = true, trim = false)]
41    pub ty: Annotated<AttributeType>,
42    #[metastructure(required = true, pii = "true")]
43    pub value: Annotated<Value>,
44}
45
46impl From<String> for AttributeValue {
47    fn from(value: String) -> Self {
48        AttributeValue {
49            ty: Annotated::new(AttributeType::String),
50            value: Annotated::new(value.into()),
51        }
52    }
53}
54
55impl From<i64> for AttributeValue {
56    fn from(value: i64) -> Self {
57        AttributeValue {
58            ty: Annotated::new(AttributeType::Integer),
59            value: Annotated::new(value.into()),
60        }
61    }
62}
63
64impl From<f64> for AttributeValue {
65    fn from(value: f64) -> Self {
66        AttributeValue {
67            ty: Annotated::new(AttributeType::Double),
68            value: Annotated::new(value.into()),
69        }
70    }
71}
72
73impl From<bool> for AttributeValue {
74    fn from(value: bool) -> Self {
75        AttributeValue {
76            ty: Annotated::new(AttributeType::Boolean),
77            value: Annotated::new(value.into()),
78        }
79    }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub enum AttributeType {
84    Boolean,
85    Integer,
86    Double,
87    String,
88    Unknown(String),
89}
90
91impl ProcessValue for AttributeType {}
92
93impl AttributeType {
94    pub fn as_str(&self) -> &str {
95        match self {
96            Self::Boolean => "boolean",
97            Self::Integer => "integer",
98            Self::Double => "double",
99            Self::String => "string",
100            Self::Unknown(value) => value,
101        }
102    }
103
104    pub fn unknown_string() -> String {
105        "unknown".to_owned()
106    }
107}
108
109impl fmt::Display for AttributeType {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        write!(f, "{}", self.as_str())
112    }
113}
114
115impl From<String> for AttributeType {
116    fn from(value: String) -> Self {
117        match value.as_str() {
118            "boolean" => Self::Boolean,
119            "integer" => Self::Integer,
120            "double" => Self::Double,
121            "string" => Self::String,
122            _ => Self::Unknown(value),
123        }
124    }
125}
126
127impl Empty for AttributeType {
128    #[inline]
129    fn is_empty(&self) -> bool {
130        false
131    }
132}
133
134impl FromValue for AttributeType {
135    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
136        match String::from_value(value) {
137            Annotated(Some(value), meta) => Annotated(Some(value.into()), meta),
138            Annotated(None, meta) => Annotated(None, meta),
139        }
140    }
141}
142
143impl IntoValue for AttributeType {
144    fn into_value(self) -> Value
145    where
146        Self: Sized,
147    {
148        Value::String(match self {
149            Self::Unknown(s) => s,
150            s => s.to_string(),
151        })
152    }
153
154    fn serialize_payload<S>(&self, s: S, _behavior: SkipSerialization) -> Result<S::Ok, S::Error>
155    where
156        Self: Sized,
157        S: serde::Serializer,
158    {
159        serde::ser::Serialize::serialize(self.as_str(), s)
160    }
161}
162
163/// Wrapper struct around a collection of attributes with some convenience methods.
164#[derive(Debug, Clone, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
165pub struct Attributes(pub Object<Attribute>);
166
167impl Attributes {
168    /// Creates an empty collection of attributes.
169    pub fn new() -> Self {
170        Self(Object::new())
171    }
172
173    /// Returns the value of the attribute with the given key.
174    pub fn get_value<Q>(&self, key: &Q) -> Option<&Value>
175    where
176        String: Borrow<Q>,
177        Q: Ord + ?Sized,
178    {
179        self.get_attribute(key)?.value.value.value()
180    }
181
182    /// Returns the attribute with the given key.
183    pub fn get_attribute<Q>(&self, key: &Q) -> Option<&Attribute>
184    where
185        String: Borrow<Q>,
186        Q: Ord + ?Sized,
187    {
188        self.0.get(key)?.value()
189    }
190
191    /// Inserts an attribute with the given value into this collection.
192    pub fn insert<V: Into<AttributeValue>>(&mut self, key: String, value: V) {
193        fn inner(slf: &mut Attributes, key: String, value: AttributeValue) {
194            let attribute = Annotated::new(Attribute {
195                value,
196                other: Default::default(),
197            });
198            slf.insert_raw(key, attribute);
199        }
200        let value = value.into();
201        inner(self, key, value);
202    }
203
204    /// Inserts an annotated attribute into this collection.
205    pub fn insert_raw(&mut self, key: String, attribute: Annotated<Attribute>) {
206        self.0.insert(key, attribute);
207    }
208
209    /// Checks whether this collection contains an attribute with the given key.
210    pub fn contains_key<Q>(&self, key: &Q) -> bool
211    where
212        String: Borrow<Q>,
213        Q: Ord + ?Sized,
214    {
215        self.0.contains_key(key)
216    }
217
218    /// Iterates mutably over this collection's attribute keys and values.
219    pub fn iter_mut(
220        &mut self,
221    ) -> std::collections::btree_map::IterMut<String, Annotated<Attribute>> {
222        self.0.iter_mut()
223    }
224}
225
226impl IntoIterator for Attributes {
227    type Item = (String, Annotated<Attribute>);
228
229    type IntoIter = std::collections::btree_map::IntoIter<String, Annotated<Attribute>>;
230
231    fn into_iter(self) -> Self::IntoIter {
232        self.0.into_iter()
233    }
234}
235
236impl FromIterator<(String, Annotated<Attribute>)> for Attributes {
237    fn from_iter<T: IntoIterator<Item = (String, Annotated<Attribute>)>>(iter: T) -> Self {
238        Self(Object::from_iter(iter))
239    }
240}