1use core::fmt;
2
3pub type Result<T> = core::result::Result<T, Error>;
4
5pub struct Error(i32);
12
13impl Error {
14 #[inline]
16 pub fn last() -> Self {
17 Self(unsafe { *libc::__errno_location() })
18 }
19
20 #[inline]
22 pub fn from_code(eno: i32) -> Self {
23 Self(eno)
24 }
25
26 #[inline]
28 pub fn code(&self) -> i32 {
29 self.0
30 }
31
32 fn strerror<'a>(&self, buf: &'a mut [u8]) -> &'a str {
33 static UNKNOWN_ERROR: &str = "Unknown error";
34 if self.0 < 0 {
35 return UNKNOWN_ERROR;
36 }
37
38 let ret = unsafe { libc::strerror_r(self.0, buf.as_mut_ptr() as *mut _, buf.len()) };
39 if ret == libc::EINVAL {
40 return UNKNOWN_ERROR;
41 }
42 assert_eq!(ret, 0, "strerror_r() returned {}", ret);
43
44 #[cfg(feature = "std")]
45 let msg = unsafe { std::ffi::CStr::from_ptr(buf.as_ptr() as *const _) }
46 .to_str()
47 .unwrap();
48
49 #[cfg(not(feature = "std"))]
50 let msg = {
51 let len = buf.iter().position(|&ch| ch == 0).unwrap();
52 core::str::from_utf8(&buf[..len]).unwrap()
53 };
54
55 #[cfg(target_env = "musl")]
56 if msg == "No error information" {
57 return UNKNOWN_ERROR;
58 }
59
60 msg
61 }
62}
63
64impl fmt::Display for Error {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 let mut buf = [0u8; 1024];
67 f.write_str(self.strerror(&mut buf))?;
68 write!(f, " (code {})", self.0)
69 }
70}
71
72impl fmt::Debug for Error {
73 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74 let mut buf = [0u8; 1024];
75 let message = self.strerror(&mut buf);
76 f.debug_struct("Error")
77 .field("code", &self.0)
78 .field("message", &message)
79 .finish()
80 }
81}
82
83#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
84#[cfg(feature = "std")]
85impl std::error::Error for Error {}
86
87#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
88#[cfg(feature = "std")]
89impl From<Error> for std::io::Error {
90 #[inline]
91 fn from(e: Error) -> Self {
92 Self::from_raw_os_error(e.0)
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_code() {
102 assert_eq!(Error::from_code(libc::EPERM).code(), libc::EPERM);
103 assert_eq!(Error::from_code(libc::ENOENT).code(), libc::ENOENT);
104 }
105
106 #[test]
107 fn test_last() {
108 unsafe {
109 *libc::__errno_location() = libc::EPERM;
110 }
111 assert_eq!(Error::last().code(), libc::EPERM);
112
113 unsafe {
114 *libc::__errno_location() = libc::ENOENT;
115 }
116 assert_eq!(Error::last().code(), libc::ENOENT);
117 }
118
119 #[test]
120 fn test_strerror() {
121 let mut buf = [0u8; 1024];
122
123 assert_eq!(
124 Error::from_code(libc::EISDIR).strerror(&mut buf),
125 "Is a directory"
126 );
127
128 assert_eq!(Error::from_code(-1).strerror(&mut buf), "Unknown error");
129 assert_eq!(Error::from_code(8192).strerror(&mut buf), "Unknown error");
130 }
131
132 #[cfg(feature = "std")]
133 #[test]
134 fn test_display() {
135 assert_eq!(
136 Error::from_code(libc::EISDIR).to_string(),
137 format!("Is a directory (code {})", libc::EISDIR)
138 );
139 }
140
141 #[cfg(feature = "std")]
142 #[test]
143 fn test_debug() {
144 assert_eq!(
145 format!("{:?}", Error::from_code(libc::EISDIR)),
146 format!(
147 "Error {{ code: {}, message: \"Is a directory\" }}",
148 libc::EISDIR
149 )
150 );
151 }
152
153 #[cfg(feature = "std")]
154 #[test]
155 fn test_from_error() {
156 assert_eq!(
157 std::io::Error::from(Error::from_code(libc::ENOENT)).raw_os_error(),
158 Some(libc::ENOENT)
159 );
160 }
161}