1use std::borrow::Borrow;
2use std::borrow::Cow;
3use std::error;
4use std::error::Error as _;
5use std::fmt::Debug;
6use std::fmt::Display;
7use std::fmt::Formatter;
8use std::fmt::Result as FmtResult;
9use std::io;
10use std::mem::transmute;
11use std::ops::Deref;
12use std::result;
13
14pub type Result<T, E = Error> = result::Result<T, E>;
16
17#[allow(clippy::wildcard_imports)]
18mod private {
19 use super::*;
20
21 pub trait Sealed {}
22
23 impl<T> Sealed for Option<T> {}
24 impl<T, E> Sealed for Result<T, E> {}
25 impl Sealed for &'static str {}
26 impl Sealed for String {}
27 impl Sealed for Error {}
28
29 impl Sealed for io::Error {}
30}
31
32#[derive(Debug)]
35#[repr(transparent)]
36#[doc(hidden)]
37pub struct Str(str);
38
39impl ToOwned for Str {
40 type Owned = Box<str>;
41
42 #[inline]
43 fn to_owned(&self) -> Self::Owned {
44 self.0.to_string().into_boxed_str()
45 }
46}
47
48impl Borrow<Str> for Box<str> {
49 #[inline]
50 fn borrow(&self) -> &Str {
51 unsafe { transmute::<&str, &Str>(self.deref()) }
54 }
55}
56
57impl Deref for Str {
58 type Target = str;
59
60 fn deref(&self) -> &Self::Target {
61 &self.0
62 }
63}
64
65impl Display for Str {
67 #[inline]
68 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
69 Display::fmt(&self.0, f)
70 }
71}
72
73pub trait IntoCowStr: private::Sealed {
77 fn into_cow_str(self) -> Cow<'static, Str>;
78}
79
80impl IntoCowStr for &'static str {
81 fn into_cow_str(self) -> Cow<'static, Str> {
82 let other = unsafe { transmute::<&str, &Str>(self) };
85 Cow::Borrowed(other)
86 }
87}
88
89impl IntoCowStr for String {
90 fn into_cow_str(self) -> Cow<'static, Str> {
91 Cow::Owned(self.into_boxed_str())
92 }
93}
94
95enum ErrorImpl {
98 Io(io::Error),
99 ContextOwned {
106 context: Box<str>,
107 source: Box<ErrorImpl>,
108 },
109 ContextStatic {
110 context: &'static str,
111 source: Box<ErrorImpl>,
112 },
113}
114
115impl ErrorImpl {
116 fn kind(&self) -> ErrorKind {
117 match self {
118 Self::Io(error) => match error.kind() {
119 io::ErrorKind::NotFound => ErrorKind::NotFound,
120 io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied,
121 io::ErrorKind::AlreadyExists => ErrorKind::AlreadyExists,
122 io::ErrorKind::WouldBlock => ErrorKind::WouldBlock,
123 io::ErrorKind::InvalidInput => ErrorKind::InvalidInput,
124 io::ErrorKind::InvalidData => ErrorKind::InvalidData,
125 io::ErrorKind::TimedOut => ErrorKind::TimedOut,
126 io::ErrorKind::WriteZero => ErrorKind::WriteZero,
127 io::ErrorKind::Interrupted => ErrorKind::Interrupted,
128 io::ErrorKind::Unsupported => ErrorKind::Unsupported,
129 io::ErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof,
130 io::ErrorKind::OutOfMemory => ErrorKind::OutOfMemory,
131 _ => ErrorKind::Other,
132 },
133 Self::ContextOwned { source, .. } | Self::ContextStatic { source, .. } => {
134 source.deref().kind()
135 }
136 }
137 }
138
139 #[cfg(test)]
140 fn is_owned(&self) -> Option<bool> {
141 match self {
142 Self::ContextOwned { .. } => Some(true),
143 Self::ContextStatic { .. } => Some(false),
144 _ => None,
145 }
146 }
147}
148
149impl Debug for ErrorImpl {
150 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
153 if f.alternate() {
154 let mut dbg;
155
156 match self {
157 Self::Io(io) => {
158 dbg = f.debug_tuple(stringify!(Io));
159 dbg.field(io)
160 }
161 Self::ContextOwned { context, .. } => {
162 dbg = f.debug_tuple(stringify!(ContextOwned));
163 dbg.field(context)
164 }
165 Self::ContextStatic { context, .. } => {
166 dbg = f.debug_tuple(stringify!(ContextStatic));
167 dbg.field(context)
168 }
169 }
170 .finish()
171 } else {
172 let () = match self {
173 Self::Io(error) => write!(f, "Error: {error}")?,
174 Self::ContextOwned { context, .. } => write!(f, "Error: {context}")?,
175 Self::ContextStatic { context, .. } => write!(f, "Error: {context}")?,
176 };
177
178 if let Some(source) = self.source() {
179 let () = f.write_str("\n\nCaused by:")?;
180
181 let mut error = Some(source);
182 while let Some(err) = error {
183 let () = write!(f, "\n {err:}")?;
184 error = err.source();
185 }
186 }
187 Ok(())
188 }
189 }
190}
191
192impl Display for ErrorImpl {
193 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
194 let () = match self {
195 Self::Io(error) => Display::fmt(error, f)?,
196 Self::ContextOwned { context, .. } => Display::fmt(context, f)?,
197 Self::ContextStatic { context, .. } => Display::fmt(context, f)?,
198 };
199
200 if f.alternate() {
201 let mut error = self.source();
202 while let Some(err) = error {
203 let () = write!(f, ": {err}")?;
204 error = err.source();
205 }
206 }
207 Ok(())
208 }
209}
210
211impl error::Error for ErrorImpl {
212 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
213 match self {
214 Self::Io(error) => error.source(),
215 Self::ContextOwned { source, .. } | Self::ContextStatic { source, .. } => Some(source),
216 }
217 }
218}
219
220#[derive(Clone, Copy, Debug, PartialEq)]
226#[non_exhaustive]
227pub enum ErrorKind {
228 NotFound,
230 PermissionDenied,
232 AlreadyExists,
234 WouldBlock,
237 InvalidInput,
239 InvalidData,
241 TimedOut,
243 WriteZero,
246 Interrupted,
250 Unsupported,
252 UnexpectedEof,
255 OutOfMemory,
258 Other,
261}
262
263#[repr(transparent)]
322pub struct Error {
323 error: Box<ErrorImpl>,
325}
326
327impl Error {
328 #[inline]
333 pub fn from_raw_os_error(code: i32) -> Self {
334 debug_assert!(
335 code > 0,
336 "OS error code should be positive integer; got: {code}"
337 );
338 Self::from(io::Error::from_raw_os_error(code))
339 }
340
341 #[inline]
342 pub(crate) fn with_io_error<E>(kind: io::ErrorKind, error: E) -> Self
343 where
344 E: ToString,
345 {
346 Self::from(io::Error::new(kind, error.to_string()))
347 }
348
349 #[inline]
350 pub(crate) fn with_invalid_data<E>(error: E) -> Self
351 where
352 E: ToString,
353 {
354 Self::with_io_error(io::ErrorKind::InvalidData, error)
355 }
356
357 #[inline]
360 pub fn kind(&self) -> ErrorKind {
361 self.error.kind()
362 }
363
364 fn layer_context(self, context: Cow<'static, Str>) -> Self {
367 match context {
368 Cow::Owned(context) => Self {
369 error: Box::new(ErrorImpl::ContextOwned {
370 context,
371 source: self.error,
372 }),
373 },
374 Cow::Borrowed(context) => Self {
375 error: Box::new(ErrorImpl::ContextStatic {
376 context,
377 source: self.error,
378 }),
379 },
380 }
381 }
382}
383
384impl Debug for Error {
385 #[inline]
386 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
387 Debug::fmt(&self.error, f)
388 }
389}
390
391impl Display for Error {
392 #[inline]
393 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
394 Display::fmt(&self.error, f)
395 }
396}
397
398impl error::Error for Error {
399 #[inline]
400 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
401 self.error.source()
402 }
403}
404
405impl From<io::Error> for Error {
406 fn from(other: io::Error) -> Self {
407 Self {
408 error: Box::new(ErrorImpl::Io(other)),
409 }
410 }
411}
412
413pub trait ErrorExt: private::Sealed {
415 type Output;
418
419 fn context<C>(self, context: C) -> Self::Output
424 where
425 C: IntoCowStr;
426
427 fn with_context<C, F>(self, f: F) -> Self::Output
429 where
430 C: IntoCowStr,
431 F: FnOnce() -> C;
432}
433
434impl ErrorExt for Error {
435 type Output = Error;
436
437 fn context<C>(self, context: C) -> Self::Output
438 where
439 C: IntoCowStr,
440 {
441 self.layer_context(context.into_cow_str())
442 }
443
444 fn with_context<C, F>(self, f: F) -> Self::Output
445 where
446 C: IntoCowStr,
447 F: FnOnce() -> C,
448 {
449 self.layer_context(f().into_cow_str())
450 }
451}
452
453impl<T, E> ErrorExt for Result<T, E>
454where
455 E: ErrorExt,
456{
457 type Output = Result<T, E::Output>;
458
459 fn context<C>(self, context: C) -> Self::Output
460 where
461 C: IntoCowStr,
462 {
463 match self {
464 Ok(val) => Ok(val),
465 Err(err) => Err(err.context(context)),
466 }
467 }
468
469 fn with_context<C, F>(self, f: F) -> Self::Output
470 where
471 C: IntoCowStr,
472 F: FnOnce() -> C,
473 {
474 match self {
475 Ok(val) => Ok(val),
476 Err(err) => Err(err.with_context(f)),
477 }
478 }
479}
480
481impl ErrorExt for io::Error {
482 type Output = Error;
483
484 fn context<C>(self, context: C) -> Self::Output
485 where
486 C: IntoCowStr,
487 {
488 Error::from(self).context(context)
489 }
490
491 fn with_context<C, F>(self, f: F) -> Self::Output
492 where
493 C: IntoCowStr,
494 F: FnOnce() -> C,
495 {
496 Error::from(self).with_context(f)
497 }
498}
499
500pub trait IntoError<T>: private::Sealed
503where
504 Self: Sized,
505{
506 fn ok_or_error<C, F>(self, kind: io::ErrorKind, f: F) -> Result<T, Error>
507 where
508 C: ToString,
509 F: FnOnce() -> C;
510
511 #[inline]
512 fn ok_or_invalid_data<C, F>(self, f: F) -> Result<T, Error>
513 where
514 C: ToString,
515 F: FnOnce() -> C,
516 {
517 self.ok_or_error(io::ErrorKind::InvalidData, f)
518 }
519}
520
521impl<T> IntoError<T> for Option<T> {
522 #[inline]
523 fn ok_or_error<C, F>(self, kind: io::ErrorKind, f: F) -> Result<T, Error>
524 where
525 C: ToString,
526 F: FnOnce() -> C,
527 {
528 self.ok_or_else(|| Error::with_io_error(kind, f().to_string()))
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 use std::mem::size_of;
537
538 #[test]
540 fn str_wrapper() {
541 let b = "test string".to_string().into_boxed_str();
542 let s: &Str = b.borrow();
543 let _b: Box<str> = s.to_owned();
544
545 assert_eq!(s.to_string(), b.deref());
546 assert_eq!(format!("{s:?}"), "Str(\"test string\")");
547 }
548
549 #[test]
551 fn error_size() {
552 assert_eq!(size_of::<Error>(), size_of::<usize>());
553 assert_eq!(size_of::<ErrorImpl>(), 4 * size_of::<usize>());
554 }
555
556 #[test]
558 fn error_formatting() {
559 let err = io::Error::new(io::ErrorKind::InvalidData, "some invalid data");
560 let err = Error::from(err);
561
562 let src = err.source();
563 assert!(src.is_none(), "{src:?}");
564 assert!(err.error.is_owned().is_none());
565 assert_eq!(err.kind(), ErrorKind::InvalidData);
566 assert_eq!(format!("{err}"), "some invalid data");
567 assert_eq!(format!("{err:#}"), "some invalid data");
568 assert_eq!(format!("{err:?}"), "Error: some invalid data");
569 let expected = r#"Io(
571 Custom {
572 kind: InvalidData,
573 error: "some invalid data",
574 },
575)"#;
576 assert_eq!(format!("{err:#?}"), expected);
577
578 let err = err.context("inner context");
579 let src = err.source();
580 assert!(src.is_some(), "{src:?}");
581 assert!(!err.error.is_owned().unwrap());
582 assert_eq!(err.kind(), ErrorKind::InvalidData);
583 assert_eq!(format!("{err}"), "inner context");
584 assert_eq!(format!("{err:#}"), "inner context: some invalid data");
585
586 let expected = r#"Error: inner context
587
588Caused by:
589 some invalid data"#;
590 assert_eq!(format!("{err:?}"), expected);
591 assert_ne!(format!("{err:#?}"), "");
593
594 let err = err.context("outer context".to_string());
595 let src = err.source();
596 assert!(src.is_some(), "{src:?}");
597 assert!(err.error.is_owned().unwrap());
598 assert_eq!(err.kind(), ErrorKind::InvalidData);
599 assert_eq!(format!("{err}"), "outer context");
600 assert_eq!(
601 format!("{err:#}"),
602 "outer context: inner context: some invalid data"
603 );
604
605 let expected = r#"Error: outer context
606
607Caused by:
608 inner context
609 some invalid data"#;
610 assert_eq!(format!("{err:?}"), expected);
611 assert_ne!(format!("{err:#?}"), "");
612 }
613}