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<Self>,
108 },
109 ContextStatic {
110 context: &'static str,
111 source: Box<Self>,
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 _ if error.raw_os_error() == Some(libc::E2BIG) => ErrorKind::TooBig,
134 _ => ErrorKind::Other,
135 },
136 Self::ContextOwned { source, .. } | Self::ContextStatic { source, .. } => {
137 source.deref().kind()
138 }
139 }
140 }
141
142 #[cfg(test)]
143 fn is_owned(&self) -> Option<bool> {
144 match self {
145 Self::ContextOwned { .. } => Some(true),
146 Self::ContextStatic { .. } => Some(false),
147 _ => None,
148 }
149 }
150}
151
152impl Debug for ErrorImpl {
153 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
156 if f.alternate() {
157 let mut dbg;
158
159 match self {
160 Self::Io(io) => {
161 dbg = f.debug_tuple(stringify!(Io));
162 dbg.field(io)
163 }
164 Self::ContextOwned { context, .. } => {
165 dbg = f.debug_tuple(stringify!(ContextOwned));
166 dbg.field(context)
167 }
168 Self::ContextStatic { context, .. } => {
169 dbg = f.debug_tuple(stringify!(ContextStatic));
170 dbg.field(context)
171 }
172 }
173 .finish()
174 } else {
175 let () = match self {
176 Self::Io(error) => write!(f, "Error: {error}")?,
177 Self::ContextOwned { context, .. } => write!(f, "Error: {context}")?,
178 Self::ContextStatic { context, .. } => write!(f, "Error: {context}")?,
179 };
180
181 if let Some(source) = self.source() {
182 let () = f.write_str("\n\nCaused by:")?;
183
184 let mut error = Some(source);
185 while let Some(err) = error {
186 let () = write!(f, "\n {err:}")?;
187 error = err.source();
188 }
189 }
190 Ok(())
191 }
192 }
193}
194
195impl Display for ErrorImpl {
196 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
197 let () = match self {
198 Self::Io(error) => Display::fmt(error, f)?,
199 Self::ContextOwned { context, .. } => Display::fmt(context, f)?,
200 Self::ContextStatic { context, .. } => Display::fmt(context, f)?,
201 };
202
203 if f.alternate() {
204 let mut error = self.source();
205 while let Some(err) = error {
206 let () = write!(f, ": {err}")?;
207 error = err.source();
208 }
209 }
210 Ok(())
211 }
212}
213
214impl error::Error for ErrorImpl {
215 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
216 match self {
217 Self::Io(error) => error.source(),
218 Self::ContextOwned { source, .. } | Self::ContextStatic { source, .. } => Some(source),
219 }
220 }
221}
222
223#[derive(Clone, Copy, Debug, PartialEq)]
229#[non_exhaustive]
230pub enum ErrorKind {
231 NotFound,
233 PermissionDenied,
235 AlreadyExists,
237 WouldBlock,
240 InvalidInput,
242 InvalidData,
244 TimedOut,
246 WriteZero,
249 Interrupted,
253 Unsupported,
255 UnexpectedEof,
258 OutOfMemory,
261 TooBig,
269 Other,
272}
273
274#[repr(transparent)]
333pub struct Error {
334 error: Box<ErrorImpl>,
336}
337
338impl Error {
339 #[inline]
344 pub fn from_raw_os_error(code: i32) -> Self {
345 debug_assert!(
346 code > 0,
347 "OS error code should be positive integer; got: {code}"
348 );
349 Self::from(io::Error::from_raw_os_error(code))
350 }
351
352 #[inline]
353 pub(crate) fn with_io_error<E>(kind: io::ErrorKind, error: E) -> Self
354 where
355 E: ToString,
356 {
357 Self::from(io::Error::new(kind, error.to_string()))
358 }
359
360 #[inline]
361 pub(crate) fn with_invalid_data<E>(error: E) -> Self
362 where
363 E: ToString,
364 {
365 Self::with_io_error(io::ErrorKind::InvalidData, error)
366 }
367
368 #[inline]
369 pub(crate) fn with_invalid_input<E>(error: E) -> Self
370 where
371 E: ToString,
372 {
373 Self::with_io_error(io::ErrorKind::InvalidInput, error)
374 }
375
376 #[inline]
379 pub fn kind(&self) -> ErrorKind {
380 self.error.kind()
381 }
382
383 fn layer_context(self, context: Cow<'static, Str>) -> Self {
386 match context {
387 Cow::Owned(context) => Self {
388 error: Box::new(ErrorImpl::ContextOwned {
389 context,
390 source: self.error,
391 }),
392 },
393 Cow::Borrowed(context) => Self {
394 error: Box::new(ErrorImpl::ContextStatic {
395 context,
396 source: self.error,
397 }),
398 },
399 }
400 }
401}
402
403impl Debug for Error {
404 #[inline]
405 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
406 Debug::fmt(&self.error, f)
407 }
408}
409
410impl Display for Error {
411 #[inline]
412 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
413 Display::fmt(&self.error, f)
414 }
415}
416
417impl error::Error for Error {
418 #[inline]
419 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
420 self.error.source()
421 }
422}
423
424impl From<io::Error> for Error {
425 fn from(other: io::Error) -> Self {
426 Self {
427 error: Box::new(ErrorImpl::Io(other)),
428 }
429 }
430}
431
432pub trait ErrorExt: private::Sealed {
434 type Output;
437
438 fn context<C>(self, context: C) -> Self::Output
443 where
444 C: IntoCowStr;
445
446 fn with_context<C, F>(self, f: F) -> Self::Output
448 where
449 C: IntoCowStr,
450 F: FnOnce() -> C;
451}
452
453impl ErrorExt for Error {
454 type Output = Self;
455
456 fn context<C>(self, context: C) -> Self::Output
457 where
458 C: IntoCowStr,
459 {
460 self.layer_context(context.into_cow_str())
461 }
462
463 fn with_context<C, F>(self, f: F) -> Self::Output
464 where
465 C: IntoCowStr,
466 F: FnOnce() -> C,
467 {
468 self.layer_context(f().into_cow_str())
469 }
470}
471
472impl<T, E> ErrorExt for Result<T, E>
473where
474 E: ErrorExt,
475{
476 type Output = Result<T, E::Output>;
477
478 fn context<C>(self, context: C) -> Self::Output
479 where
480 C: IntoCowStr,
481 {
482 match self {
483 Ok(val) => Ok(val),
484 Err(err) => Err(err.context(context)),
485 }
486 }
487
488 fn with_context<C, F>(self, f: F) -> Self::Output
489 where
490 C: IntoCowStr,
491 F: FnOnce() -> C,
492 {
493 match self {
494 Ok(val) => Ok(val),
495 Err(err) => Err(err.with_context(f)),
496 }
497 }
498}
499
500impl ErrorExt for io::Error {
501 type Output = Error;
502
503 fn context<C>(self, context: C) -> Self::Output
504 where
505 C: IntoCowStr,
506 {
507 Error::from(self).context(context)
508 }
509
510 fn with_context<C, F>(self, f: F) -> Self::Output
511 where
512 C: IntoCowStr,
513 F: FnOnce() -> C,
514 {
515 Error::from(self).with_context(f)
516 }
517}
518
519pub trait IntoError<T>: private::Sealed
522where
523 Self: Sized,
524{
525 fn ok_or_error<C, F>(self, kind: io::ErrorKind, f: F) -> Result<T, Error>
526 where
527 C: ToString,
528 F: FnOnce() -> C;
529
530 #[inline]
531 fn ok_or_invalid_data<C, F>(self, f: F) -> Result<T, Error>
532 where
533 C: ToString,
534 F: FnOnce() -> C,
535 {
536 self.ok_or_error(io::ErrorKind::InvalidData, f)
537 }
538}
539
540impl<T> IntoError<T> for Option<T> {
541 #[inline]
542 fn ok_or_error<C, F>(self, kind: io::ErrorKind, f: F) -> Result<T, Error>
543 where
544 C: ToString,
545 F: FnOnce() -> C,
546 {
547 self.ok_or_else(|| Error::with_io_error(kind, f().to_string()))
548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554
555 use std::mem::size_of;
556
557 #[test]
559 fn str_wrapper() {
560 let b = "test string".to_string().into_boxed_str();
561 let s: &Str = b.borrow();
562 let _b: Box<str> = s.to_owned();
563
564 assert_eq!(s.to_string(), b.deref());
565 assert_eq!(format!("{s:?}"), "Str(\"test string\")");
566 }
567
568 #[test]
570 fn error_size() {
571 assert_eq!(size_of::<Error>(), size_of::<usize>());
572 assert_eq!(size_of::<ErrorImpl>(), 4 * size_of::<usize>());
573 }
574
575 #[test]
577 fn error_formatting() {
578 let err = io::Error::new(io::ErrorKind::InvalidData, "some invalid data");
579 let err = Error::from(err);
580
581 let src = err.source();
582 assert!(src.is_none(), "{src:?}");
583 assert!(err.error.is_owned().is_none());
584 assert_eq!(err.kind(), ErrorKind::InvalidData);
585 assert_eq!(format!("{err}"), "some invalid data");
586 assert_eq!(format!("{err:#}"), "some invalid data");
587 assert_eq!(format!("{err:?}"), "Error: some invalid data");
588 let expected = r#"Io(
590 Custom {
591 kind: InvalidData,
592 error: "some invalid data",
593 },
594)"#;
595 assert_eq!(format!("{err:#?}"), expected);
596
597 let err = err.context("inner context");
598 let src = err.source();
599 assert!(src.is_some(), "{src:?}");
600 assert!(!err.error.is_owned().unwrap());
601 assert_eq!(err.kind(), ErrorKind::InvalidData);
602 assert_eq!(format!("{err}"), "inner context");
603 assert_eq!(format!("{err:#}"), "inner context: some invalid data");
604
605 let expected = r#"Error: inner context
606
607Caused by:
608 some invalid data"#;
609 assert_eq!(format!("{err:?}"), expected);
610 assert_ne!(format!("{err:#?}"), "");
612
613 let err = err.context("outer context".to_string());
614 let src = err.source();
615 assert!(src.is_some(), "{src:?}");
616 assert!(err.error.is_owned().unwrap());
617 assert_eq!(err.kind(), ErrorKind::InvalidData);
618 assert_eq!(format!("{err}"), "outer context");
619 assert_eq!(
620 format!("{err:#}"),
621 "outer context: inner context: some invalid data"
622 );
623
624 let expected = r#"Error: outer context
625
626Caused by:
627 inner context
628 some invalid data"#;
629 assert_eq!(format!("{err:?}"), expected);
630 assert_ne!(format!("{err:#?}"), "");
631 }
632
633 #[test]
635 fn e2big_maps_to_too_big() {
636 let err = Error::from_raw_os_error(libc::E2BIG);
637 assert_eq!(err.kind(), ErrorKind::TooBig);
638
639 let err = err.context("inserting key into map");
640 assert_eq!(err.kind(), ErrorKind::TooBig);
641 }
642}