n0_error/
meta.rs

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