gsm_core/messaging_card/
telemetry.rs1use 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}