relay_event_schema/protocol/
attributes.rs1use std::borrow::{Borrow, Cow};
2use std::fmt;
3
4use enumset::EnumSet;
5use relay_protocol::{
6 Annotated, Empty, FromValue, IntoValue, Meta, Object, SkipSerialization, Value,
7};
8
9use crate::processor::{
10 Pii, ProcessValue, ProcessingResult, ProcessingState, Processor, ValueType,
11};
12
13#[derive(Clone, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
14pub struct Attribute {
15 #[metastructure(flatten)]
16 pub value: AttributeValue,
17
18 #[metastructure(additional_properties)]
20 pub other: Object<Value>,
21}
22
23impl Attribute {
24 pub fn new(attribute_type: AttributeType, value: Value) -> Self {
25 Self {
26 value: AttributeValue {
27 ty: Annotated::new(attribute_type),
28 value: Annotated::new(value),
29 },
30 other: Object::new(),
31 }
32 }
33
34 pub fn into_string(self) -> Option<String> {
36 self.value.value.into_value()?.into_string()
37 }
38}
39
40impl fmt::Debug for Attribute {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 f.debug_struct("Attribute")
43 .field("value", &self.value.value)
44 .field("type", &self.value.ty)
45 .field("other", &self.other)
46 .finish()
47 }
48}
49
50impl<T> From<T> for Attribute
51where
52 AttributeValue: From<T>,
53{
54 fn from(value: T) -> Self {
55 let value = value.into();
56 Self {
57 value,
58 other: Default::default(),
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
64pub struct AttributeValue {
65 #[metastructure(field = "type", required = true, trim = false, pii = "false")]
66 pub ty: Annotated<AttributeType>,
67 #[metastructure(required = true, pii = "attribute_pii_from_conventions")]
68 pub value: Annotated<Value>,
69}
70
71macro_rules! impl_from {
72 ($ty:ident, $aty: expr) => {
73 impl From<Annotated<$ty>> for AttributeValue {
74 fn from(value: Annotated<$ty>) -> Self {
75 AttributeValue {
76 ty: Annotated::new($aty),
77 value: value.map_value(Into::into),
78 }
79 }
80 }
81
82 impl From<$ty> for AttributeValue {
83 fn from(value: $ty) -> Self {
84 AttributeValue::from(Annotated::new(value))
85 }
86 }
87 };
88}
89
90impl_from!(String, AttributeType::String);
91impl_from!(i64, AttributeType::Integer);
92impl_from!(f64, AttributeType::Double);
93impl_from!(bool, AttributeType::Boolean);
94
95pub fn attribute_pii_from_conventions(state: &ProcessingState) -> Pii {
106 for key in state.keys() {
107 let Some(info) = relay_conventions::attribute_info(key) else {
108 continue;
109 };
110
111 return match info.pii {
112 relay_conventions::Pii::True => Pii::True,
113 relay_conventions::Pii::False => Pii::False,
114 relay_conventions::Pii::Maybe => Pii::Maybe,
115 };
116 }
117
118 Pii::True
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub enum AttributeType {
123 Boolean,
124 Integer,
125 Double,
126 String,
127 Unknown(String),
128}
129
130impl ProcessValue for AttributeType {}
131
132impl AttributeType {
133 pub fn as_str(&self) -> &str {
134 match self {
135 Self::Boolean => "boolean",
136 Self::Integer => "integer",
137 Self::Double => "double",
138 Self::String => "string",
139 Self::Unknown(value) => value,
140 }
141 }
142
143 pub fn unknown_string() -> String {
144 "unknown".to_owned()
145 }
146}
147
148impl fmt::Display for AttributeType {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 write!(f, "{}", self.as_str())
151 }
152}
153
154impl From<String> for AttributeType {
155 fn from(value: String) -> Self {
156 match value.as_str() {
157 "boolean" => Self::Boolean,
158 "integer" => Self::Integer,
159 "double" => Self::Double,
160 "string" => Self::String,
161 _ => Self::Unknown(value),
162 }
163 }
164}
165
166impl Empty for AttributeType {
167 #[inline]
168 fn is_empty(&self) -> bool {
169 false
170 }
171}
172
173impl FromValue for AttributeType {
174 fn from_value(value: Annotated<Value>) -> Annotated<Self> {
175 match String::from_value(value) {
176 Annotated(Some(value), meta) => Annotated(Some(value.into()), meta),
177 Annotated(None, meta) => Annotated(None, meta),
178 }
179 }
180}
181
182impl IntoValue for AttributeType {
183 fn into_value(self) -> Value
184 where
185 Self: Sized,
186 {
187 Value::String(match self {
188 Self::Unknown(s) => s,
189 s => s.to_string(),
190 })
191 }
192
193 fn serialize_payload<S>(&self, s: S, _behavior: SkipSerialization) -> Result<S::Ok, S::Error>
194 where
195 Self: Sized,
196 S: serde::Serializer,
197 {
198 serde::ser::Serialize::serialize(self.as_str(), s)
199 }
200}
201
202#[derive(Debug, Clone, Default, PartialEq, Empty, FromValue, IntoValue)]
204pub struct Attributes(pub Object<Attribute>);
205
206impl Attributes {
207 pub fn new() -> Self {
209 Self(Object::new())
210 }
211
212 pub fn get_value<Q>(&self, key: &Q) -> Option<&Value>
214 where
215 String: Borrow<Q>,
216 Q: Ord + ?Sized,
217 {
218 self.get_annotated_value(key)?.value()
219 }
220
221 pub fn get_attribute<Q>(&self, key: &Q) -> Option<&Attribute>
223 where
224 String: Borrow<Q>,
225 Q: Ord + ?Sized,
226 {
227 self.0.get(key)?.value()
228 }
229
230 pub fn get_annotated_value<Q>(&self, key: &Q) -> Option<&Annotated<Value>>
232 where
233 String: Borrow<Q>,
234 Q: Ord + ?Sized,
235 {
236 Some(&self.0.get(key)?.value()?.value.value)
237 }
238
239 pub fn insert<K: Into<String>, V: Into<AttributeValue>>(&mut self, key: K, value: V) {
241 fn inner(slf: &mut Attributes, key: String, value: AttributeValue) {
242 let attribute = Annotated::new(Attribute {
243 value,
244 other: Default::default(),
245 });
246 slf.0.insert(key, attribute);
247 }
248 let value = value.into();
249 if !value.value.is_empty() {
250 inner(self, key.into(), value);
251 }
252 }
253
254 pub fn insert_if_missing<F, V>(&mut self, key: &str, value: F)
256 where
257 F: FnOnce() -> V,
258 V: Into<AttributeValue>,
259 {
260 if !self.0.contains_key(key) {
261 self.insert(key.to_owned(), value());
262 }
263 }
264
265 pub fn contains_key<Q>(&self, key: &Q) -> bool
267 where
268 String: Borrow<Q>,
269 Q: Ord + ?Sized,
270 {
271 self.0.contains_key(key)
272 }
273
274 pub fn remove<Q>(&mut self, key: &Q) -> Option<Annotated<Attribute>>
276 where
277 String: Borrow<Q>,
278 Q: Ord + ?Sized,
279 {
280 self.0.remove(key)
281 }
282}
283
284impl IntoIterator for Attributes {
285 type Item = (String, Annotated<Attribute>);
286
287 type IntoIter = std::collections::btree_map::IntoIter<String, Annotated<Attribute>>;
288
289 fn into_iter(self) -> Self::IntoIter {
290 self.0.into_iter()
291 }
292}
293
294impl FromIterator<(String, Annotated<Attribute>)> for Attributes {
295 fn from_iter<T: IntoIterator<Item = (String, Annotated<Attribute>)>>(iter: T) -> Self {
296 Self(Object::from_iter(iter))
297 }
298}
299
300impl<const N: usize> From<[(String, Annotated<Attribute>); N]> for Attributes {
301 fn from(value: [(String, Annotated<Attribute>); N]) -> Self {
302 value.into_iter().collect()
303 }
304}
305
306impl ProcessValue for Attributes {
307 #[inline]
308 fn value_type(&self) -> EnumSet<ValueType> {
309 EnumSet::only(ValueType::Object)
310 }
311
312 #[inline]
313 fn process_value<P>(
314 &mut self,
315 meta: &mut Meta,
316 processor: &mut P,
317 state: &ProcessingState<'_>,
318 ) -> ProcessingResult
319 where
320 P: Processor,
321 {
322 processor.process_attributes(self, meta, state)
323 }
324
325 fn process_child_values<P>(
326 &mut self,
327 processor: &mut P,
328 state: &ProcessingState<'_>,
329 ) -> ProcessingResult
330 where
331 P: Processor,
332 {
333 let enter_state = state.enter_nothing(Some(Cow::Borrowed(state.attrs())));
334 self.0.process_child_values(processor, &enter_state)
335 }
336}