n0_error/
meta.rs

1use std::{fmt, sync::OnceLock};
2
3/// Wrapper around `std::panic::Location` used for display in reports.
4#[derive(derive_more::Debug, derive_more::Display, Clone, Copy)]
5#[debug("{_0:?}")]
6pub struct Location(&'static std::panic::Location<'static>);
7
8/// Captured metadata for an error creation site.
9///
10/// Currently this only contains the call-site [`Location`].
11#[derive(Clone)]
12pub struct Meta {
13    location: Option<Location>,
14}
15
16impl fmt::Display for Meta {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        if let Some(location) = self.location.as_ref() {
19            write!(f, "{location}")?;
20        }
21        Ok(())
22    }
23}
24
25impl fmt::Debug for Meta {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        write!(f, "Meta")?;
28        if let Some(location) = self.location.as_ref() {
29            write!(f, "({location})")?;
30        }
31        Ok(())
32    }
33}
34
35/// Creates new [`Meta`] capturing the caller location.
36#[track_caller]
37pub fn meta() -> Meta {
38    Meta::default()
39}
40
41impl Default for Meta {
42    #[track_caller]
43    fn default() -> Self {
44        Self {
45            location: location(),
46        }
47    }
48}
49
50impl Meta {
51    #[track_caller]
52    /// Creates new [`Meta`] capturing the caller location.
53    pub fn new() -> Self {
54        Self::default()
55    }
56
57    /// Returns the captured call-site location.
58    pub fn location(&self) -> Option<&Location> {
59        self.location.as_ref()
60    }
61}
62
63#[cfg(test)]
64static BACKTRACE_ENABLED: OnceLock<std::sync::RwLock<bool>> = OnceLock::new();
65
66#[cfg(not(test))]
67static BACKTRACE_ENABLED: OnceLock<bool> = OnceLock::new();
68
69#[doc(hidden)]
70pub fn backtrace_enabled() -> bool {
71    let from_env = || {
72        matches!(
73            std::env::var("RUST_BACKTRACE").as_deref(),
74            Ok("1") | Ok("full")
75        ) || matches!(std::env::var("RUST_ERROR_LOCATION").as_deref(), Ok("1"))
76    };
77    #[cfg(test)]
78    return *(BACKTRACE_ENABLED
79        .get_or_init(|| std::sync::RwLock::new(from_env()))
80        .read()
81        .unwrap());
82
83    #[cfg(not(test))]
84    return *(BACKTRACE_ENABLED.get_or_init(from_env));
85}
86
87#[doc(hidden)]
88#[cfg(test)]
89pub fn set_backtrace_enabled(value: bool) {
90    let mut inner = BACKTRACE_ENABLED
91        .get_or_init(Default::default)
92        .write()
93        .unwrap();
94    *inner = value;
95}
96
97#[track_caller]
98fn location() -> Option<Location> {
99    if backtrace_enabled() {
100        Some(Location(std::panic::Location::caller()))
101    } else {
102        None
103    }
104}