gsm_core/messaging_card/
telemetry.rs

1use crate::messaging_card::tier::Tier;
2
3#[derive(Debug, Clone, PartialEq)]
4pub enum TelemetryEvent {
5    Rendered {
6        platform: String,
7        tier: Tier,
8        warnings: usize,
9        used_modal: bool,
10        limit_exceeded: bool,
11        sanitized_count: usize,
12        url_blocked_count: usize,
13        downgrade_count: usize,
14        native_count: usize,
15    },
16    Downgraded {
17        from: Tier,
18        to: Tier,
19    },
20}
21
22pub trait TelemetryHook: Send + Sync {
23    fn emit(&self, event: TelemetryEvent);
24}
25
26#[derive(Default)]
27pub struct NullTelemetry;
28
29impl TelemetryHook for NullTelemetry {
30    fn emit(&self, _event: TelemetryEvent) {}
31}
32
33pub struct CardTelemetry<'a> {
34    hook: &'a dyn TelemetryHook,
35}
36
37impl<'a> CardTelemetry<'a> {
38    pub fn new(hook: &'a dyn TelemetryHook) -> Self {
39        Self { hook }
40    }
41
42    pub fn downgrading(&self, from: Tier, to: Tier) {
43        self.hook.emit(TelemetryEvent::Downgraded { from, to });
44    }
45
46    #[allow(clippy::too_many_arguments)]
47    pub fn rendered(
48        &self,
49        platform: &str,
50        tier: Tier,
51        warnings: usize,
52        used_modal: bool,
53        limit_exceeded: bool,
54        sanitized_count: usize,
55        url_blocked_count: usize,
56        downgraded: bool,
57    ) {
58        let downgrade_count = if downgraded { 1 } else { 0 };
59        let native_count = if downgraded { 0 } else { 1 };
60        self.hook.emit(TelemetryEvent::Rendered {
61            platform: platform.to_string(),
62            tier,
63            warnings,
64            used_modal,
65            limit_exceeded,
66            sanitized_count,
67            url_blocked_count,
68            downgrade_count,
69            native_count,
70        });
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    struct TestTelemetry {
79        pub events: std::sync::Mutex<Vec<TelemetryEvent>>,
80    }
81
82    impl TestTelemetry {
83        fn new() -> Self {
84            Self {
85                events: std::sync::Mutex::new(Vec::new()),
86            }
87        }
88    }
89
90    impl TelemetryHook for TestTelemetry {
91        fn emit(&self, event: TelemetryEvent) {
92            self.events.lock().unwrap().push(event);
93        }
94    }
95
96    #[test]
97    fn telemetry_records_events() {
98        let hook = TestTelemetry::new();
99        let telemetry = CardTelemetry::new(&hook);
100        telemetry.downgrading(Tier::Premium, Tier::Basic);
101        telemetry.rendered("teams", Tier::Basic, 1, true, false, 0, 0, false);
102        let events = hook.events.lock().unwrap();
103        assert_eq!(events.len(), 2);
104    }
105}