Skip to main content

nydus_api/
error.rs

1// Copyright 2020 Ant Group. All rights reserved.
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use std::fmt::Debug;
6
7/// Display error messages with line number, file path and optional backtrace.
8pub 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
31/// Define error macro like `x!()` or `x!(err)`.
32/// Note: The `x!()` macro will convert any origin error (Os, Simple, Custom) to Custom error.
33macro_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
47/// Define error macro for libc error codes
48macro_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
54// TODO: Add format string support
55// Add more libc error macro here if necessary
56define_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/// Return EINVAL error with formatted error message.
68#[macro_export]
69macro_rules! bail_einval {
70    ($($arg:tt)*) => {{
71        return Err(einval!(format!($($arg)*)))
72    }}
73}
74
75/// Return EIO error with formatted error message.
76#[macro_export]
77macro_rules! bail_eio {
78    ($($arg:tt)*) => {{
79        return Err(eio!(format!($($arg)*)))
80    }}
81}
82
83// Add more custom error macro here if necessary
84define_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        // Test einval macro
121        let err = einval!();
122        assert_eq!(err.kind(), Error::from_raw_os_error(libc::EINVAL).kind());
123
124        // Test enoent macro
125        let err = enoent!();
126        assert_eq!(err.kind(), Error::from_raw_os_error(libc::ENOENT).kind());
127
128        // Test ebadf macro
129        let err = ebadf!();
130        assert_eq!(err.kind(), Error::from_raw_os_error(libc::EBADF).kind());
131
132        // Test eacces macro
133        let err = eacces!();
134        assert_eq!(err.kind(), Error::from_raw_os_error(libc::EACCES).kind());
135
136        // Test enotdir macro
137        let err = enotdir!();
138        assert_eq!(err.kind(), Error::from_raw_os_error(libc::ENOTDIR).kind());
139
140        // Test eisdir macro
141        let err = eisdir!();
142        assert_eq!(err.kind(), Error::from_raw_os_error(libc::EISDIR).kind());
143
144        // Test ealready macro
145        let err = ealready!();
146        assert_eq!(err.kind(), Error::from_raw_os_error(libc::EALREADY).kind());
147
148        // Test enosys macro
149        let err = enosys!();
150        assert_eq!(err.kind(), Error::from_raw_os_error(libc::ENOSYS).kind());
151
152        // Test epipe macro
153        let err = epipe!();
154        assert_eq!(err.kind(), Error::from_raw_os_error(libc::EPIPE).kind());
155
156        // Test eio macro
157        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        // Test einval macro with context
166        let err = einval!(test_msg);
167        assert_eq!(err.kind(), Error::from_raw_os_error(libc::EINVAL).kind());
168
169        // Test enoent macro with context
170        let err = enoent!(test_msg);
171        assert_eq!(err.kind(), Error::from_raw_os_error(libc::ENOENT).kind());
172
173        // Test eio macro with context
174        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        // Test last_error macro
181        let err = last_error!();
182        // We can't predict the exact error, but we can check it's a valid error
183        assert!(!err.to_string().is_empty());
184
185        // Test eother macro
186        let err = eother!();
187        assert_eq!(err.kind(), ErrorKind::Other);
188
189        // Test eother macro with context
190        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        // Test bail_einval macro
205        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        // The error message format is controlled by the macro, so just check it's not empty
210        assert!(!err.to_string().is_empty());
211
212        // Test bail_eio macro
213        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        // The error message format is controlled by the macro, so just check it's not empty
218        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        // Test bail_einval with formatting
233        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        // The error message format is controlled by the macro, so just check it's not empty
238        assert!(!err.to_string().is_empty());
239
240        // Test bail_eio with formatting
241        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        // The error message format is controlled by the macro, so just check it's not empty
246        assert!(!err.to_string().is_empty());
247
248        // Test success case
249        let result = test_bail_with_format(3);
250        assert!(result.is_ok());
251    }
252}