Skip to main content

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}