rune/shared/
mod.rs

1mod assert_send;
2mod caller;
3mod consts;
4mod fixed_vec;
5mod gen;
6
7pub(crate) use self::assert_send::AssertSend;
8pub(crate) use self::caller::Caller;
9pub(crate) use self::consts::Consts;
10pub(crate) use self::fixed_vec::FixedVec;
11pub(crate) use self::gen::Gen;
12
13/// Test whether current assertions model should panic.
14#[cfg(all(debug_assertions, feature = "std"))]
15mod r#impl {
16    use core::fmt::{self, Write};
17    use core::sync::atomic::{AtomicU8, Ordering};
18
19    use std::env;
20    use std::thread::panicking;
21
22    pub(crate) use std::backtrace::Backtrace;
23
24    pub(crate) enum RuneAssert {
25        /// Assert should panic.
26        Panic,
27        /// Assert should trace.
28        Trace,
29        /// Assert should log an error.
30        Error,
31    }
32
33    impl RuneAssert {
34        /// Test if the assert is a panic.
35        #[allow(unused)]
36        pub(crate) fn is_panic(&self) -> bool {
37            matches!(self, Self::Panic)
38        }
39
40        /// Test if the assert is a trace.
41        #[allow(unused)]
42        pub(crate) fn is_trace(&self) -> bool {
43            matches!(self, Self::Trace)
44        }
45    }
46
47    const VAR: &str = "RUNE_ASSERT";
48
49    static ENABLED: AtomicU8 = AtomicU8::new(0);
50
51    pub(crate) fn rune_assert() -> RuneAssert {
52        let mut value = ENABLED.load(Ordering::Relaxed);
53
54        if value == 0 {
55            value = match env::var(VAR).as_deref() {
56                Ok("panic") => 1,
57                Ok("trace") => 2,
58                _ => 3,
59            };
60
61            ENABLED.store(value, Ordering::Relaxed);
62        }
63
64        match value {
65            1 if !panicking() => RuneAssert::Panic,
66            2 if !panicking() => RuneAssert::Trace,
67            _ => RuneAssert::Error,
68        }
69    }
70
71    pub(crate) struct CaptureAt {
72        at: &'static str,
73        done: usize,
74        string: rust_alloc::string::String,
75    }
76
77    impl CaptureAt {
78        pub(crate) fn new(at: &'static str) -> Self {
79            Self {
80                at,
81                done: 0,
82                string: rust_alloc::string::String::default(),
83            }
84        }
85
86        pub(crate) fn as_str(&self) -> Option<&str> {
87            if self.done > 0 {
88                Some(&self.string[self.done..])
89            } else {
90                None
91            }
92        }
93    }
94
95    impl Write for CaptureAt {
96        #[inline]
97        fn write_str(&mut self, mut s: &str) -> fmt::Result {
98            if self.done > 0 {
99                return Ok(());
100            }
101
102            while let Some(n) = s.find('\n') {
103                self.string.push_str(&s[..n]);
104
105                if let Some(n) = self.string.find("at ") {
106                    let at = &self.string[n + 3..];
107
108                    if at.contains(self.at) {
109                        self.done = n + 3;
110                        return Ok(());
111                    }
112                }
113
114                self.string.clear();
115                s = &s[n + 1..];
116            }
117
118            self.string.push_str(s);
119            Ok(())
120        }
121    }
122
123    macro_rules! _rune_diagnose {
124        ($($tt:tt)*) => {
125            if $crate::shared::rune_assert().is_panic() {
126                panic!($($tt)*);
127            } else {
128                tracing::trace!($($tt)*);
129            }
130        };
131    }
132
133    macro_rules! _rune_trace {
134        ($at:expr, $tok:expr) => {{
135            if $crate::shared::rune_assert().is_trace() {
136                use std::backtrace::Backtrace;
137                use std::fmt::Write as _;
138
139                let bt = Backtrace::force_capture();
140                let mut at = $crate::shared::CaptureAt::new($at);
141
142                write!(at, "{bt}").with_span($tok.span)?;
143
144                if let Some(_line) = at.as_str() {
145                    tracing::trace!("{_line}: {:?}", $tok);
146                }
147            }
148        }};
149    }
150
151    /// A macro for logging or panicking based on the current assertions model.
152    ///
153    /// The assertion model can be changed from logging to panicking by setting
154    /// the `RUNE_ASSERT=panic` environment.
155    #[doc(inline)]
156    pub(crate) use _rune_diagnose as rune_diagnose;
157
158    /// A macro for tracing a specific call site.
159    ///
160    /// Tracing is enabled if `RUNE_ASSERT=trace` is set.
161    #[doc(inline)]
162    pub(crate) use _rune_trace as rune_trace;
163}
164
165#[cfg(not(all(debug_assertions, feature = "std")))]
166mod r#impl {
167    use core::fmt;
168
169    macro_rules! _rune_diagnose {
170        ($($tt:tt)*) => {
171            tracing::trace!($($tt)*);
172        };
173    }
174
175    macro_rules! _rune_trace {
176        ($at:expr, $tok:expr) => {{
177            _ = $at;
178            _ = $tok;
179        }};
180    }
181
182    pub(crate) struct Backtrace;
183
184    impl Backtrace {
185        #[inline(always)]
186        pub(crate) fn capture() -> Self {
187            Self
188        }
189    }
190
191    impl fmt::Display for Backtrace {
192        #[inline(always)]
193        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194            write!(
195                f,
196                "backtrace not available, missing cfg(all(debug_assertions, feature = \"std\"))"
197            )
198        }
199    }
200
201    /// A macro for logging or panicking based on the current assertions model.
202    ///
203    /// The assertion model can be changed from logging to panicking by setting
204    /// the `RUNE_ASSERT=panic` environment.
205    #[doc(inline)]
206    pub(crate) use _rune_diagnose as rune_diagnose;
207
208    /// A macro for tracing a specific call site.
209    ///
210    /// Tracing is enabled if `RUNE_ASSERT=trace` is set.
211    #[doc(inline)]
212    pub(crate) use _rune_trace as rune_trace;
213}
214
215pub(crate) use self::r#impl::*;