Skip to main content

argus_core/
error.rs

1use std::path::PathBuf;
2
3use miette::Diagnostic;
4
5/// Errors that can occur across the Argus platform.
6///
7/// Each variant wraps a specific error domain. Library crates use this type
8/// directly; the binary crate converts to `miette::Report` at the boundary.
9///
10/// # Examples
11///
12/// ```
13/// use argus_core::ArgusError;
14///
15/// let err = ArgusError::Config("missing API key".into());
16/// assert!(err.to_string().contains("missing API key"));
17/// ```
18#[derive(Debug, thiserror::Error, Diagnostic)]
19pub enum ArgusError {
20    /// Filesystem I/O failure.
21    #[error("IO error: {0}")]
22    #[diagnostic(code(argus::io), help("Check file permissions and paths"))]
23    Io(#[from] std::io::Error),
24
25    /// Invalid or missing configuration.
26    #[error("Configuration error: {0}")]
27    #[diagnostic(
28        code(argus::config),
29        help("Run `argus init` to create a default config, or check .argus.toml syntax")
30    )]
31    Config(String),
32
33    /// Git operation failure.
34    #[error("Git error: {0}")]
35    #[diagnostic(code(argus::git), help("Make sure you're inside a git repository"))]
36    Git(String),
37
38    /// GitHub API failure.
39    #[error("GitHub API error: {0}")]
40    #[diagnostic(
41        code(argus::github),
42        help("Check your GITHUB_TOKEN permissions and network connection")
43    )]
44    GitHub(String),
45
46    /// Source code parsing failure.
47    #[error("Parse error: {0}")]
48    #[diagnostic(
49        code(argus::parse),
50        help("Check that the file is valid source code in a supported language")
51    )]
52    Parse(String),
53
54    /// LLM API or response error.
55    #[error("LLM error: {0}")]
56    #[diagnostic(code(argus::llm), help("Check your API key and provider config in .argus.toml. Run `argus doctor` to diagnose."))]
57    Llm(String),
58
59    /// JSON serialization / deserialization failure.
60    #[error("Serialization error: {0}")]
61    #[diagnostic(code(argus::serde))]
62    Serialization(#[from] serde_json::Error),
63
64    /// TOML deserialization failure.
65    #[error("TOML parse error: {0}")]
66    #[diagnostic(
67        code(argus::toml),
68        help("Check .argus.toml syntax — run `argus init` to generate a fresh one")
69    )]
70    Toml(#[from] toml::de::Error),
71
72    /// A required file was not found.
73    #[error("File not found: {}", .0.display())]
74    #[diagnostic(
75        code(argus::file_not_found),
76        help("Check the path exists and is readable")
77    )]
78    FileNotFound(PathBuf),
79
80    /// Embedding API error.
81    #[error("Embedding error: {0}")]
82    #[diagnostic(
83        code(argus::embedding),
84        help("Check your embedding provider API key. Run `argus doctor` to diagnose.")
85    )]
86    Embedding(String),
87
88    /// Database operation failure.
89    #[error("Database error: {0}")]
90    #[diagnostic(
91        code(argus::database),
92        help("Try deleting .argus/index.db and re-indexing")
93    )]
94    Database(String),
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn io_error_converts() {
103        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "gone");
104        let err: ArgusError = io_err.into();
105        assert!(err.to_string().contains("gone"));
106    }
107
108    #[test]
109    fn config_error_displays_message() {
110        let err = ArgusError::Config("bad value".into());
111        assert_eq!(err.to_string(), "Configuration error: bad value");
112    }
113
114    #[test]
115    fn file_not_found_shows_path() {
116        let err = ArgusError::FileNotFound(PathBuf::from("/tmp/missing.rs"));
117        assert!(err.to_string().contains("/tmp/missing.rs"));
118    }
119}