Skip to main content

cadmus_core/view/settings_editor/kinds/
telemetry.rs

1//! Setting kinds for the Telemetry category.
2
3use super::{SettingData, SettingIdentity, SettingKind, ToggleSettings, WidgetKind};
4use crate::fl;
5use crate::settings::Settings;
6use crate::view::{Bus, EntryId, EntryKind, Event, ToggleEvent};
7
8#[cfg(any(feature = "tracing", feature = "profiling"))]
9use super::InputSettingKind;
10#[cfg(any(feature = "tracing", feature = "profiling"))]
11use crate::view::ViewId;
12use std::str::FromStr;
13
14/// Logging enabled toggle setting
15pub struct LoggingEnabled;
16
17impl SettingKind for LoggingEnabled {
18    fn identity(&self) -> SettingIdentity {
19        SettingIdentity::LoggingEnabled
20    }
21
22    fn label(&self, _settings: &Settings) -> String {
23        fl!("settings-telemetry-enable-logging")
24    }
25
26    fn fetch(&self, settings: &Settings) -> SettingData {
27        SettingData {
28            value: settings.logging.enabled.to_string(),
29            widget: WidgetKind::Toggle {
30                left_label: fl!("settings-general-toggle-on"),
31                right_label: fl!("settings-general-toggle-off"),
32                enabled: settings.logging.enabled,
33                tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::LoggingEnabled)),
34            },
35        }
36    }
37
38    fn handle(
39        &self,
40        evt: &Event,
41        settings: &mut Settings,
42        _bus: &mut Bus,
43    ) -> (Option<String>, bool) {
44        if let Event::Toggle(ToggleEvent::Setting(ToggleSettings::LoggingEnabled)) = evt {
45            settings.logging.enabled = !settings.logging.enabled;
46            return (Some(settings.logging.enabled.to_string()), true);
47        }
48        (None, false)
49    }
50}
51
52/// Log level selection setting
53pub struct LogLevel;
54
55impl LogLevel {
56    fn level_to_i18n(level: &tracing::Level) -> String {
57        match *level {
58            tracing::Level::TRACE => fl!("settings-log-level-trace"),
59            tracing::Level::DEBUG => fl!("settings-log-level-debug"),
60            tracing::Level::INFO => fl!("settings-log-level-info"),
61            tracing::Level::WARN => fl!("settings-log-level-warn"),
62            tracing::Level::ERROR => fl!("settings-log-level-error"),
63        }
64    }
65}
66
67impl SettingKind for LogLevel {
68    fn identity(&self) -> SettingIdentity {
69        SettingIdentity::LogLevel
70    }
71
72    fn label(&self, _settings: &Settings) -> String {
73        fl!("settings-telemetry-log-level")
74    }
75
76    fn fetch(&self, settings: &Settings) -> SettingData {
77        let current = tracing::Level::from_str(settings.logging.level.as_str())
78            .unwrap_or(tracing::Level::INFO);
79
80        let entries = vec![
81            EntryKind::RadioButton(
82                Self::level_to_i18n(&tracing::Level::TRACE),
83                EntryId::SetLogLevel(tracing::Level::TRACE),
84                current == tracing::Level::TRACE,
85            ),
86            EntryKind::RadioButton(
87                Self::level_to_i18n(&tracing::Level::DEBUG),
88                EntryId::SetLogLevel(tracing::Level::DEBUG),
89                current == tracing::Level::DEBUG,
90            ),
91            EntryKind::RadioButton(
92                Self::level_to_i18n(&tracing::Level::INFO),
93                EntryId::SetLogLevel(tracing::Level::INFO),
94                current == tracing::Level::INFO,
95            ),
96            EntryKind::RadioButton(
97                Self::level_to_i18n(&tracing::Level::WARN),
98                EntryId::SetLogLevel(tracing::Level::WARN),
99                current == tracing::Level::WARN,
100            ),
101            EntryKind::RadioButton(
102                Self::level_to_i18n(&tracing::Level::ERROR),
103                EntryId::SetLogLevel(tracing::Level::ERROR),
104                current == tracing::Level::ERROR,
105            ),
106        ];
107
108        SettingData {
109            value: Self::level_to_i18n(&current),
110            widget: WidgetKind::SubMenu(entries),
111        }
112    }
113
114    fn handle(
115        &self,
116        evt: &Event,
117        settings: &mut Settings,
118        _bus: &mut Bus,
119    ) -> (Option<String>, bool) {
120        if let Event::Select(EntryId::SetLogLevel(ref level)) = evt {
121            settings.logging.level = level.to_string();
122            return (Some(Self::level_to_i18n(level)), true);
123        }
124        (None, false)
125    }
126}
127
128/// OTLP endpoint configuration setting (tracing feature)
129#[cfg(feature = "tracing")]
130pub struct OtlpEndpoint;
131
132#[cfg(feature = "tracing")]
133impl SettingKind for OtlpEndpoint {
134    fn identity(&self) -> SettingIdentity {
135        SettingIdentity::OtlpEndpoint
136    }
137
138    fn label(&self, _settings: &Settings) -> String {
139        fl!("settings-telemetry-otlp-endpoint")
140    }
141
142    fn fetch(&self, settings: &Settings) -> SettingData {
143        let value = settings
144            .logging
145            .otlp_endpoint
146            .clone()
147            .unwrap_or_else(|| fl!("settings-general-not-set"));
148
149        SettingData {
150            value,
151            widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditOtlpEndpoint)),
152        }
153    }
154
155    fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
156        Some(self)
157    }
158}
159
160#[cfg(feature = "tracing")]
161impl InputSettingKind for OtlpEndpoint {
162    fn submit_view_id(&self) -> ViewId {
163        ViewId::OtlpEndpointInput
164    }
165
166    fn open_entry_id(&self) -> EntryId {
167        EntryId::EditOtlpEndpoint
168    }
169
170    fn input_label(&self) -> String {
171        fl!("settings-telemetry-otlp-endpoint")
172    }
173
174    fn input_max_chars(&self) -> usize {
175        50
176    }
177
178    fn current_text(&self, settings: &Settings) -> String {
179        settings.logging.otlp_endpoint.clone().unwrap_or_default()
180    }
181
182    fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
183        let trimmed = text.trim();
184        settings.logging.otlp_endpoint = if trimmed.is_empty() {
185            None
186        } else {
187            Some(trimmed.to_string())
188        };
189        settings
190            .logging
191            .otlp_endpoint
192            .clone()
193            .unwrap_or_else(|| fl!("settings-general-not-set"))
194    }
195}
196
197/// Pyroscope server endpoint configuration setting (profiling feature)
198#[cfg(feature = "profiling")]
199pub struct PyroscopeEndpoint;
200
201#[cfg(feature = "profiling")]
202impl SettingKind for PyroscopeEndpoint {
203    fn identity(&self) -> SettingIdentity {
204        SettingIdentity::PyroscopeEndpoint
205    }
206
207    fn label(&self, _settings: &Settings) -> String {
208        fl!("settings-telemetry-pyroscope-endpoint")
209    }
210
211    fn fetch(&self, settings: &Settings) -> SettingData {
212        let value = settings
213            .logging
214            .pyroscope_endpoint
215            .clone()
216            .unwrap_or_else(|| fl!("settings-general-not-set"));
217
218        SettingData {
219            value,
220            widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditPyroscopeEndpoint)),
221        }
222    }
223
224    fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
225        Some(self)
226    }
227}
228
229#[cfg(feature = "profiling")]
230impl InputSettingKind for PyroscopeEndpoint {
231    fn submit_view_id(&self) -> ViewId {
232        ViewId::PyroscopeEndpointInput
233    }
234
235    fn open_entry_id(&self) -> EntryId {
236        EntryId::EditPyroscopeEndpoint
237    }
238
239    fn input_label(&self) -> String {
240        fl!("settings-telemetry-pyroscope-endpoint")
241    }
242
243    fn input_max_chars(&self) -> usize {
244        50
245    }
246
247    fn current_text(&self, settings: &Settings) -> String {
248        settings
249            .logging
250            .pyroscope_endpoint
251            .clone()
252            .unwrap_or_default()
253    }
254
255    fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
256        let trimmed = text.trim();
257        settings.logging.pyroscope_endpoint = if trimmed.is_empty() {
258            None
259        } else {
260            Some(trimmed.to_string())
261        };
262        settings
263            .logging
264            .pyroscope_endpoint
265            .clone()
266            .unwrap_or_else(|| fl!("settings-general-not-set"))
267    }
268}
269
270/// Kernel logging toggle setting (test + kobo features)
271#[cfg(all(feature = "test", feature = "kobo"))]
272pub struct EnableKernLog;
273
274#[cfg(all(feature = "test", feature = "kobo"))]
275impl SettingKind for EnableKernLog {
276    fn identity(&self) -> SettingIdentity {
277        SettingIdentity::EnableKernLog
278    }
279
280    fn label(&self, _settings: &Settings) -> String {
281        fl!("settings-telemetry-enable-kernel-log")
282    }
283
284    fn fetch(&self, settings: &Settings) -> SettingData {
285        SettingData {
286            value: settings.logging.enable_kern_log.to_string(),
287            widget: WidgetKind::Toggle {
288                left_label: fl!("settings-general-toggle-on"),
289                right_label: fl!("settings-general-toggle-off"),
290                enabled: settings.logging.enable_kern_log,
291                tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::EnableKernLog)),
292            },
293        }
294    }
295
296    fn handle(
297        &self,
298        evt: &Event,
299        settings: &mut Settings,
300        _bus: &mut Bus,
301    ) -> (Option<String>, bool) {
302        if let Event::Toggle(ToggleEvent::Setting(ToggleSettings::EnableKernLog)) = evt {
303            settings.logging.enable_kern_log = !settings.logging.enable_kern_log;
304            return (Some(settings.logging.enable_kern_log.to_string()), true);
305        }
306        (None, false)
307    }
308}
309
310/// D-Bus logging toggle setting (test + kobo features)
311#[cfg(all(feature = "test", feature = "kobo"))]
312pub struct EnableDbusLog;
313
314#[cfg(all(feature = "test", feature = "kobo"))]
315impl SettingKind for EnableDbusLog {
316    fn identity(&self) -> SettingIdentity {
317        SettingIdentity::EnableDbusLog
318    }
319
320    fn label(&self, _settings: &Settings) -> String {
321        fl!("settings-telemetry-enable-dbus-log")
322    }
323
324    fn fetch(&self, settings: &Settings) -> SettingData {
325        SettingData {
326            value: settings.logging.enable_dbus_log.to_string(),
327            widget: WidgetKind::Toggle {
328                left_label: fl!("settings-general-toggle-on"),
329                right_label: fl!("settings-general-toggle-off"),
330                enabled: settings.logging.enable_dbus_log,
331                tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::EnableDbusLog)),
332            },
333        }
334    }
335
336    fn handle(
337        &self,
338        evt: &Event,
339        settings: &mut Settings,
340        _bus: &mut Bus,
341    ) -> (Option<String>, bool) {
342        if let Event::Toggle(ToggleEvent::Setting(ToggleSettings::EnableDbusLog)) = evt {
343            settings.logging.enable_dbus_log = !settings.logging.enable_dbus_log;
344            return (Some(settings.logging.enable_dbus_log.to_string()), true);
345        }
346        (None, false)
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353    use crate::settings::Settings;
354    use crate::view::settings_editor::kinds::ToggleSettings;
355    use crate::view::{Bus, EntryId, Event, ToggleEvent};
356    use std::collections::VecDeque;
357
358    mod logging_enabled {
359        use super::*;
360
361        #[test]
362        fn handle_toggle_disables_when_enabled() {
363            let setting = LoggingEnabled;
364            let mut settings = Settings::default();
365            settings.logging.enabled = true;
366            let mut bus: Bus = VecDeque::new();
367            let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::LoggingEnabled));
368
369            let result = setting.handle(&event, &mut settings, &mut bus);
370
371            assert!(result.0.is_some());
372            assert!(!settings.logging.enabled);
373        }
374
375        #[test]
376        fn handle_toggle_enables_when_disabled() {
377            let setting = LoggingEnabled;
378            let mut settings = Settings::default();
379            settings.logging.enabled = false;
380            let mut bus: Bus = VecDeque::new();
381            let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::LoggingEnabled));
382
383            let result = setting.handle(&event, &mut settings, &mut bus);
384
385            assert!(result.0.is_some());
386            assert!(settings.logging.enabled);
387        }
388
389        #[test]
390        fn handle_returns_none_for_wrong_event() {
391            let setting = LoggingEnabled;
392            let mut settings = Settings::default();
393            let mut bus: Bus = VecDeque::new();
394
395            let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
396
397            assert!(result.0.is_none());
398        }
399
400        #[test]
401        fn handle_returns_none_for_wrong_toggle() {
402            let setting = LoggingEnabled;
403            let mut settings = Settings::default();
404            let mut bus: Bus = VecDeque::new();
405
406            let result = setting.handle(
407                &Event::Toggle(ToggleEvent::Setting(ToggleSettings::SleepCover)),
408                &mut settings,
409                &mut bus,
410            );
411
412            assert!(result.0.is_none());
413        }
414    }
415
416    mod log_level {
417        use super::*;
418
419        #[test]
420        fn handle_set_level_updates_settings() {
421            let setting = LogLevel;
422            let mut settings = Settings::default();
423            settings.logging.level = "INFO".to_string();
424            let mut bus: Bus = VecDeque::new();
425            let event = Event::Select(EntryId::SetLogLevel(tracing::Level::WARN));
426
427            let result = setting.handle(&event, &mut settings, &mut bus);
428
429            assert!(result.0.is_some());
430            assert_eq!(settings.logging.level, "WARN");
431        }
432
433        #[test]
434        fn handle_can_set_all_levels() {
435            let setting = LogLevel;
436            let mut settings = Settings::default();
437            let mut bus: Bus = VecDeque::new();
438
439            for level in [
440                tracing::Level::TRACE,
441                tracing::Level::DEBUG,
442                tracing::Level::INFO,
443                tracing::Level::WARN,
444                tracing::Level::ERROR,
445            ] {
446                let event = Event::Select(EntryId::SetLogLevel(level));
447                setting.handle(&event, &mut settings, &mut bus);
448                assert_eq!(settings.logging.level, level.to_string());
449            }
450        }
451
452        #[test]
453        fn handle_returns_none_for_wrong_event() {
454            let setting = LogLevel;
455            let mut settings = Settings::default();
456            let mut bus: Bus = VecDeque::new();
457
458            let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
459
460            assert!(result.0.is_none());
461        }
462    }
463
464    #[cfg(feature = "tracing")]
465    mod otlp_endpoint {
466        use super::*;
467        use crate::view::settings_editor::kinds::InputSettingKind;
468
469        #[test]
470        fn apply_text_sets_endpoint() {
471            let setting = OtlpEndpoint;
472            let mut settings = Settings::default();
473
474            let display = setting.apply_text("http://otel:4317", &mut settings);
475
476            assert_eq!(display, "http://otel:4317");
477            assert_eq!(
478                settings.logging.otlp_endpoint,
479                Some("http://otel:4317".to_string())
480            );
481        }
482
483        #[test]
484        fn apply_text_trims_whitespace() {
485            let setting = OtlpEndpoint;
486            let mut settings = Settings::default();
487
488            let display = setting.apply_text("  http://otel:4317  ", &mut settings);
489
490            assert_eq!(display, "http://otel:4317");
491            assert_eq!(
492                settings.logging.otlp_endpoint,
493                Some("http://otel:4317".to_string())
494            );
495        }
496
497        #[test]
498        fn apply_text_empty_clears_endpoint() {
499            let setting = OtlpEndpoint;
500            let mut settings = Settings::default();
501            settings.logging.otlp_endpoint = Some("http://old:4317".to_string());
502
503            let display = setting.apply_text("", &mut settings);
504
505            assert_eq!(display, "Not set");
506            assert_eq!(settings.logging.otlp_endpoint, None);
507        }
508
509        #[test]
510        fn apply_text_whitespace_only_clears_endpoint() {
511            let setting = OtlpEndpoint;
512            let mut settings = Settings::default();
513            settings.logging.otlp_endpoint = Some("http://old:4317".to_string());
514
515            let display = setting.apply_text("   ", &mut settings);
516
517            assert_eq!(display, "Not set");
518            assert_eq!(settings.logging.otlp_endpoint, None);
519        }
520    }
521
522    #[cfg(feature = "test")]
523    mod enable_kern_log {
524        use super::*;
525
526        #[test]
527        fn handle_toggle_enables_when_disabled() {
528            let setting = EnableKernLog;
529            let mut settings = Settings::default();
530            settings.logging.enable_kern_log = false;
531            let mut bus: Bus = VecDeque::new();
532            let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::EnableKernLog));
533
534            let result = setting.handle(&event, &mut settings, &mut bus);
535
536            assert!(result.0.is_some());
537            assert!(settings.logging.enable_kern_log);
538        }
539
540        #[test]
541        fn handle_toggle_disables_when_enabled() {
542            let setting = EnableKernLog;
543            let mut settings = Settings::default();
544            settings.logging.enable_kern_log = true;
545            let mut bus: Bus = VecDeque::new();
546            let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::EnableKernLog));
547
548            let result = setting.handle(&event, &mut settings, &mut bus);
549
550            assert!(result.0.is_some());
551            assert!(!settings.logging.enable_kern_log);
552        }
553
554        #[test]
555        fn handle_returns_none_for_wrong_event() {
556            let setting = EnableKernLog;
557            let mut settings = Settings::default();
558            let mut bus: Bus = VecDeque::new();
559
560            let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
561
562            assert!(result.0.is_none());
563        }
564
565        #[test]
566        fn handle_returns_none_for_wrong_toggle() {
567            let setting = EnableKernLog;
568            let mut settings = Settings::default();
569            let mut bus: Bus = VecDeque::new();
570
571            let result = setting.handle(
572                &Event::Toggle(ToggleEvent::Setting(ToggleSettings::LoggingEnabled)),
573                &mut settings,
574                &mut bus,
575            );
576
577            assert!(result.0.is_none());
578        }
579    }
580
581    #[cfg(all(feature = "test", feature = "kobo"))]
582    mod enable_dbus_log {
583        use super::*;
584
585        #[test]
586        fn handle_toggle_enables_when_disabled() {
587            let setting = EnableDbusLog;
588            let mut settings = Settings::default();
589            settings.logging.enable_dbus_log = false;
590            let mut bus: Bus = VecDeque::new();
591            let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::EnableDbusLog));
592
593            let result = setting.handle(&event, &mut settings, &mut bus);
594
595            assert!(result.0.is_some());
596            assert!(settings.logging.enable_dbus_log);
597        }
598
599        #[test]
600        fn handle_toggle_disables_when_enabled() {
601            let setting = EnableDbusLog;
602            let mut settings = Settings::default();
603            settings.logging.enable_dbus_log = true;
604            let mut bus: Bus = VecDeque::new();
605            let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::EnableDbusLog));
606
607            let result = setting.handle(&event, &mut settings, &mut bus);
608
609            assert!(result.0.is_some());
610            assert!(!settings.logging.enable_dbus_log);
611        }
612
613        #[test]
614        fn handle_returns_none_for_wrong_event() {
615            let setting = EnableDbusLog;
616            let mut settings = Settings::default();
617            let mut bus: Bus = VecDeque::new();
618
619            let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
620
621            assert!(result.0.is_none());
622        }
623
624        #[test]
625        fn handle_returns_none_for_wrong_toggle() {
626            let setting = EnableDbusLog;
627            let mut settings = Settings::default();
628            let mut bus: Bus = VecDeque::new();
629
630            let result = setting.handle(
631                &Event::Toggle(ToggleEvent::Setting(ToggleSettings::LoggingEnabled)),
632                &mut settings,
633                &mut bus,
634            );
635
636            assert!(result.0.is_none());
637        }
638    }
639}