1use std::path::PathBuf;
6use thiserror::Error;
7
8pub type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Debug, Error)]
13pub enum Error {
14 #[error("Failed to parse {path}: {message}")]
16 Parse {
17 path: PathBuf,
19 message: String,
21 },
22
23 #[error("IO error for {path}: {source}")]
25 Io {
26 path: PathBuf,
28 #[source]
30 source: std::io::Error,
31 },
32
33 #[error("Configuration error: {message}")]
35 Config {
36 message: String,
38 },
39
40 #[error("IO error: {0}")]
42 IoGeneric(#[from] std::io::Error),
43}
44
45impl Error {
46 pub fn parse(path: impl Into<PathBuf>, message: impl Into<String>) -> Self {
48 Self::Parse {
49 path: path.into(),
50 message: message.into(),
51 }
52 }
53
54 pub fn io(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
56 Self::Io {
57 path: path.into(),
58 source,
59 }
60 }
61
62 pub fn config(message: impl Into<String>) -> Self {
64 Self::Config {
65 message: message.into(),
66 }
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_parse_error_display() {
76 let err = Error::parse("/path/to/file.rs", "unexpected token");
77 let msg = err.to_string();
78 assert!(msg.contains("/path/to/file.rs"));
79 assert!(msg.contains("unexpected token"));
80 }
81
82 #[test]
83 fn test_io_error_display() {
84 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
85 let err = Error::io("/path/to/missing.rs", io_err);
86 let msg = err.to_string();
87 assert!(msg.contains("/path/to/missing.rs"));
88 assert!(msg.contains("file not found"));
89 }
90
91 #[test]
92 fn test_config_error_display() {
93 let err = Error::config("invalid severity level");
94 let msg = err.to_string();
95 assert!(msg.contains("invalid severity level"));
96 }
97
98 #[test]
99 fn test_io_generic_from() {
100 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
101 let err: Error = io_err.into();
102 assert!(matches!(err, Error::IoGeneric(_)));
103 }
104}