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