n0_error/
meta.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use std::sync::OnceLock;

/// Wrapper around `std::panic::Location` used for display in reports.
#[derive(derive_more::Debug, derive_more::Display, Clone, Copy)]
#[debug("{_0:?}")]
pub struct Location(&'static std::panic::Location<'static>);

/// Captured metadata for an error creation site.
///
/// Currently this only contains the call-site [`Location`].
#[derive(Debug)]
pub struct Meta {
    location: Option<Location>,
}

/// Creates new [`Meta`] capturing the caller location.
#[track_caller]
pub fn meta() -> Meta {
    Meta::default()
}

impl Default for Meta {
    #[track_caller]
    fn default() -> Self {
        Self {
            location: location(),
        }
    }
}

impl Meta {
    #[track_caller]
    /// Creates new [`Meta`] capturing the caller location.
    pub fn new() -> Self {
        Self::default()
    }
    /// Returns the captured location.
    pub fn location(&self) -> Option<&Location> {
        self.location.as_ref()
    }
}

#[cfg(test)]
static BACKTRACE_ENABLED: OnceLock<std::sync::RwLock<bool>> = OnceLock::new();

#[cfg(not(test))]
static BACKTRACE_ENABLED: OnceLock<bool> = OnceLock::new();

#[doc(hidden)]
pub fn backtrace_enabled() -> bool {
    let from_env = || {
        matches!(
            std::env::var("RUST_BACKTRACE").as_deref(),
            Ok("1") | Ok("full")
        ) || matches!(std::env::var("RUST_ERROR_LOCATION").as_deref(), Ok("1"))
    };
    #[cfg(test)]
    return *(BACKTRACE_ENABLED
        .get_or_init(|| std::sync::RwLock::new(from_env()))
        .read()
        .unwrap());

    #[cfg(not(test))]
    return *(BACKTRACE_ENABLED.get_or_init(from_env));
}

#[doc(hidden)]
#[cfg(test)]
pub fn set_backtrace_enabled(value: bool) {
    let mut inner = BACKTRACE_ENABLED
        .get_or_init(Default::default)
        .write()
        .unwrap();
    *inner = value;
}

#[track_caller]
fn location() -> Option<Location> {
    if backtrace_enabled() {
        Some(Location(std::panic::Location::caller()))
    } else {
        None
    }
}