cargo_perf/
error.rs

1//! Custom error types for cargo-perf.
2//!
3//! Provides structured error handling with clear error categories.
4
5use std::path::PathBuf;
6use thiserror::Error;
7
8/// A type alias for `Result<T, Error>`.
9pub type Result<T> = std::result::Result<T, Error>;
10
11/// Errors that can occur during cargo-perf operation.
12#[derive(Debug, Error)]
13pub enum Error {
14    /// Failed to parse a Rust source file.
15    #[error("Failed to parse {path}: {message}")]
16    Parse {
17        /// Path to the file that failed to parse.
18        path: PathBuf,
19        /// Description of the parse error.
20        message: String,
21    },
22
23    /// Failed to read or access a file.
24    #[error("IO error for {path}: {source}")]
25    Io {
26        /// Path to the file that caused the error.
27        path: PathBuf,
28        /// Underlying IO error.
29        #[source]
30        source: std::io::Error,
31    },
32
33    /// Failed to load or parse configuration.
34    #[error("Configuration error: {message}")]
35    Config {
36        /// Description of the configuration error.
37        message: String,
38    },
39
40    /// Generic IO error without path context.
41    #[error("IO error: {0}")]
42    IoGeneric(#[from] std::io::Error),
43}
44
45impl Error {
46    /// Create a parse error for a specific file.
47    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    /// Create an IO error for a specific file.
55    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    /// Create a configuration error.
63    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}