eject/error.rs
1//! Errors returned by this crate.
2//!
3//! Errors will try to be classified into the categories defined
4//! in the [`ErrorKind`](ErrorKind) enum.
5//! ```
6//! use eject::{device::Device, error::ErrorKind};
7//!
8//! let error = Device::open("doesntexist").err().unwrap();
9//! assert_eq!(error.kind(), ErrorKind::NotFound);
10//! ```
11//!
12//! You can convert an [`Error`] to an [`std::io::Error`]
13//! ```
14//! use eject::device::Device;
15//! use std::{fs::File, io};
16//!
17//! let std_err = File::open("doesntexist").err().unwrap();
18//! let eject_err: io::Error = Device::open("doesntexist").err().unwrap().into();
19//!
20//! assert_eq!(std_err.to_string(), eject_err.to_string());
21//! assert_eq!(std_err.kind(), eject_err.kind());
22//! assert_eq!(std_err.raw_os_error(), eject_err.raw_os_error());
23//! ```
24//!
25//! If the error comes from the OS, you can get its OS specific code.
26//! ```
27//! use eject::device::Device;
28//!
29//! let error = Device::open("doesntexist").err().unwrap();
30//! println!("Error code: {}", error.os_code().unwrap());
31//! ```
32
33/// Result returned by functions in this crate.
34///
35/// See the [`error`][crate::error] module docs for details and examples.
36pub type Result<T> = core::result::Result<T, Error>;
37
38#[derive(thiserror::Error, Debug, Clone)]
39#[error("{}", message)]
40/// Error type for functions in this crate.
41///
42/// See the [`error`][crate::error] module docs for more details and examples.
43pub struct Error {
44 /// OS error code, or 0 if the error doesn't come from the OS.
45 pub(crate) code: i32,
46 /// User friendly error messages, which come from the OS in OS errors.
47 pub(crate) message: String,
48 /// OS agnostic error category.
49 pub(crate) kind: ErrorKind,
50}
51
52impl Error {
53 /// Returns the OS specific error code or `None` if the
54 /// error doesn't come directly from the OS.
55 pub fn os_code(&self) -> Option<i32> {
56 if self.code == 0 {
57 None
58 } else {
59 Some(self.code)
60 }
61 }
62
63 /// Returns an OS agnostic category for this error.
64 pub fn kind(&self) -> ErrorKind {
65 self.kind
66 }
67}
68
69impl From<Error> for std::io::Error {
70 fn from(e: Error) -> Self {
71 if e.code == 0 {
72 Self::new(e.kind.into(), e.message)
73 } else {
74 Self::from_raw_os_error(e.code)
75 }
76 }
77}
78
79#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
80#[non_exhaustive]
81/// General categories for OS and library errors.
82///
83/// Bear in mind that errors that are currently [`Unknown`][Self::Unknown] may be moved to
84/// a different category in the future. This would not be considered a breaking
85/// change. Instead of matching against [`Unknown`][Self::Unknown], use a wildcard pattern: `_ => `
86///
87/// See the [`error`][crate::error] module docs for details and examples.
88pub enum ErrorKind {
89 /// The operation failed due to a permission issue.
90 AccessDenied,
91 /// The file or path doesn't exist.
92 NotFound,
93 /// The path contains invalid characters or is improperly formatted.
94 InvalidPath,
95 /// The device doesn't support performing this operation.
96 /// This can often happen when a device is not of the type you expect,
97 /// or you've opened something that is not a device, like a regular file.
98 UnsupportedOperation,
99 /// The category of this error could not be determined.
100 Unknown,
101}
102
103impl From<ErrorKind> for std::io::ErrorKind {
104 fn from(e: ErrorKind) -> Self {
105 match e {
106 ErrorKind::AccessDenied => Self::PermissionDenied,
107 ErrorKind::NotFound => Self::NotFound,
108 ErrorKind::InvalidPath => Self::InvalidInput,
109 ErrorKind::UnsupportedOperation => Self::Unsupported,
110 ErrorKind::Unknown => std::io::Error::from_raw_os_error(498498498).kind(),
111 }
112 }
113}