1use std::{num::TryFromIntError, path::PathBuf};
2
3#[derive(Debug, thiserror::Error)]
4pub enum Error {
5 #[error("{context}: {source}")]
6 Io {
7 context: String,
8 source: std::io::Error,
9 },
10
11 #[error("{0}")]
12 Syscall(#[from] nix::errno::Errno),
13
14 #[error("{0}")]
15 TryFromInt(#[from] TryFromIntError),
16
17 #[cfg(feature = "rayon")]
18 #[error("{0}")]
19 ThreadPool(#[from] rayon::ThreadPoolBuildError),
20
21 #[error("{path}: offset {offset} beyond file size {file_len}")]
22 OffsetBeyondFile {
23 path: PathBuf,
24 offset: u64,
25 file_len: u64,
26 },
27}
28
29impl Error {
30 pub fn io(context: impl Into<String>, source: std::io::Error) -> Self {
31 Self::Io {
32 context: context.into(),
33 source,
34 }
35 }
36}
37
38impl From<std::io::Error> for Error {
39 fn from(source: std::io::Error) -> Self {
40 Self::Io {
41 context: String::new(),
42 source,
43 }
44 }
45}
46
47pub type Result<T> = std::result::Result<T, Error>;
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn test_io_error_display_with_context() {
55 let err = Error::io(
56 "/tmp/test.dat",
57 std::io::Error::new(std::io::ErrorKind::NotFound, "file not found"),
58 );
59 let msg = err.to_string();
60 assert!(msg.contains("/tmp/test.dat"), "msg: {msg}");
61 assert!(msg.contains("file not found"), "msg: {msg}");
62 }
63
64 #[test]
65 fn test_io_error_from_std() {
66 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
67 let err: Error = io_err.into();
68 assert!(matches!(err, Error::Io { .. }));
69 }
70
71 #[test]
72 fn test_syscall_error_from_errno() {
73 let err: Error = nix::errno::Errno::EBADF.into();
74 assert!(matches!(err, Error::Syscall(_)));
75 assert!(err.to_string().contains("EBADF"));
76 }
77
78 #[test]
79 fn test_offset_beyond_file_display() {
80 let err = Error::OffsetBeyondFile {
81 path: PathBuf::from("/data/big.bin"),
82 offset: 1000,
83 file_len: 500,
84 };
85 let msg = err.to_string();
86 assert!(msg.contains("/data/big.bin"), "msg: {msg}");
87 assert!(msg.contains("1000"), "msg: {msg}");
88 assert!(msg.contains("500"), "msg: {msg}");
89 }
90}