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]
358 pub(crate) fn with_invalid_input<E>(error: E) -> Self
359 where
360 E: ToString,
361 {
362 Self::with_io_error(io::ErrorKind::InvalidInput, error)
363 }
364
365 #[inline]
368 pub fn kind(&self) -> ErrorKind {
369 self.error.kind()
370 }
371
372 fn layer_context(self, context: Cow<'static, Str>) -> Self {
375 match context {
376 Cow::Owned(context) => Self {
377 error: Box::new(ErrorImpl::ContextOwned {
378 context,
379 source: self.error,
380 }),
381 },
382 Cow::Borrowed(context) => Self {
383 error: Box::new(ErrorImpl::ContextStatic {
384 context,
385 source: self.error,
386 }),
387 },
388 }
389 }
390}
391
392impl Debug for Error {
393 #[inline]
394 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
395 Debug::fmt(&self.error, f)
396 }
397}
398
399impl Display for Error {
400 #[inline]
401 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
402 Display::fmt(&self.error, f)
403 }
404}
405
406impl error::Error for Error {
407 #[inline]
408 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
409 self.error.source()
410 }
411}
412
413impl From<io::Error> for Error {
414 fn from(other: io::Error) -> Self {
415 Self {
416 error: Box::new(ErrorImpl::Io(other)),
417 }
418 }
419}
420
421pub trait ErrorExt: private::Sealed {
423 type Output;
426
427 fn context<C>(self, context: C) -> Self::Output
432 where
433 C: IntoCowStr;
434
435 fn with_context<C, F>(self, f: F) -> Self::Output
437 where
438 C: IntoCowStr,
439 F: FnOnce() -> C;
440}
441
442impl ErrorExt for Error {
443 type Output = Error;
444
445 fn context<C>(self, context: C) -> Self::Output
446 where
447 C: IntoCowStr,
448 {
449 self.layer_context(context.into_cow_str())
450 }
451
452 fn with_context<C, F>(self, f: F) -> Self::Output
453 where
454 C: IntoCowStr,
455 F: FnOnce() -> C,
456 {
457 self.layer_context(f().into_cow_str())
458 }
459}
460
461impl<T, E> ErrorExt for Result<T, E>
462where
463 E: ErrorExt,
464{
465 type Output = Result<T, E::Output>;
466
467 fn context<C>(self, context: C) -> Self::Output
468 where
469 C: IntoCowStr,
470 {
471 match self {
472 Ok(val) => Ok(val),
473 Err(err) => Err(err.context(context)),
474 }
475 }
476
477 fn with_context<C, F>(self, f: F) -> Self::Output
478 where
479 C: IntoCowStr,
480 F: FnOnce() -> C,
481 {
482 match self {
483 Ok(val) => Ok(val),
484 Err(err) => Err(err.with_context(f)),
485 }
486 }
487}
488
489impl ErrorExt for io::Error {
490 type Output = Error;
491
492 fn context<C>(self, context: C) -> Self::Output
493 where
494 C: IntoCowStr,
495 {
496 Error::from(self).context(context)
497 }
498
499 fn with_context<C, F>(self, f: F) -> Self::Output
500 where
501 C: IntoCowStr,
502 F: FnOnce() -> C,
503 {
504 Error::from(self).with_context(f)
505 }
506}
507
508pub trait IntoError<T>: private::Sealed
511where
512 Self: Sized,
513{
514 fn ok_or_error<C, F>(self, kind: io::ErrorKind, f: F) -> Result<T, Error>
515 where
516 C: ToString,
517 F: FnOnce() -> C;
518
519 #[inline]
520 fn ok_or_invalid_data<C, F>(self, f: F) -> Result<T, Error>
521 where
522 C: ToString,
523 F: FnOnce() -> C,
524 {
525 self.ok_or_error(io::ErrorKind::InvalidData, f)
526 }
527}
528
529impl<T> IntoError<T> for Option<T> {
530 #[inline]
531 fn ok_or_error<C, F>(self, kind: io::ErrorKind, f: F) -> Result<T, Error>
532 where
533 C: ToString,
534 F: FnOnce() -> C,
535 {
536 self.ok_or_else(|| Error::with_io_error(kind, f().to_string()))
537 }
538}
539
540#[cfg(test)]
541mod tests {
542 use super::*;
543
544 use std::mem::size_of;
545
546 #[test]
548 fn str_wrapper() {
549 let b = "test string".to_string().into_boxed_str();
550 let s: &Str = b.borrow();
551 let _b: Box<str> = s.to_owned();
552
553 assert_eq!(s.to_string(), b.deref());
554 assert_eq!(format!("{s:?}"), "Str(\"test string\")");
555 }
556
557 #[test]
559 fn error_size() {
560 assert_eq!(size_of::<Error>(), size_of::<usize>());
561 assert_eq!(size_of::<ErrorImpl>(), 4 * size_of::<usize>());
562 }
563
564 #[test]
566 fn error_formatting() {
567 let err = io::Error::new(io::ErrorKind::InvalidData, "some invalid data");
568 let err = Error::from(err);
569
570 let src = err.source();
571 assert!(src.is_none(), "{src:?}");
572 assert!(err.error.is_owned().is_none());
573 assert_eq!(err.kind(), ErrorKind::InvalidData);
574 assert_eq!(format!("{err}"), "some invalid data");
575 assert_eq!(format!("{err:#}"), "some invalid data");
576 assert_eq!(format!("{err:?}"), "Error: some invalid data");
577 let expected = r#"Io(
579 Custom {
580 kind: InvalidData,
581 error: "some invalid data",
582 },
583)"#;
584 assert_eq!(format!("{err:#?}"), expected);
585
586 let err = err.context("inner context");
587 let src = err.source();
588 assert!(src.is_some(), "{src:?}");
589 assert!(!err.error.is_owned().unwrap());
590 assert_eq!(err.kind(), ErrorKind::InvalidData);
591 assert_eq!(format!("{err}"), "inner context");
592 assert_eq!(format!("{err:#}"), "inner context: some invalid data");
593
594 let expected = r#"Error: inner context
595
596Caused by:
597 some invalid data"#;
598 assert_eq!(format!("{err:?}"), expected);
599 assert_ne!(format!("{err:#?}"), "");
601
602 let err = err.context("outer context".to_string());
603 let src = err.source();
604 assert!(src.is_some(), "{src:?}");
605 assert!(err.error.is_owned().unwrap());
606 assert_eq!(err.kind(), ErrorKind::InvalidData);
607 assert_eq!(format!("{err}"), "outer context");
608 assert_eq!(
609 format!("{err:#}"),
610 "outer context: inner context: some invalid data"
611 );
612
613 let expected = r#"Error: outer context
614
615Caused by:
616 inner context
617 some invalid data"#;
618 assert_eq!(format!("{err:?}"), expected);
619 assert_ne!(format!("{err:#?}"), "");
620 }
621}