#![warn(missing_docs)]
#![cfg_attr(
feature = "unstable-error-generic-member-access",
feature(error_generic_member_access)
)]
#![cfg_attr(
feature = "unstable-error-generic-member-access",
doc(test(attr(feature(error_generic_member_access))))
)]
#![cfg_attr(not(feature = "std"), no_std)]
pub extern crate alloc;
#[cfg(feature = "std")]
mod backtrace;
mod chain;
mod diagnostic;
#[cfg(feature = "serde")]
pub mod erased;
#[cfg(feature = "extras")]
pub mod extras;
#[cfg(feature = "std")]
mod marker;
#[cfg(not(feature = "std"))]
mod nostd_stubs;
mod spantrace;
#[cfg(feature = "test-utils")]
pub mod test_utils;
mod traits;
mod welp;
use alloc::borrow::Cow;
use alloc::string::String;
use core::borrow::Borrow;
use core::ops::Deref;
#[cfg(feature = "std")]
pub use backtrace::{
Backtrace, RustBacktrace, clear_rust_backtrace_override, rust_backtrace, rust_panic_backtrace,
set_rust_backtrace_override, with_rust_backtrace_override,
};
pub use chain::{Chain, ErrorChainExt};
pub use diagnostic::Diagnostic;
#[cfg(not(feature = "std"))]
pub use nostd_stubs::Backtrace;
pub use spantrace::{OptionalSpanTrace, SpanTrace, SpanTraceStatus};
#[cfg(feature = "tracing")]
pub mod tracing;
pub use traits::*;
pub use welp::{Welp, WelpOptionExt, WelpResultExt};
#[doc(hidden)]
pub mod __private {
#[cfg(feature = "std")]
pub use crate::backtrace::CORE_SRC_PATH;
pub struct CaptureProbe<'a, T: ?Sized>(pub &'a T);
pub trait CaptureFromExt {
fn resolve<C: crate::Capturable>(&self) -> C;
}
impl<T: crate::Diagnostic> CaptureFromExt for CaptureProbe<'_, T> {
#[inline]
#[track_caller]
fn resolve<C: crate::Capturable>(&self) -> C {
C::capture_or_extract(self.0)
}
}
pub trait CaptureFromFallback {
fn resolve<C: crate::Capturable>(&self) -> C;
}
impl<T: ?Sized> CaptureFromFallback for &CaptureProbe<'_, T> {
#[inline]
#[track_caller]
fn resolve<C: crate::Capturable>(&self) -> C {
C::capture()
}
}
pub struct DiagProbe<'a, T: ?Sized>(pub &'a T);
pub trait DiagForwardExt<'a> {
fn fwd_code(&self) -> Option<crate::ErrorCode>;
fn fwd_help(&self) -> Option<crate::HelpText>;
fn fwd_backtrace(&self) -> Option<&'a crate::Backtrace>;
fn fwd_spantrace(&self) -> Option<&'a crate::SpanTrace>;
fn fwd_location(&self) -> Option<&'static core::panic::Location<'static>>;
fn fwd_exit_code(&self) -> Option<core::num::NonZeroU8>;
}
impl<'a, T: crate::Diagnostic + ?Sized> DiagForwardExt<'a> for DiagProbe<'a, T> {
#[inline]
fn fwd_code(&self) -> Option<crate::ErrorCode> {
self.0.oopsie_error_code()
}
#[inline]
fn fwd_help(&self) -> Option<crate::HelpText> {
self.0.oopsie_help_text()
}
#[inline]
fn fwd_backtrace(&self) -> Option<&'a crate::Backtrace> {
self.0.oopsie_backtrace()
}
#[inline]
fn fwd_spantrace(&self) -> Option<&'a crate::SpanTrace> {
self.0.oopsie_spantrace()
}
#[inline]
fn fwd_location(&self) -> Option<&'static core::panic::Location<'static>> {
self.0.oopsie_location()
}
#[inline]
fn fwd_exit_code(&self) -> Option<core::num::NonZeroU8> {
self.0.oopsie_exit_code()
}
}
pub trait DiagForwardFallback<'a> {
#[inline]
fn fwd_code(&self) -> Option<crate::ErrorCode> {
None
}
#[inline]
fn fwd_help(&self) -> Option<crate::HelpText> {
None
}
#[inline]
fn fwd_backtrace(&self) -> Option<&'a crate::Backtrace> {
None
}
#[inline]
fn fwd_spantrace(&self) -> Option<&'a crate::SpanTrace> {
None
}
#[inline]
fn fwd_location(&self) -> Option<&'static core::panic::Location<'static>> {
None
}
#[inline]
fn fwd_exit_code(&self) -> Option<core::num::NonZeroU8> {
None
}
}
impl<'a, T: ?Sized> DiagForwardFallback<'a> for &DiagProbe<'a, T> {}
#[inline]
#[must_use]
pub fn source_trace<'a, T: 'static>(
source: &'a (dyn core::error::Error + 'static),
) -> Option<&'a T> {
#[cfg(feature = "unstable-error-generic-member-access")]
{
core::error::request_ref::<T>(source)
}
#[cfg(not(feature = "unstable-error-generic-member-access"))]
{
let _ = source;
None
}
}
#[inline]
#[must_use]
pub fn source_backtrace<'a>(
source: &'a (dyn core::error::Error + 'static),
) -> Option<&'a crate::Backtrace> {
source_trace::<crate::Backtrace>(source).filter(|bt| bt.is_captured())
}
#[inline]
#[must_use]
pub fn source_spantrace<'a>(
source: &'a (dyn core::error::Error + 'static),
) -> Option<&'a crate::SpanTrace> {
source_trace::<crate::SpanTrace>(source).filter(|st| st.is_captured())
}
#[cfg(feature = "std")]
pub use crate::marker::{TraceMarker, restore_marker, set_marker};
#[cfg(not(feature = "std"))]
pub use crate::nostd_stubs::{TraceMarker, restore_marker, set_marker};
#[inline]
#[must_use]
pub fn source_exit_code(
source: &(dyn core::error::Error + 'static),
) -> Option<core::num::NonZeroU8> {
#[cfg(feature = "unstable-error-generic-member-access")]
{
core::error::request_value::<core::num::NonZeroU8>(source)
}
#[cfg(not(feature = "unstable-error-generic-member-access"))]
{
let _ = source;
None
}
}
#[inline]
#[must_use]
pub const fn nonzero_u8_unchecked(value: u8) -> core::num::NonZeroU8 {
match core::num::NonZeroU8::new(value) {
Some(n) => n,
None => panic!("exit code must be non-zero"),
}
}
pub struct ConstStr<const N: usize> {
buf: [u8; N],
len: usize,
}
impl<const N: usize> ConstStr<N> {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
buf: [0; N],
len: 0,
}
}
#[inline]
#[must_use]
pub const fn str(mut self, s: &str) -> Self {
let b = s.as_bytes();
let mut i = 0;
while i < b.len() {
self.buf[self.len] = b[i];
self.len += 1;
i += 1;
}
self
}
#[inline]
#[must_use]
pub const fn usize(mut self, mut n: usize) -> Self {
let mut digits = [0u8; 20];
let mut count = if n == 0 {
digits[0] = b'0';
1
} else {
0
};
while n > 0 {
digits[count] = b'0' + (n % 10) as u8;
n /= 10;
count += 1;
}
while count > 0 {
count -= 1;
self.buf[self.len] = digits[count];
self.len += 1;
}
self
}
#[inline]
#[must_use]
pub const fn as_str(&self) -> &str {
match core::str::from_utf8(self.buf.split_at(self.len).0) {
Ok(s) => s,
Err(_) => panic!("ConstStr: built a non-utf8 message"),
}
}
}
impl<const N: usize> Default for ConstStr<N> {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "chrono")]
pub use chrono;
#[cfg(feature = "std")]
pub use std::time::SystemTime;
pub use alloc;
}
macro_rules! impl_string_newtypes {
($($(#[$meta:meta])* $ident:ident,)* $(,)?) => { $(
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)]
$(#[$meta])*
pub struct $ident(Cow<'static, str>);
impl $ident {
#[doc = concat!("Build a new `", stringify!($ident), "` from a `&'static str`.")]
#[must_use]
#[inline]
pub const fn from_static(s: &'static str) -> Self {
Self(Cow::Borrowed(s))
}
#[doc = concat!("Build a new `", stringify!($ident), "` from a `String`.")]
#[must_use]
#[inline]
pub const fn from_string(s: String) -> Self {
Self(Cow::Owned(s))
}
#[must_use]
#[inline]
pub const fn as_str(&self) -> &str {
match &self.0 {
Cow::Borrowed(s) => s,
Cow::Owned(s) => s.as_str(),
}
}
#[doc = concat!("Consume the `", stringify!($ident), "` and return the underlying `Cow`.")]
#[must_use]
#[inline]
pub fn into_inner(self) -> Cow<'static, str> {
self.0
}
}
impl Deref for $ident {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl Borrow<str> for $ident {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl From<&'static str> for $ident {
#[inline]
fn from(s: &'static str) -> Self {
Self::from_static(s)
}
}
impl From<String> for $ident {
#[inline]
fn from(s: String) -> Self {
Self::from_string(s)
}
}
impl core::fmt::Display for $ident {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
)* };
}
impl_string_newtypes!(
ErrorCode,
HelpText,
);
#[cfg(test)]
mod const_str_tests {
use crate::__private::ConstStr;
#[test]
fn builds_interleaved_string() {
let s = ConstStr::<64>::new()
.str("`E` is ")
.usize(80)
.str(" bytes, must be ≤ 64");
assert_eq!(s.as_str(), "`E` is 80 bytes, must be ≤ 64");
}
#[test]
fn formats_zero_and_large() {
assert_eq!(ConstStr::<8>::new().as_str(), "");
assert_eq!(ConstStr::<32>::new().usize(0).as_str(), "0");
assert_eq!(
ConstStr::<32>::new().usize(18446744073709551615).as_str(),
"18446744073709551615"
);
}
#[test]
fn usable_in_const_context() {
const M: &str = {
const C: ConstStr<16> = ConstStr::new().str("n=").usize(42);
C.as_str()
};
assert_eq!(M, "n=42");
}
}