relay_event_schema/protocol/
attributes.rs

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