relay_event_schema/protocol/
attributes.rs1use 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 #[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#[derive(Debug, Clone, Default, PartialEq, Empty, FromValue, IntoValue)]
156pub struct Attributes(pub Object<Attribute>);
157
158impl Attributes {
159 pub fn new() -> Self {
161 Self(Object::new())
162 }
163
164 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 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 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 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 pub fn insert_raw(&mut self, key: String, attribute: Annotated<Attribute>) {
210 self.0.insert(key, attribute);
211 }
212
213 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 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}