1#![warn(missing_docs)]
4#![cfg_attr(
5 feature = "unstable-error-generic-member-access",
6 feature(error_generic_member_access)
7)]
8#![cfg_attr(
12 feature = "unstable-error-generic-member-access",
13 doc(test(attr(feature(error_generic_member_access))))
14)]
15
16mod backtrace;
17mod chain;
18mod diagnostic;
19#[cfg(feature = "serde")]
20pub mod erased;
21#[cfg(feature = "extras")]
22pub mod extras;
23mod marker;
24mod spantrace;
25#[cfg(feature = "test-utils")]
26pub mod test_utils;
27mod traits;
28mod welp;
29
30use std::borrow::{Borrow, Cow};
31use std::ops::Deref;
32
33pub use backtrace::{
34 Backtrace, RustBacktrace, clear_rust_backtrace_override, rust_backtrace, rust_panic_backtrace,
35 set_rust_backtrace_override, with_rust_backtrace_override,
36};
37pub use chain::{Chain, ErrorChainExt};
38pub use diagnostic::Diagnostic;
39pub use spantrace::{OptionalSpanTrace, SpanTrace};
40#[cfg(feature = "tracing")]
41pub mod tracing;
42
43pub use traits::*;
44pub use welp::{Welp, WelpOptionExt, WelpResultExt};
45#[doc(hidden)]
47pub mod __private {
48 pub use crate::backtrace::CORE_SRC_PATH;
49 pub struct CaptureProbe<'a, T: ?Sized>(pub &'a T);
56
57 pub trait CaptureFromExt {
59 fn resolve<C: crate::Capturable>(&self) -> C;
60 }
61
62 impl<T: crate::Diagnostic> CaptureFromExt for CaptureProbe<'_, T> {
63 #[inline]
64 #[track_caller]
65 fn resolve<C: crate::Capturable>(&self) -> C {
66 C::capture_or_extract(self.0)
67 }
68 }
69
70 pub trait CaptureFromFallback {
72 fn resolve<C: crate::Capturable>(&self) -> C;
73 }
74
75 impl<T: ?Sized> CaptureFromFallback for &CaptureProbe<'_, T> {
76 #[inline]
77 #[track_caller]
78 fn resolve<C: crate::Capturable>(&self) -> C {
79 C::capture()
80 }
81 }
82
83 pub struct DiagProbe<'a, T: ?Sized>(pub &'a T);
94
95 pub trait DiagForwardExt<'a> {
97 fn fwd_code(&self) -> Option<crate::ErrorCode>;
98 fn fwd_help(&self) -> Option<crate::HelpText>;
99 fn fwd_backtrace(&self) -> Option<&'a crate::Backtrace>;
100 fn fwd_spantrace(&self) -> Option<&'a crate::SpanTrace>;
101 fn fwd_location(&self) -> Option<&'static std::panic::Location<'static>>;
102 fn fwd_exit_code(&self) -> Option<core::num::NonZeroU8>;
103 }
104
105 impl<'a, T: crate::Diagnostic + ?Sized> DiagForwardExt<'a> for DiagProbe<'a, T> {
106 #[inline]
107 fn fwd_code(&self) -> Option<crate::ErrorCode> {
108 self.0.oopsie_error_code()
109 }
110 #[inline]
111 fn fwd_help(&self) -> Option<crate::HelpText> {
112 self.0.oopsie_help_text()
113 }
114 #[inline]
115 fn fwd_backtrace(&self) -> Option<&'a crate::Backtrace> {
116 self.0.oopsie_backtrace()
117 }
118 #[inline]
119 fn fwd_spantrace(&self) -> Option<&'a crate::SpanTrace> {
120 self.0.oopsie_spantrace()
121 }
122 #[inline]
123 fn fwd_location(&self) -> Option<&'static std::panic::Location<'static>> {
124 self.0.oopsie_location()
125 }
126 #[inline]
127 fn fwd_exit_code(&self) -> Option<core::num::NonZeroU8> {
128 self.0.oopsie_exit_code()
129 }
130 }
131
132 pub trait DiagForwardFallback<'a> {
134 #[inline]
135 fn fwd_code(&self) -> Option<crate::ErrorCode> {
136 None
137 }
138 #[inline]
139 fn fwd_help(&self) -> Option<crate::HelpText> {
140 None
141 }
142 #[inline]
143 fn fwd_backtrace(&self) -> Option<&'a crate::Backtrace> {
144 None
145 }
146 #[inline]
147 fn fwd_spantrace(&self) -> Option<&'a crate::SpanTrace> {
148 None
149 }
150 #[inline]
151 fn fwd_location(&self) -> Option<&'static std::panic::Location<'static>> {
152 None
153 }
154 #[inline]
155 fn fwd_exit_code(&self) -> Option<core::num::NonZeroU8> {
156 None
157 }
158 }
159
160 impl<'a, T: ?Sized> DiagForwardFallback<'a> for &DiagProbe<'a, T> {}
161
162 #[inline]
175 #[must_use]
176 pub fn source_trace<'a, T: 'static>(
177 source: &'a (dyn std::error::Error + 'static),
178 ) -> Option<&'a T> {
179 #[cfg(feature = "unstable-error-generic-member-access")]
180 {
181 core::error::request_ref::<T>(source)
182 }
183 #[cfg(not(feature = "unstable-error-generic-member-access"))]
184 {
185 let _ = source;
186 None
187 }
188 }
189
190 #[inline]
194 #[must_use]
195 pub fn source_backtrace<'a>(
196 source: &'a (dyn std::error::Error + 'static),
197 ) -> Option<&'a crate::Backtrace> {
198 source_trace::<crate::Backtrace>(source).filter(|bt| bt.is_captured())
199 }
200
201 #[inline]
203 #[must_use]
204 pub fn source_spantrace<'a>(
205 source: &'a (dyn std::error::Error + 'static),
206 ) -> Option<&'a crate::SpanTrace> {
207 source_trace::<crate::SpanTrace>(source).filter(|st| st.is_captured())
208 }
209
210 pub use crate::marker::{TraceMarker, restore_marker, set_marker};
211
212 #[inline]
216 #[must_use]
217 pub fn source_exit_code(
218 source: &(dyn std::error::Error + 'static),
219 ) -> Option<core::num::NonZeroU8> {
220 #[cfg(feature = "unstable-error-generic-member-access")]
221 {
222 core::error::request_value::<core::num::NonZeroU8>(source)
223 }
224 #[cfg(not(feature = "unstable-error-generic-member-access"))]
225 {
226 let _ = source;
227 None
228 }
229 }
230
231 #[inline]
235 #[must_use]
236 pub const fn nonzero_u8_unchecked(value: u8) -> core::num::NonZeroU8 {
237 match core::num::NonZeroU8::new(value) {
238 Some(n) => n,
239 None => panic!("exit code must be non-zero"),
240 }
241 }
242
243 pub struct ConstStr<const N: usize> {
249 buf: [u8; N],
250 len: usize,
251 }
252
253 impl<const N: usize> ConstStr<N> {
254 #[inline]
255 #[must_use]
256 pub const fn new() -> Self {
257 Self {
258 buf: [0; N],
259 len: 0,
260 }
261 }
262
263 #[inline]
265 #[must_use]
266 pub const fn str(mut self, s: &str) -> Self {
267 let b = s.as_bytes();
268 let mut i = 0;
269 while i < b.len() {
270 self.buf[self.len] = b[i];
271 self.len += 1;
272 i += 1;
273 }
274 self
275 }
276
277 #[inline]
279 #[must_use]
280 pub const fn usize(mut self, mut n: usize) -> Self {
281 let mut digits = [0u8; 20];
282 let mut count = if n == 0 {
283 digits[0] = b'0';
284 1
285 } else {
286 0
287 };
288 while n > 0 {
289 digits[count] = b'0' + (n % 10) as u8;
290 n /= 10;
291 count += 1;
292 }
293 while count > 0 {
294 count -= 1;
295 self.buf[self.len] = digits[count];
296 self.len += 1;
297 }
298 self
299 }
300
301 #[inline]
304 #[must_use]
305 pub const fn as_str(&self) -> &str {
306 match core::str::from_utf8(self.buf.split_at(self.len).0) {
307 Ok(s) => s,
308 Err(_) => panic!("ConstStr: built a non-utf8 message"),
309 }
310 }
311 }
312
313 impl<const N: usize> Default for ConstStr<N> {
314 fn default() -> Self {
315 Self::new()
316 }
317 }
318
319 #[cfg(feature = "chrono")]
320 pub use chrono;
321}
322
323macro_rules! impl_string_newtypes {
324 ($($(#[$meta:meta])* $ident:ident,)* $(,)?) => { $(
325 #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
326 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
327 #[cfg_attr(feature = "serde", serde(transparent))]
328 #[repr(transparent)]
329 $(#[$meta])*
330 pub struct $ident(Cow<'static, str>);
331
332 impl $ident {
333 #[doc = concat!("Build a new `", stringify!($ident), "` from a `&'static str`.")]
334 #[must_use]
335 #[inline]
336 pub const fn from_static(s: &'static str) -> Self {
337 Self(Cow::Borrowed(s))
338 }
339
340 #[doc = concat!("Build a new `", stringify!($ident), "` from a `String`.")]
341 #[must_use]
342 #[inline]
343 pub const fn from_string(s: String) -> Self {
344 Self(Cow::Owned(s))
345 }
346
347 #[must_use]
349 #[inline]
350 pub const fn as_str(&self) -> &str {
351 match &self.0 {
352 Cow::Borrowed(s) => s,
353 Cow::Owned(s) => s.as_str(),
354 }
355 }
356
357 #[doc = concat!("Consume the `", stringify!($ident), "` and return the underlying `Cow`.")]
358 #[must_use]
359 #[inline]
360 pub fn into_inner(self) -> Cow<'static, str> {
361 self.0
362 }
363 }
364
365 impl Deref for $ident {
366 type Target = str;
367
368 #[inline]
369 fn deref(&self) -> &Self::Target {
370 self.as_str()
371 }
372 }
373
374 impl Borrow<str> for $ident {
375 #[inline]
376 fn borrow(&self) -> &str {
377 self.as_str()
378 }
379 }
380
381 impl From<&'static str> for $ident {
382 #[inline]
383 fn from(s: &'static str) -> Self {
384 Self::from_static(s)
385 }
386 }
387
388 impl From<String> for $ident {
389 #[inline]
390 fn from(s: String) -> Self {
391 Self::from_string(s)
392 }
393 }
394
395 impl std::fmt::Display for $ident {
396 #[inline]
397 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
398 self.0.fmt(f)
399 }
400 }
401
402 )* };
403}
404
405impl_string_newtypes!(
406 ErrorCode,
408 HelpText,
410);
411
412#[cfg(test)]
413mod const_str_tests {
414 use crate::__private::ConstStr;
415
416 #[test]
417 fn builds_interleaved_string() {
418 let s = ConstStr::<64>::new()
419 .str("`E` is ")
420 .usize(80)
421 .str(" bytes, must be ≤ 64");
422 assert_eq!(s.as_str(), "`E` is 80 bytes, must be ≤ 64");
423 }
424
425 #[test]
426 fn formats_zero_and_large() {
427 assert_eq!(ConstStr::<8>::new().as_str(), "");
428 assert_eq!(ConstStr::<32>::new().usize(0).as_str(), "0");
429 assert_eq!(
430 ConstStr::<32>::new().usize(18446744073709551615).as_str(),
431 "18446744073709551615"
432 );
433 }
434
435 #[test]
436 fn usable_in_const_context() {
437 const M: &str = {
438 const C: ConstStr<16> = ConstStr::new().str("n=").usize(42);
439 C.as_str()
440 };
441 assert_eq!(M, "n=42");
442 }
443}