1#![warn(missing_docs)]
11#![cfg_attr(
12 feature = "unstable-error-generic-member-access",
13 feature(error_generic_member_access)
14)]
15#![cfg_attr(
19 feature = "unstable-error-generic-member-access",
20 doc(test(attr(feature(error_generic_member_access))))
21)]
22#![cfg_attr(not(feature = "std"), no_std)]
23
24pub extern crate alloc;
25
26#[cfg(feature = "std")]
27mod backtrace;
28mod chain;
29mod diagnostic;
30#[cfg(feature = "serde")]
31pub mod erased;
32#[cfg(feature = "extras")]
33pub mod extras;
34#[cfg(feature = "std")]
35mod marker;
36#[cfg(not(feature = "std"))]
37mod nostd_stubs;
38mod spantrace;
39#[cfg(feature = "test-utils")]
40pub mod test_utils;
41mod traits;
42mod welp;
43
44use alloc::borrow::Cow;
45use alloc::string::String;
46use core::borrow::Borrow;
47use core::ops::Deref;
48
49#[cfg(feature = "std")]
50pub use backtrace::{
51 Backtrace, RustBacktrace, clear_rust_backtrace_override, rust_backtrace, rust_panic_backtrace,
52 set_rust_backtrace_override, with_rust_backtrace_override,
53};
54pub use chain::{Chain, ErrorChainExt};
55pub use diagnostic::Diagnostic;
56#[cfg(not(feature = "std"))]
57pub use nostd_stubs::Backtrace;
58pub use spantrace::{OptionalSpanTrace, SpanTrace, SpanTraceStatus};
59#[cfg(feature = "tracing")]
60pub mod tracing;
61
62pub use traits::*;
63pub use welp::{Welp, WelpOptionExt, WelpResultExt};
64#[doc(hidden)]
66pub mod __private {
67 #[cfg(feature = "std")]
68 pub use crate::backtrace::CORE_SRC_PATH;
69 pub struct CaptureProbe<'a, T: ?Sized>(pub &'a T);
76
77 pub trait CaptureFromExt {
79 fn resolve<C: crate::Capturable>(&self) -> C;
80 }
81
82 impl<T: crate::Diagnostic> CaptureFromExt for CaptureProbe<'_, T> {
83 #[inline]
84 #[track_caller]
85 fn resolve<C: crate::Capturable>(&self) -> C {
86 C::capture_or_extract(self.0)
87 }
88 }
89
90 pub trait CaptureFromFallback {
92 fn resolve<C: crate::Capturable>(&self) -> C;
93 }
94
95 impl<T: ?Sized> CaptureFromFallback for &CaptureProbe<'_, T> {
96 #[inline]
97 #[track_caller]
98 fn resolve<C: crate::Capturable>(&self) -> C {
99 C::capture()
100 }
101 }
102
103 pub struct DiagProbe<'a, T: ?Sized>(pub &'a T);
114
115 pub trait DiagForwardExt<'a> {
117 fn fwd_code(&self) -> Option<crate::ErrorCode>;
118 fn fwd_help(&self) -> Option<crate::HelpText>;
119 fn fwd_backtrace(&self) -> Option<&'a crate::Backtrace>;
120 fn fwd_spantrace(&self) -> Option<&'a crate::SpanTrace>;
121 fn fwd_location(&self) -> Option<&'static core::panic::Location<'static>>;
122 fn fwd_exit_code(&self) -> Option<core::num::NonZeroU8>;
123 }
124
125 impl<'a, T: crate::Diagnostic + ?Sized> DiagForwardExt<'a> for DiagProbe<'a, T> {
126 #[inline]
127 fn fwd_code(&self) -> Option<crate::ErrorCode> {
128 self.0.oopsie_error_code()
129 }
130 #[inline]
131 fn fwd_help(&self) -> Option<crate::HelpText> {
132 self.0.oopsie_help_text()
133 }
134 #[inline]
135 fn fwd_backtrace(&self) -> Option<&'a crate::Backtrace> {
136 self.0.oopsie_backtrace()
137 }
138 #[inline]
139 fn fwd_spantrace(&self) -> Option<&'a crate::SpanTrace> {
140 self.0.oopsie_spantrace()
141 }
142 #[inline]
143 fn fwd_location(&self) -> Option<&'static core::panic::Location<'static>> {
144 self.0.oopsie_location()
145 }
146 #[inline]
147 fn fwd_exit_code(&self) -> Option<core::num::NonZeroU8> {
148 self.0.oopsie_exit_code()
149 }
150 }
151
152 pub trait DiagForwardFallback<'a> {
154 #[inline]
155 fn fwd_code(&self) -> Option<crate::ErrorCode> {
156 None
157 }
158 #[inline]
159 fn fwd_help(&self) -> Option<crate::HelpText> {
160 None
161 }
162 #[inline]
163 fn fwd_backtrace(&self) -> Option<&'a crate::Backtrace> {
164 None
165 }
166 #[inline]
167 fn fwd_spantrace(&self) -> Option<&'a crate::SpanTrace> {
168 None
169 }
170 #[inline]
171 fn fwd_location(&self) -> Option<&'static core::panic::Location<'static>> {
172 None
173 }
174 #[inline]
175 fn fwd_exit_code(&self) -> Option<core::num::NonZeroU8> {
176 None
177 }
178 }
179
180 impl<'a, T: ?Sized> DiagForwardFallback<'a> for &DiagProbe<'a, T> {}
181
182 #[inline]
195 #[must_use]
196 pub fn source_trace<'a, T: 'static>(
197 source: &'a (dyn core::error::Error + 'static),
198 ) -> Option<&'a T> {
199 #[cfg(feature = "unstable-error-generic-member-access")]
200 {
201 core::error::request_ref::<T>(source)
202 }
203 #[cfg(not(feature = "unstable-error-generic-member-access"))]
204 {
205 let _ = source;
206 None
207 }
208 }
209
210 #[inline]
214 #[must_use]
215 pub fn source_backtrace<'a>(
216 source: &'a (dyn core::error::Error + 'static),
217 ) -> Option<&'a crate::Backtrace> {
218 source_trace::<crate::Backtrace>(source).filter(|bt| bt.is_captured())
219 }
220
221 #[inline]
223 #[must_use]
224 pub fn source_spantrace<'a>(
225 source: &'a (dyn core::error::Error + 'static),
226 ) -> Option<&'a crate::SpanTrace> {
227 source_trace::<crate::SpanTrace>(source).filter(|st| st.is_captured())
228 }
229
230 #[cfg(feature = "std")]
231 pub use crate::marker::{TraceMarker, restore_marker, set_marker};
232 #[cfg(not(feature = "std"))]
233 pub use crate::nostd_stubs::{TraceMarker, restore_marker, set_marker};
234
235 #[inline]
239 #[must_use]
240 pub fn source_exit_code(
241 source: &(dyn core::error::Error + 'static),
242 ) -> Option<core::num::NonZeroU8> {
243 #[cfg(feature = "unstable-error-generic-member-access")]
244 {
245 core::error::request_value::<core::num::NonZeroU8>(source)
246 }
247 #[cfg(not(feature = "unstable-error-generic-member-access"))]
248 {
249 let _ = source;
250 None
251 }
252 }
253
254 #[inline]
258 #[must_use]
259 pub const fn nonzero_u8_unchecked(value: u8) -> core::num::NonZeroU8 {
260 match core::num::NonZeroU8::new(value) {
261 Some(n) => n,
262 None => panic!("exit code must be non-zero"),
263 }
264 }
265
266 pub struct ConstStr<const N: usize> {
272 buf: [u8; N],
273 len: usize,
274 }
275
276 impl<const N: usize> ConstStr<N> {
277 #[inline]
278 #[must_use]
279 pub const fn new() -> Self {
280 Self {
281 buf: [0; N],
282 len: 0,
283 }
284 }
285
286 #[inline]
288 #[must_use]
289 pub const fn str(mut self, s: &str) -> Self {
290 let b = s.as_bytes();
291 let mut i = 0;
292 while i < b.len() {
293 self.buf[self.len] = b[i];
294 self.len += 1;
295 i += 1;
296 }
297 self
298 }
299
300 #[inline]
302 #[must_use]
303 pub const fn usize(mut self, mut n: usize) -> Self {
304 let mut digits = [0u8; 20];
305 let mut count = if n == 0 {
306 digits[0] = b'0';
307 1
308 } else {
309 0
310 };
311 while n > 0 {
312 digits[count] = b'0' + (n % 10) as u8;
313 n /= 10;
314 count += 1;
315 }
316 while count > 0 {
317 count -= 1;
318 self.buf[self.len] = digits[count];
319 self.len += 1;
320 }
321 self
322 }
323
324 #[inline]
327 #[must_use]
328 pub const fn as_str(&self) -> &str {
329 match core::str::from_utf8(self.buf.split_at(self.len).0) {
330 Ok(s) => s,
331 Err(_) => panic!("ConstStr: built a non-utf8 message"),
332 }
333 }
334 }
335
336 impl<const N: usize> Default for ConstStr<N> {
337 fn default() -> Self {
338 Self::new()
339 }
340 }
341
342 #[cfg(feature = "chrono")]
343 pub use chrono;
344
345 #[cfg(feature = "std")]
349 pub use std::time::SystemTime;
350
351 pub use alloc;
358}
359
360macro_rules! impl_string_newtypes {
361 ($($(#[$meta:meta])* $ident:ident,)* $(,)?) => { $(
362 #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
363 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
364 #[cfg_attr(feature = "serde", serde(transparent))]
365 #[repr(transparent)]
366 $(#[$meta])*
367 pub struct $ident(Cow<'static, str>);
368
369 impl $ident {
370 #[doc = concat!("Build a new `", stringify!($ident), "` from a `&'static str`.")]
371 #[must_use]
372 #[inline]
373 pub const fn from_static(s: &'static str) -> Self {
374 Self(Cow::Borrowed(s))
375 }
376
377 #[doc = concat!("Build a new `", stringify!($ident), "` from a `String`.")]
378 #[must_use]
379 #[inline]
380 pub const fn from_string(s: String) -> Self {
381 Self(Cow::Owned(s))
382 }
383
384 #[must_use]
386 #[inline]
387 pub const fn as_str(&self) -> &str {
388 match &self.0 {
389 Cow::Borrowed(s) => s,
390 Cow::Owned(s) => s.as_str(),
391 }
392 }
393
394 #[doc = concat!("Consume the `", stringify!($ident), "` and return the underlying `Cow`.")]
395 #[must_use]
396 #[inline]
397 pub fn into_inner(self) -> Cow<'static, str> {
398 self.0
399 }
400 }
401
402 impl Deref for $ident {
403 type Target = str;
404
405 #[inline]
406 fn deref(&self) -> &Self::Target {
407 self.as_str()
408 }
409 }
410
411 impl Borrow<str> for $ident {
412 #[inline]
413 fn borrow(&self) -> &str {
414 self.as_str()
415 }
416 }
417
418 impl From<&'static str> for $ident {
419 #[inline]
420 fn from(s: &'static str) -> Self {
421 Self::from_static(s)
422 }
423 }
424
425 impl From<String> for $ident {
426 #[inline]
427 fn from(s: String) -> Self {
428 Self::from_string(s)
429 }
430 }
431
432 impl core::fmt::Display for $ident {
433 #[inline]
434 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
435 self.0.fmt(f)
436 }
437 }
438
439 )* };
440}
441
442impl_string_newtypes!(
443 ErrorCode,
445 HelpText,
447);
448
449#[cfg(test)]
450mod const_str_tests {
451 use crate::__private::ConstStr;
452
453 #[test]
454 fn builds_interleaved_string() {
455 let s = ConstStr::<64>::new()
456 .str("`E` is ")
457 .usize(80)
458 .str(" bytes, must be ≤ 64");
459 assert_eq!(s.as_str(), "`E` is 80 bytes, must be ≤ 64");
460 }
461
462 #[test]
463 fn formats_zero_and_large() {
464 assert_eq!(ConstStr::<8>::new().as_str(), "");
465 assert_eq!(ConstStr::<32>::new().usize(0).as_str(), "0");
466 assert_eq!(
467 ConstStr::<32>::new().usize(18446744073709551615).as_str(),
468 "18446744073709551615"
469 );
470 }
471
472 #[test]
473 fn usable_in_const_context() {
474 const M: &str = {
475 const C: ConstStr<16> = ConstStr::new().str("n=").usize(42);
476 C.as_str()
477 };
478 assert_eq!(M, "n=42");
479 }
480}