1use std::fmt;
2
3pub type Result<T> = std::result::Result<T, Error>;
5
6#[derive(Debug)]
8pub enum Error {
9 Database(rusqlite::Error),
11
12 Io(std::io::Error),
14
15 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 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 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}