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)]
333#[doc(alias = "libbpf_get_error")]
334pub struct Error {
335 error: Box<ErrorImpl>,
337}
338
339impl Error {
340 #[inline]
345 pub fn from_raw_os_error(code: i32) -> Self {
346 debug_assert!(
347 code > 0,
348 "OS error code should be positive integer; got: {code}"
349 );
350 Self::from(io::Error::from_raw_os_error(code))
351 }
352
353 #[inline]
354 pub(crate) fn with_io_error<E>(kind: io::ErrorKind, error: E) -> Self
355 where
356 E: ToString,
357 {
358 Self::from(io::Error::new(kind, error.to_string()))
359 }
360
361 #[inline]
362 pub(crate) fn with_invalid_data<E>(error: E) -> Self
363 where
364 E: ToString,
365 {
366 Self::with_io_error(io::ErrorKind::InvalidData, error)
367 }
368
369 #[inline]
370 pub(crate) fn with_invalid_input<E>(error: E) -> Self
371 where
372 E: ToString,
373 {
374 Self::with_io_error(io::ErrorKind::InvalidInput, error)
375 }
376
377 #[inline]
380 pub fn kind(&self) -> ErrorKind {
381 self.error.kind()
382 }
383
384 fn layer_context(self, context: Cow<'static, Str>) -> Self {
387 match context {
388 Cow::Owned(context) => Self {
389 error: Box::new(ErrorImpl::ContextOwned {
390 context,
391 source: self.error,
392 }),
393 },
394 Cow::Borrowed(context) => Self {
395 error: Box::new(ErrorImpl::ContextStatic {
396 context,
397 source: self.error,
398 }),
399 },
400 }
401 }
402}
403
404impl Debug for Error {
405 #[inline]
406 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
407 Debug::fmt(&self.error, f)
408 }
409}
410
411impl Display for Error {
412 #[inline]
413 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
414 Display::fmt(&self.error, f)
415 }
416}
417
418impl error::Error for Error {
419 #[inline]
420 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
421 self.error.source()
422 }
423}
424
425impl From<io::Error> for Error {
426 fn from(other: io::Error) -> Self {
427 Self {
428 error: Box::new(ErrorImpl::Io(other)),
429 }
430 }
431}
432
433pub trait ErrorExt: private::Sealed {
435 type Output;
438
439 fn context<C>(self, context: C) -> Self::Output
444 where
445 C: IntoCowStr;
446
447 fn with_context<C, F>(self, f: F) -> Self::Output
449 where
450 C: IntoCowStr,
451 F: FnOnce() -> C;
452}
453
454impl ErrorExt for Error {
455 type Output = Self;
456
457 fn context<C>(self, context: C) -> Self::Output
458 where
459 C: IntoCowStr,
460 {
461 self.layer_context(context.into_cow_str())
462 }
463
464 fn with_context<C, F>(self, f: F) -> Self::Output
465 where
466 C: IntoCowStr,
467 F: FnOnce() -> C,
468 {
469 self.layer_context(f().into_cow_str())
470 }
471}
472
473impl<T, E> ErrorExt for Result<T, E>
474where
475 E: ErrorExt,
476{
477 type Output = Result<T, E::Output>;
478
479 fn context<C>(self, context: C) -> Self::Output
480 where
481 C: IntoCowStr,
482 {
483 match self {
484 Ok(val) => Ok(val),
485 Err(err) => Err(err.context(context)),
486 }
487 }
488
489 fn with_context<C, F>(self, f: F) -> Self::Output
490 where
491 C: IntoCowStr,
492 F: FnOnce() -> C,
493 {
494 match self {
495 Ok(val) => Ok(val),
496 Err(err) => Err(err.with_context(f)),
497 }
498 }
499}
500
501impl ErrorExt for io::Error {
502 type Output = Error;
503
504 fn context<C>(self, context: C) -> Self::Output
505 where
506 C: IntoCowStr,
507 {
508 Error::from(self).context(context)
509 }
510
511 fn with_context<C, F>(self, f: F) -> Self::Output
512 where
513 C: IntoCowStr,
514 F: FnOnce() -> C,
515 {
516 Error::from(self).with_context(f)
517 }
518}
519
520pub trait IntoError<T>: private::Sealed
523where
524 Self: Sized,
525{
526 fn ok_or_error<C, F>(self, kind: io::ErrorKind, f: F) -> Result<T, Error>
527 where
528 C: ToString,
529 F: FnOnce() -> C;
530
531 #[inline]
532 fn ok_or_invalid_data<C, F>(self, f: F) -> Result<T, Error>
533 where
534 C: ToString,
535 F: FnOnce() -> C,
536 {
537 self.ok_or_error(io::ErrorKind::InvalidData, f)
538 }
539}
540
541impl<T> IntoError<T> for Option<T> {
542 #[inline]
543 fn ok_or_error<C, F>(self, kind: io::ErrorKind, f: F) -> Result<T, Error>
544 where
545 C: ToString,
546 F: FnOnce() -> C,
547 {
548 self.ok_or_else(|| Error::with_io_error(kind, f().to_string()))
549 }
550}
551
552#[cfg(test)]
553mod tests {
554 use super::*;
555
556 use std::mem::size_of;
557
558 #[test]
560 fn str_wrapper() {
561 let b = "test string".to_string().into_boxed_str();
562 let s: &Str = b.borrow();
563 let _b: Box<str> = s.to_owned();
564
565 assert_eq!(s.to_string(), b.deref());
566 assert_eq!(format!("{s:?}"), "Str(\"test string\")");
567 }
568
569 #[test]
571 fn error_size() {
572 assert_eq!(size_of::<Error>(), size_of::<usize>());
573 assert_eq!(size_of::<ErrorImpl>(), 4 * size_of::<usize>());
574 }
575
576 #[test]
578 fn error_formatting() {
579 let err = io::Error::new(io::ErrorKind::InvalidData, "some invalid data");
580 let err = Error::from(err);
581
582 let src = err.source();
583 assert!(src.is_none(), "{src:?}");
584 assert!(err.error.is_owned().is_none());
585 assert_eq!(err.kind(), ErrorKind::InvalidData);
586 assert_eq!(format!("{err}"), "some invalid data");
587 assert_eq!(format!("{err:#}"), "some invalid data");
588 assert_eq!(format!("{err:?}"), "Error: some invalid data");
589 let expected = r#"Io(
591 Custom {
592 kind: InvalidData,
593 error: "some invalid data",
594 },
595)"#;
596 assert_eq!(format!("{err:#?}"), expected);
597
598 let err = err.context("inner context");
599 let src = err.source();
600 assert!(src.is_some(), "{src:?}");
601 assert!(!err.error.is_owned().unwrap());
602 assert_eq!(err.kind(), ErrorKind::InvalidData);
603 assert_eq!(format!("{err}"), "inner context");
604 assert_eq!(format!("{err:#}"), "inner context: some invalid data");
605
606 let expected = r#"Error: inner context
607
608Caused by:
609 some invalid data"#;
610 assert_eq!(format!("{err:?}"), expected);
611 assert_ne!(format!("{err:#?}"), "");
613
614 let err = err.context("outer context".to_string());
615 let src = err.source();
616 assert!(src.is_some(), "{src:?}");
617 assert!(err.error.is_owned().unwrap());
618 assert_eq!(err.kind(), ErrorKind::InvalidData);
619 assert_eq!(format!("{err}"), "outer context");
620 assert_eq!(
621 format!("{err:#}"),
622 "outer context: inner context: some invalid data"
623 );
624
625 let expected = r#"Error: outer context
626
627Caused by:
628 inner context
629 some invalid data"#;
630 assert_eq!(format!("{err:?}"), expected);
631 assert_ne!(format!("{err:#?}"), "");
632 }
633
634 #[test]
636 fn e2big_maps_to_too_big() {
637 let err = Error::from_raw_os_error(libc::E2BIG);
638 assert_eq!(err.kind(), ErrorKind::TooBig);
639
640 let err = err.context("inserting key into map");
641 assert_eq!(err.kind(), ErrorKind::TooBig);
642 }
643}