error_repr/
error.rs

1use crate::{
2    RawOsError,
3    kind::{ErrorKind, FromRawOsError},
4};
5
6#[cfg(feature = "alloc")]
7use alloc::boxed::Box;
8
9#[derive(Debug)]
10enum ErrorPayload {
11    Simple,
12    RawOsError(RawOsError),
13    Message(&'static str),
14    #[cfg(feature = "alloc")]
15    Error(Box<dyn core::error::Error + Send + Sync + 'static>),
16}
17
18/// Primary Error type from this crate.
19/// [`Error`] contains a kind and an optional payload, which may be an OS Error Code, a Custom Message, or (with an allocator available) a custom Error object.
20#[derive(Debug)]
21pub struct Error<K> {
22    kind: K,
23    payload: ErrorPayload,
24    #[cfg(feature = "error-track_caller")]
25    #[allow(dead_code)]
26    caller_location: &'static core::panic::Location<'static>, // intentionally unused field
27}
28
29impl<K: ErrorKind> core::fmt::Display for Error<K> {
30    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31        core::fmt::Display::fmt(&self.kind, f)?;
32        match &self.payload {
33            ErrorPayload::Simple => Ok(()),
34            ErrorPayload::RawOsError(os) => f.write_fmt(format_args!(" (raw os error {os})")),
35            ErrorPayload::Message(m) => f.write_fmt(format_args!(": {m}")),
36            #[cfg(feature = "alloc")]
37            ErrorPayload::Error(error) => f.write_fmt(format_args!(": {error}")),
38        }
39    }
40}
41
42impl<K> Error<K> {
43    #[cfg_attr(feature = "error-track_caller", track_caller)]
44    const fn internal_new(kind: K, payload: ErrorPayload) -> Self {
45        Self {
46            kind,
47            payload,
48            #[cfg(feature = "error-track_caller")]
49            caller_location: core::panic::Location::caller(),
50        }
51    }
52
53    /// Constructs a new error with a kind, but not payload
54    #[cfg_attr(feature = "error-track_caller", track_caller)]
55    pub const fn new_simple(kind: K) -> Self {
56        Self::internal_new(kind, ErrorPayload::Simple)
57    }
58
59    /// Constructs a new error with a kind and a custom message
60    #[cfg_attr(feature = "error-track_caller", track_caller)]
61    pub const fn new_with_message(kind: K, msg: &'static str) -> Self {
62        Self::internal_new(kind, ErrorPayload::Message(msg))
63    }
64
65    /// Constructs a new error with a kind and a custom payload
66    #[cfg_attr(feature = "error-track_caller", track_caller)]
67    #[cfg(feature = "alloc")]
68    pub fn new<E: Into<Box<dyn core::error::Error + Send + Sync + 'static>>>(
69        kind: K,
70        error: E,
71    ) -> Self {
72        Self::internal_new(kind, ErrorPayload::Error(error.into()))
73    }
74
75    /// Returns the error Kind of the Error
76    pub fn kind(&self) -> K
77    where
78        K: Copy,
79    {
80        self.kind
81    }
82
83    /// Returns the raw os error if the error was constructed with one (via. [`Error::from_raw_os_error`])
84    pub fn raw_os_error(&self) -> Option<RawOsError> {
85        match self.payload {
86            ErrorPayload::RawOsError(e) => Some(e),
87            _ => None,
88        }
89    }
90
91    /// Converts `self` into an boxed error if a custom payload is present.
92    /// [`Error::into_error`] will return [`Some`] if constructed with a custom payload ([`Error::new`], [`Error::other`], or [`Error::uncategorized`]),
93    /// or if constructed with a custom message ([`Error::new_with_message`], [`Error::other_with_message`], or [`Error::uncategorized_with_message`]), and [`None`] otherwise.
94    ///
95    /// Note that the latter case cannot be downcast.
96    #[cfg(feature = "alloc")]
97    pub fn into_error(self) -> Option<Box<dyn core::error::Error + Send + Sync + 'static>> {
98        match self.payload {
99            ErrorPayload::Error(payload) => Some(payload),
100            ErrorPayload::Message(m) => Some(m.into()),
101            _ => None,
102        }
103    }
104
105    /// Converts `self` into an boxed error if a custom payload is present.
106    /// [`Error::into_inner`] will return [`Some`] if constructed with a custom payload ([`Error::new`], [`Error::other`], or [`Error::uncategorized`]), and [`None`] otherwise.
107    #[cfg(feature = "alloc")]
108    pub fn into_inner(self) -> Option<Box<dyn core::error::Error + Send + Sync + 'static>> {
109        match self.payload {
110            ErrorPayload::Error(payload) => Some(payload),
111            _ => None,
112        }
113    }
114}
115
116impl<K: ErrorKind> Error<K> {
117    /// Constructs a new [`Error`] with no payload that indicates an other error.
118    #[cfg_attr(feature = "error-track_caller", track_caller)]
119    pub const fn other_simple() -> Self {
120        Self::internal_new(K::OTHER, ErrorPayload::Simple)
121    }
122
123    /// Constructs a new [`Error`] with a custom message that indicates an other error.
124    #[cfg_attr(feature = "error-track_caller", track_caller)]
125    pub const fn other_with_message(msg: &'static str) -> Self {
126        Self::internal_new(K::OTHER, ErrorPayload::Message(msg))
127    }
128
129    /// Constructs a new [`Error`] with a custom payload that indicates an other error.
130    #[cfg_attr(feature = "error-track_caller", track_caller)]
131    #[cfg(feature = "alloc")]
132    pub fn other<E: Into<Box<dyn core::error::Error + Send + Sync + 'static>>>(error: E) -> Self {
133        Self::internal_new(K::OTHER, ErrorPayload::Error(error.into()))
134    }
135
136    /// Constructs a new [`Error`] with no payload that indicates an uncategorized error.
137    #[cfg_attr(feature = "error-track_caller", track_caller)]
138    pub fn uncategorized_simple() -> Self {
139        Self::internal_new(K::uncategorized(), ErrorPayload::Simple)
140    }
141
142    /// Constructs a new [`Error`] with a custom message that indicates an uncategorized error.
143    #[cfg_attr(feature = "error-track_caller", track_caller)]
144    pub fn uncategorized_with_message(msg: &'static str) -> Self {
145        Self::internal_new(K::uncategorized(), ErrorPayload::Message(msg))
146    }
147
148    /// Constructs a new [`Error`] with a custom payload that indicates an uncategorized error.
149    #[cfg_attr(feature = "error-track_caller", track_caller)]
150    #[cfg(feature = "alloc")]
151    pub fn uncategorized<E: Into<Box<dyn core::error::Error + Send + Sync + 'static>>>(
152        error: E,
153    ) -> Self {
154        Self::internal_new(K::uncategorized(), ErrorPayload::Error(error.into()))
155    }
156}
157
158impl<K: FromRawOsError> Error<K> {
159    /// Constructs a new [`Error`] that contains an Error created from the OS
160    #[cfg_attr(feature = "error-track_caller", track_caller)]
161    pub fn from_raw_os_error(error: RawOsError) -> Self {
162        Self::internal_new(K::from_raw_os_error(error), ErrorPayload::RawOsError(error))
163    }
164}
165
166impl<K: ErrorKind> core::error::Error for Error<K> {}
167
168#[cfg(feature = "std")]
169impl<K: crate::kind::FromIoKind> From<std::io::ErrorKind> for Error<K> {
170    #[cfg_attr(feature = "error-track_caller", track_caller)]
171    fn from(kind: std::io::ErrorKind) -> Self {
172        Error::new_simple(K::from_io_error_kind(kind))
173    }
174}
175
176#[cfg(feature = "std")]
177impl<K: crate::kind::FromIoKind> From<std::io::Error> for Error<K> {
178    #[cfg_attr(feature = "error-track_caller", track_caller)]
179    fn from(value: std::io::Error) -> Self {
180        let kind = K::from_io_error_kind(value.kind());
181        if let Some(code) = value.raw_os_error() {
182            Self::internal_new(kind, ErrorPayload::RawOsError(code))
183        } else if let Some(data) = value.into_inner() {
184            Self::internal_new(kind, ErrorPayload::Error(data))
185        } else {
186            Self::internal_new(kind, ErrorPayload::Simple)
187        }
188    }
189}
190
191#[cfg(feature = "std")]
192impl<K: crate::kind::IntoIoKind> From<Error<K>> for std::io::Error {
193    fn from(value: Error<K>) -> Self {
194        if let Some(code) = value.raw_os_error() {
195            Self::from_raw_os_error(code)
196        } else {
197            let kind = K::into_io_error_kind(value.kind());
198
199            if let Some(payload) = value.into_error() {
200                std::io::Error::new(kind, payload)
201            } else {
202                std::io::Error::new(kind, "(no context)")
203            }
204        }
205    }
206}