Skip to main content

bvr/
error.rs

1use std::path::PathBuf;
2
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum BvrError {
7    #[error("io error: {0}")]
8    Io(#[from] std::io::Error),
9
10    #[error("json parse error: {0}")]
11    Json(#[from] serde_json::Error),
12
13    #[error("sqlite error: {0}")]
14    Sqlite(#[from] rusqlite::Error),
15
16    #[error("beads directory not found: {0}")]
17    MissingBeadsDir(PathBuf),
18
19    #[error("no beads JSONL file found in {0}")]
20    MissingBeadsFile(PathBuf),
21
22    #[error("invalid issue data: {0}")]
23    InvalidIssue(String),
24
25    #[error("invalid argument: {0}")]
26    InvalidArgument(String),
27
28    #[error("tui runtime error: {0}")]
29    Tui(String),
30}
31
32pub type Result<T> = std::result::Result<T, BvrError>;
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn io_error_display_includes_source() {
40        let err = BvrError::Io(std::io::Error::new(std::io::ErrorKind::NotFound, "gone"));
41        assert!(err.to_string().contains("io error"));
42        assert!(err.to_string().contains("gone"));
43    }
44
45    #[test]
46    fn json_error_converts_from_serde() {
47        let bad_json = "{{invalid";
48        let result: std::result::Result<serde_json::Value, _> = serde_json::from_str(bad_json);
49        let serde_err = result.unwrap_err();
50        let err: BvrError = serde_err.into();
51        assert!(err.to_string().contains("json parse error"));
52    }
53
54    #[test]
55    fn sqlite_error_converts_from_rusqlite() {
56        let conn = rusqlite::Connection::open_in_memory().expect("open in-memory sqlite");
57        let sqlite_err = conn
58            .execute("SELECT definitely_not_valid_sql", [])
59            .expect_err("invalid SQL should fail");
60        let err: BvrError = sqlite_err.into();
61        assert!(err.to_string().contains("sqlite error"));
62    }
63
64    #[test]
65    fn missing_beads_dir_shows_path() {
66        let err = BvrError::MissingBeadsDir(PathBuf::from("/tmp/nope"));
67        assert!(err.to_string().contains("/tmp/nope"));
68    }
69
70    #[test]
71    fn missing_beads_file_shows_path() {
72        let err = BvrError::MissingBeadsFile(PathBuf::from("/tmp/.beads"));
73        assert!(err.to_string().contains("/tmp/.beads"));
74    }
75
76    #[test]
77    fn invalid_issue_shows_detail() {
78        let err = BvrError::InvalidIssue("bad id".to_string());
79        assert!(err.to_string().contains("bad id"));
80    }
81
82    #[test]
83    fn invalid_argument_shows_detail() {
84        let err = BvrError::InvalidArgument("--unknown".to_string());
85        assert!(err.to_string().contains("--unknown"));
86    }
87
88    #[test]
89    fn tui_error_shows_detail() {
90        let err = BvrError::Tui("render crash".to_string());
91        assert!(err.to_string().contains("render crash"));
92    }
93
94    #[test]
95    fn error_debug_format_works() {
96        let err = BvrError::InvalidArgument("test".to_string());
97        let debug_str = format!("{err:?}");
98        assert!(debug_str.contains("InvalidArgument"));
99    }
100}