relay_event_schema/protocol/
breakdowns.rs

1use std::ops::{Deref, DerefMut};
2
3use relay_protocol::{Annotated, Empty, Error, FromValue, IntoValue, Object, Value};
4
5use crate::processor::ProcessValue;
6use crate::protocol::Measurements;
7
8/// A map of breakdowns.
9/// Breakdowns may be available on any event type. A breakdown are product-defined measurement values
10/// generated by the client, or materialized during ingestion. For example, for transactions, we may
11/// emit span operation breakdowns based on the attached span data.
12#[derive(Clone, Debug, Default, PartialEq, Empty, IntoValue, ProcessValue)]
13pub struct Breakdowns(pub Object<Measurements>);
14
15impl Breakdowns {
16    pub fn is_valid_breakdown_name(name: &str) -> bool {
17        !name.is_empty()
18            && name.starts_with(|c: char| c.is_ascii_alphabetic())
19            && name
20                .chars()
21                .all(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.'))
22    }
23}
24
25impl FromValue for Breakdowns {
26    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
27        let mut processing_errors = Vec::new();
28
29        let mut breakdowns = Object::from_value(value).map_value(|breakdowns| {
30            let breakdowns = breakdowns
31                .into_iter()
32                .filter_map(|(name, object)| {
33                    let name = name.trim();
34
35                    if Breakdowns::is_valid_breakdown_name(name) {
36                        return Some((name.into(), object));
37                    } else {
38                        processing_errors.push(Error::invalid(format!(
39                            "breakdown name '{name}' can contain only characters a-z0-9._"
40                        )));
41                    }
42
43                    None
44                })
45                .collect();
46
47            Self(breakdowns)
48        });
49
50        for error in processing_errors {
51            breakdowns.meta_mut().add_error(error);
52        }
53
54        breakdowns
55    }
56}
57
58impl Deref for Breakdowns {
59    type Target = Object<Measurements>;
60
61    fn deref(&self) -> &Self::Target {
62        &self.0
63    }
64}
65
66impl DerefMut for Breakdowns {
67    fn deref_mut(&mut self) -> &mut Self::Target {
68        &mut self.0
69    }
70}