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