1#[derive(Debug)]
4pub enum Error {
5 NotFound,
7 Invalid,
9 Db(rusqlite::Error),
11 Unavailable(String),
16 Other(String),
19}
20
21impl From<rusqlite::Error> for Error {
22 fn from(e: rusqlite::Error) -> Self {
23 Error::Db(e)
24 }
25}
26
27impl From<anyhow::Error> for Error {
28 fn from(e: anyhow::Error) -> Self {
29 Error::Other(e.to_string())
30 }
31}
32
33impl std::fmt::Display for Error {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match self {
36 Error::NotFound => write!(f, "not found"),
37 Error::Invalid => write!(f, "invalid input"),
38 Error::Db(e) => write!(f, "database error: {e}"),
39 Error::Unavailable(msg) => write!(f, "library unavailable: {msg}"),
40 Error::Other(msg) => write!(f, "{msg}"),
41 }
42 }
43}
44
45impl std::error::Error for Error {}
46
47pub type Result<T> = std::result::Result<T, Error>;
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn display_matches_each_variant() {
55 assert_eq!(Error::NotFound.to_string(), "not found");
56 assert_eq!(Error::Invalid.to_string(), "invalid input");
57 assert_eq!(
58 Error::Unavailable("root gone".to_string()).to_string(),
59 "library unavailable: root gone"
60 );
61 assert_eq!(Error::Other("boom".to_string()).to_string(), "boom");
62 }
63
64 #[test]
65 fn display_for_db_variant_includes_the_underlying_error() {
66 let e = Error::Db(rusqlite::Error::QueryReturnedNoRows);
67 assert!(e.to_string().starts_with("database error: "));
68 assert!(e.to_string().contains("Query returned no rows"));
69 }
70
71 #[test]
72 fn from_rusqlite_error_wraps_as_db_variant() {
73 let e: Error = rusqlite::Error::QueryReturnedNoRows.into();
74 assert!(matches!(e, Error::Db(_)));
75 }
76
77 #[test]
78 fn from_anyhow_error_wraps_message_as_other_variant() {
79 let e: Error = anyhow::anyhow!("something broke").into();
80 match e {
81 Error::Other(msg) => assert_eq!(msg, "something broke"),
82 other => panic!("expected Error::Other, got {other:?}"),
83 }
84 }
85}