1use std::fmt::Debug;
6
7pub fn make_error(
9 err: std::io::Error,
10 _raw: impl Debug,
11 _file: &str,
12 _line: u32,
13) -> std::io::Error {
14 #[cfg(feature = "error-backtrace")]
15 {
16 if let Ok(val) = std::env::var("RUST_BACKTRACE") {
17 if val.trim() != "0" {
18 error!("Stack:\n{:?}", backtrace::Backtrace::new());
19 error!("Error:\n\t{:?}\n\tat {}:{}", _raw, _file, _line);
20 return err;
21 }
22 }
23 error!(
24 "Error:\n\t{:?}\n\tat {}:{}\n\tnote: enable `RUST_BACKTRACE=1` env to display a backtrace",
25 _raw, _file, _line
26 );
27 }
28 err
29}
30
31macro_rules! define_error_macro {
34 ($fn:ident, $err:expr) => {
35 #[macro_export]
36 macro_rules! $fn {
37 () => {
38 std::io::Error::new($err.kind(), format!("{}: {}:{}", $err, file!(), line!()))
39 };
40 ($raw:expr) => {
41 $crate::error::make_error($err, &$raw, file!(), line!())
42 };
43 }
44 };
45}
46
47macro_rules! define_libc_error_macro {
49 ($fn:ident, $code:ident) => {
50 define_error_macro!($fn, std::io::Error::from_raw_os_error(libc::$code));
51 };
52}
53
54define_libc_error_macro!(einval, EINVAL);
57define_libc_error_macro!(enoent, ENOENT);
58define_libc_error_macro!(ebadf, EBADF);
59define_libc_error_macro!(eacces, EACCES);
60define_libc_error_macro!(enotdir, ENOTDIR);
61define_libc_error_macro!(eisdir, EISDIR);
62define_libc_error_macro!(ealready, EALREADY);
63define_libc_error_macro!(enosys, ENOSYS);
64define_libc_error_macro!(epipe, EPIPE);
65define_libc_error_macro!(eio, EIO);
66
67#[macro_export]
69macro_rules! bail_einval {
70 ($($arg:tt)*) => {{
71 return Err(einval!(format!($($arg)*)))
72 }}
73}
74
75#[macro_export]
77macro_rules! bail_eio {
78 ($($arg:tt)*) => {{
79 return Err(eio!(format!($($arg)*)))
80 }}
81}
82
83define_error_macro!(last_error, std::io::Error::last_os_error());
85define_error_macro!(eother, std::io::Error::new(std::io::ErrorKind::Other, ""));
86
87#[cfg(test)]
88mod tests {
89 use std::io::{Error, ErrorKind};
90
91 fn check_size(size: usize) -> std::io::Result<()> {
92 if size > 0x1000 {
93 return Err(einval!());
94 }
95
96 Ok(())
97 }
98
99 #[test]
100 fn test_einval() {
101 assert_eq!(
102 check_size(0x2000).unwrap_err().kind(),
103 std::io::Error::from_raw_os_error(libc::EINVAL).kind()
104 );
105 }
106
107 #[test]
108 fn test_make_error() {
109 let original_error = Error::other("test error");
110 let debug_info = "debug information";
111 let file = "test.rs";
112 let line = 42;
113
114 let result_error = super::make_error(original_error, debug_info, file, line);
115 assert_eq!(result_error.kind(), ErrorKind::Other);
116 }
117
118 #[test]
119 fn test_libc_error_macros() {
120 let err = einval!();
122 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EINVAL).kind());
123
124 let err = enoent!();
126 assert_eq!(err.kind(), Error::from_raw_os_error(libc::ENOENT).kind());
127
128 let err = ebadf!();
130 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EBADF).kind());
131
132 let err = eacces!();
134 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EACCES).kind());
135
136 let err = enotdir!();
138 assert_eq!(err.kind(), Error::from_raw_os_error(libc::ENOTDIR).kind());
139
140 let err = eisdir!();
142 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EISDIR).kind());
143
144 let err = ealready!();
146 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EALREADY).kind());
147
148 let err = enosys!();
150 assert_eq!(err.kind(), Error::from_raw_os_error(libc::ENOSYS).kind());
151
152 let err = epipe!();
154 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EPIPE).kind());
155
156 let err = eio!();
158 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EIO).kind());
159 }
160
161 #[test]
162 fn test_libc_error_macros_with_context() {
163 let test_msg = "test context";
164
165 let err = einval!(test_msg);
167 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EINVAL).kind());
168
169 let err = enoent!(test_msg);
171 assert_eq!(err.kind(), Error::from_raw_os_error(libc::ENOENT).kind());
172
173 let err = eio!(test_msg);
175 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EIO).kind());
176 }
177
178 #[test]
179 fn test_custom_error_macros() {
180 let err = last_error!();
182 assert!(!err.to_string().is_empty());
184
185 let err = eother!();
187 assert_eq!(err.kind(), ErrorKind::Other);
188
189 let err = eother!("custom context");
191 assert_eq!(err.kind(), ErrorKind::Other);
192 }
193
194 fn test_bail_einval_function() -> std::io::Result<()> {
195 bail_einval!("test error message");
196 }
197
198 fn test_bail_eio_function() -> std::io::Result<()> {
199 bail_eio!("test error message");
200 }
201
202 #[test]
203 fn test_bail_macros() {
204 let result = test_bail_einval_function();
206 assert!(result.is_err());
207 let err = result.unwrap_err();
208 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EINVAL).kind());
209 assert!(!err.to_string().is_empty());
211
212 let result = test_bail_eio_function();
214 assert!(result.is_err());
215 let err = result.unwrap_err();
216 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EIO).kind());
217 assert!(!err.to_string().is_empty());
219 }
220
221 #[test]
222 fn test_bail_macros_with_formatting() {
223 fn test_bail_with_format(code: i32) -> std::io::Result<()> {
224 if code == 1 {
225 bail_einval!("error code: {}", code);
226 } else if code == 2 {
227 bail_eio!("I/O error with code: {}", code);
228 }
229 Ok(())
230 }
231
232 let result = test_bail_with_format(1);
234 assert!(result.is_err());
235 let err = result.unwrap_err();
236 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EINVAL).kind());
237 assert!(!err.to_string().is_empty());
239
240 let result = test_bail_with_format(2);
242 assert!(result.is_err());
243 let err = result.unwrap_err();
244 assert_eq!(err.kind(), Error::from_raw_os_error(libc::EIO).kind());
245 assert!(!err.to_string().is_empty());
247
248 let result = test_bail_with_format(3);
250 assert!(result.is_ok());
251 }
252}