agtrace_index/
error.rs

1use std::fmt;
2
3/// Result type for agtrace-index operations
4pub type Result<T> = std::result::Result<T, Error>;
5
6/// Error types that can occur in the index layer
7#[derive(Debug)]
8pub enum Error {
9    /// Database operation failed
10    Database(rusqlite::Error),
11
12    /// IO operation failed
13    Io(std::io::Error),
14
15    /// Query-specific error (invalid input, not found, etc.)
16    Query(String),
17}
18
19impl fmt::Display for Error {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            Error::Database(err) => {
23                let msg = err.to_string();
24                // Detect schema mismatch errors and provide actionable hint
25                if msg.contains("no such column") || msg.contains("no such table") {
26                    write!(
27                        f,
28                        "Database schema mismatch: {}. Please restart the CLI to auto-migrate.",
29                        msg
30                    )
31                } else {
32                    write!(f, "Database error: {}", err)
33                }
34            }
35            Error::Io(err) => write!(f, "IO error: {}", err),
36            Error::Query(msg) => write!(f, "Query error: {}", msg),
37        }
38    }
39}
40
41impl std::error::Error for Error {
42    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
43        match self {
44            Error::Database(err) => Some(err),
45            Error::Io(err) => Some(err),
46            Error::Query(_) => None,
47        }
48    }
49}
50
51impl From<rusqlite::Error> for Error {
52    fn from(err: rusqlite::Error) -> Self {
53        Error::Database(err)
54    }
55}
56
57impl From<std::io::Error> for Error {
58    fn from(err: std::io::Error) -> Self {
59        Error::Io(err)
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn test_schema_mismatch_error_message() {
69        // Simulate a "no such column" error
70        let sqlite_err = rusqlite::Error::SqliteFailure(
71            rusqlite::ffi::Error::new(1),
72            Some("no such column: parent_session_id".to_string()),
73        );
74        let err = Error::Database(sqlite_err);
75        let msg = err.to_string();
76
77        assert!(msg.contains("Database schema mismatch"));
78        assert!(msg.contains("Please restart the CLI to auto-migrate"));
79    }
80
81    #[test]
82    fn test_regular_database_error_message() {
83        let sqlite_err = rusqlite::Error::SqliteFailure(
84            rusqlite::ffi::Error::new(1),
85            Some("UNIQUE constraint failed".to_string()),
86        );
87        let err = Error::Database(sqlite_err);
88        let msg = err.to_string();
89
90        assert!(msg.starts_with("Database error:"));
91        assert!(!msg.contains("restart"));
92    }
93}