ipfs_sqlite_block_store/
error.rs

1use derive_more::Display;
2use std::error::Error;
3
4#[derive(Debug, Display)]
5pub enum BlockStoreError {
6    /// Error when interacting with the sqlite database
7    #[display(
8        fmt = "sqlite error while {}: {} caused by {:?}",
9        _1,
10        _0,
11        "_0.source().map(std::string::ToString::to_string)"
12    )]
13    SqliteError(rusqlite::Error, &'static str),
14    /// Error convering from a cid to a fixed sized representation.
15    /// This can be caused by hashes with more than 32 bytes of size
16    #[display(fmt = "error packing a CID into fixed size: {}", _0)]
17    CidError(libipld::cid::Error),
18    /// Error when converting i64 from sqlite to u64.
19    /// This is unlikely to ever happen.
20    #[display(
21        fmt = "DB corrupted, got unsuitable integer value while {}: {}",
22        _1,
23        _0
24    )]
25    TryFromIntError(std::num::TryFromIntError, &'static str),
26    #[display(fmt = "cannot open additional connection for in-memory DB")]
27    NoAdditionalInMemory,
28    /// Other error
29    Other(anyhow::Error),
30}
31
32impl From<anyhow::Error> for BlockStoreError {
33    fn from(v: anyhow::Error) -> Self {
34        Self::Other(v)
35    }
36}
37
38impl From<libipld::cid::Error> for BlockStoreError {
39    fn from(v: libipld::cid::Error) -> Self {
40        Self::CidError(v)
41    }
42}
43
44impl std::error::Error for BlockStoreError {
45    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
46        match self {
47            BlockStoreError::SqliteError(_e, _) => None,
48            BlockStoreError::CidError(e) => Some(e),
49            BlockStoreError::TryFromIntError(e, _) => Some(e),
50            BlockStoreError::Other(e) => AsRef::<dyn Error>::as_ref(e).source(),
51            BlockStoreError::NoAdditionalInMemory => None,
52        }
53    }
54}
55
56pub type Result<T> = std::result::Result<T, BlockStoreError>;
57
58pub(crate) trait Context {
59    type Output;
60    fn ctx(self, s: &'static str) -> Self::Output;
61}
62
63impl<T> Context for std::result::Result<T, rusqlite::Error> {
64    type Output = crate::Result<T>;
65
66    fn ctx(self, s: &'static str) -> Self::Output {
67        self.map_err(|e| BlockStoreError::SqliteError(e, s))
68    }
69}
70
71impl<T> Context for std::result::Result<T, std::num::TryFromIntError> {
72    type Output = crate::Result<T>;
73
74    fn ctx(self, s: &'static str) -> Self::Output {
75        self.map_err(|e| BlockStoreError::TryFromIntError(e, s))
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::Context;
82    use crate::BlockStoreError;
83    use anyhow::Context as _;
84
85    #[test]
86    fn show() {
87        let sqlite = std::result::Result::<(), _>::Err(rusqlite::Error::SqliteFailure(
88            rusqlite::ffi::Error::new(517),
89            Some("sql string".to_owned()),
90        ));
91
92        assert_eq!(format!("x {:?}", sqlite), "x Err(SqliteFailure(Error { code: DatabaseBusy, extended_code: 517 }, Some(\"sql string\")))");
93        if let Err(ref sqlite) = sqlite {
94            assert_eq!(format!("x {}", sqlite), "x sql string");
95            assert_eq!(format!("x {:#}", sqlite), "x sql string");
96        }
97
98        let db = sqlite.ctx("first");
99
100        assert_eq!(format!("x {:?}", db), "x Err(SqliteError(SqliteFailure(Error { code: DatabaseBusy, extended_code: 517 }, Some(\"sql string\")), \"first\"))");
101        if let Err(ref db) = db {
102            assert_eq!(
103                format!("x {}", db),
104                "x sqlite error while first: sql string caused by \
105                Some(\"Error code 517: \
106                    Cannot promote read transaction to write transaction because of writes by another connection\")"
107            );
108            assert_eq!(
109                format!("x {:#}", db),
110                "x sqlite error while first: sql string caused by \
111                Some(\"Error code 517: \
112                    Cannot promote read transaction to write transaction because of writes by another connection\")"
113            );
114        }
115
116        let app = db.context("second").map_err(BlockStoreError::from);
117
118        assert_eq!(
119            format!("x {:?}", app),
120            r#"x Err(Other(second
121
122Caused by:
123    sqlite error while first: sql string caused by Some("Error code 517: Cannot promote read transaction to write transaction because of writes by another connection")))"#
124        );
125        if let Err(ref app) = app {
126            assert_eq!(format!("x {}", app), "x second");
127            assert_eq!(format!("x {:#}", app), "x second: \
128                sqlite error while first: \
129                sql string caused by Some(\"Error code 517: \
130                Cannot promote read transaction to write transaction because of writes by another connection\")");
131        }
132    }
133
134    #[test]
135    fn double() {
136        let e = BlockStoreError::Other(anyhow::anyhow!("hello"));
137        assert_eq!(format!("{}", e), "hello");
138        assert_eq!(format!("{:#}", e), "hello");
139
140        let e = Result::<(), _>::Err(e).context("world").unwrap_err();
141        assert_eq!(format!("{:#}", e), "world: hello");
142
143        assert_eq!(e.to_string(), "world");
144        let e = e.source().unwrap();
145        assert_eq!(e.to_string(), "hello");
146        assert!(e.source().is_none());
147    }
148}