1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use std::ops::{Deref, DerefMut};

use relay_protocol::{Annotated, Empty, Error, FromValue, IntoValue, Object, Value};

use crate::processor::ProcessValue;
use crate::protocol::Measurements;

/// A map of breakdowns.
/// Breakdowns may be available on any event type. A breakdown are product-defined measurement values
/// generated by the client, or materialized during ingestion. For example, for transactions, we may
/// emit span operation breakdowns based on the attached span data.
#[derive(Clone, Debug, Default, PartialEq, Empty, IntoValue, ProcessValue)]
pub struct Breakdowns(pub Object<Measurements>);

impl Breakdowns {
    pub fn is_valid_breakdown_name(name: &str) -> bool {
        !name.is_empty()
            && name.starts_with(|c: char| c.is_ascii_alphabetic())
            && name
                .chars()
                .all(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.'))
    }
}

impl FromValue for Breakdowns {
    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
        let mut processing_errors = Vec::new();

        let mut breakdowns = Object::from_value(value).map_value(|breakdowns| {
            let breakdowns = breakdowns
                .into_iter()
                .filter_map(|(name, object)| {
                    let name = name.trim();

                    if Breakdowns::is_valid_breakdown_name(name) {
                        return Some((name.into(), object));
                    } else {
                        processing_errors.push(Error::invalid(format!(
                            "breakdown name '{name}' can contain only characters a-z0-9._"
                        )));
                    }

                    None
                })
                .collect();

            Self(breakdowns)
        });

        for error in processing_errors {
            breakdowns.meta_mut().add_error(error);
        }

        breakdowns
    }
}

impl Deref for Breakdowns {
    type Target = Object<Measurements>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Breakdowns {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}