charon_error/settings/er_global_settings.rs
1//! Default global settings for the error report system.
2//!
3//! [`ERGlobalSettings`] controls link formatting, known-error checking,
4//! and which [`SubmitErrorReport`](crate::SubmitErrorReport) implementation
5//! is used by the panic hook.
6
7use crate::{
8 ErrorReport, GlobalSettings, GlobalSettingsError, LinkDebugIde, ResultExt, SimpleErrorReport,
9 StringError, SubmitErrorReport,
10};
11use std::sync::{OnceLock, RwLock, RwLockReadGuard};
12/// Global configuration for the error report system.
13///
14/// Set once at application startup via
15/// [`GlobalSettings::set_global_settings`].
16#[derive(Debug, Clone)]
17pub struct ERGlobalSettings {
18 /// Controls how source locations are rendered in terminal output.
19 ///
20 /// [`LinkDebugIde::File`] emits clickable `file://` hyperlinks,
21 /// [`LinkDebugIde::Vscode`] emits `vscode://file/` links, and
22 /// [`LinkDebugIde::NoLink`] outputs plain `file:line:column` text.
23 ///
24 /// Automatically overridden to [`LinkDebugIde::NoLink`] when the
25 /// `NO_COLOR` environment variable is set.
26 ///
27 /// Default: [`LinkDebugIde::File`]
28 pub link_format: LinkDebugIde,
29
30 /// Function called during a panic to check if the error message matches a
31 /// known issue. Receives the panic message as `&str`.
32 ///
33 /// Return `Some(message)` with a user-friendly explanation if the error is
34 /// recognized, or `None` to treat it as an unknown error and generate a
35 /// full report.
36 ///
37 /// Used by [`setup_panic!`] before building the error submission report.
38 ///
39 /// Default: [`check_known_error_types_default`] (always returns `None`)
40 pub check_known_error_types_fn: fn(&str) -> Option<String>,
41
42 /// Function that creates a [`SubmitErrorReport`] from an [`ErrorReport`].
43 ///
44 /// Used by [`setup_panic!`] to generate error submission reports.
45 ///
46 /// Default: creates a [`SimpleErrorReport`]
47 pub submit_error_reporter_fn:
48 for<'a> fn(&'a ErrorReport) -> Box<dyn SubmitErrorReport<'a> + 'a>,
49}
50
51impl ERGlobalSettings {
52 /// Read the global settings, initializing with [`Default`] if not yet set.
53 ///
54 /// Unlike [`GlobalSettings::get_global_settings`] which returns an error
55 /// when unset, this method lazily creates a default configuration.
56 pub fn get_or_default_settings() -> Result<RwLockReadGuard<'static, Self>, ErrorReport> {
57 let setting_reader =
58 ERROR_REPORT_GLOBAL_SETTINGS.get_or_init(|| RwLock::new(ERGlobalSettings::default()));
59
60 setting_reader
61 .read()
62 .map_err(StringError::from_error)
63 .change_context(GlobalSettingsError::AcquireReadLockFailed)
64 .error_attach_public_string(
65 "settings_object",
66 Self::get_setting_object_name().to_string(),
67 )
68 }
69}
70
71impl GlobalSettings for ERGlobalSettings {
72 type Setting = ERGlobalSettings;
73
74 fn once_lock() -> &'static OnceLock<RwLock<Self::Setting>> {
75 &ERROR_REPORT_GLOBAL_SETTINGS
76 }
77 fn get_setting_object_name() -> &'static str {
78 "ERROR_REPORT_GLOBAL_SETTINGS"
79 }
80}
81
82static ERROR_REPORT_GLOBAL_SETTINGS: OnceLock<RwLock<ERGlobalSettings>> = OnceLock::new();
83
84impl Default for ERGlobalSettings {
85 fn default() -> Self {
86 Self {
87 link_format: LinkDebugIde::File,
88 check_known_error_types_fn: check_known_error_types_default,
89 submit_error_reporter_fn: |er| Box::new(SimpleErrorReport::new(er)),
90 }
91 }
92}
93
94/// Default known-error checker that always returns `None` (no errors recognized).
95pub fn check_known_error_types_default(_error_message: &str) -> Option<String> {
96 None
97}