1use std::{fmt, io};
2
3#[derive(Clone, Debug)]
5pub struct Error {
6 kind: ErrorKind,
7 message: String,
8}
9
10impl Error {
11 pub(crate) fn from_code(code: i32, message: Option<String>) -> Self {
12 Self {
13 kind: ErrorKind::from_code(code),
14 message: message.unwrap_or_else(|| ErrorKind::from_code(code).to_string()),
15 }
16 }
17
18 pub(crate) fn new(kind: ErrorKind, message: String) -> Self {
19 Self {
20 kind,
21 message,
22 }
23 }
24
25 pub(crate) fn background_task_failed() -> Self {
26 Self::new(ErrorKind::Generic, String::from("background task failed"))
27 }
28
29 pub fn kind(&self) -> ErrorKind {
31 self.kind
32 }
33
34 pub fn message(&self) -> &str {
36 &self.message
37 }
38}
39
40impl fmt::Display for Error {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 write!(f, "sqlite error: {} ({})", self.kind(), self.message())
43 }
44}
45
46impl std::error::Error for Error {}
47
48impl From<Error> for io::Error {
49 fn from(v: Error) -> io::Error {
50 io::Error::new(v.kind.into(), v)
51 }
52}
53
54impl From<ErrorKind> for Error {
55 fn from(kind: ErrorKind) -> Self {
56 Self {
57 kind,
58 message: kind.to_string(),
59 }
60 }
61}
62
63macro_rules! error_kind {
64 (
65 $(
66 $(#[doc = $doc:expr])*
67 #[message = $message:expr]
68 $(#[io = $error_kind:ident])?
69 $variant:ident $(= $code:ident)?,
70 )*
71 ) => {
72 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
74 #[non_exhaustive]
75 pub enum ErrorKind {
76 $(
77 $(#[doc = $doc])*
78 $variant,
79 )*
80 }
81
82 impl ErrorKind {
83 pub const fn from_code(code: i32) -> Self {
85 match code & 0xFF {
86 $(
87 $(libsqlite3_sys::$code => Self::$variant,)?
88 )*
89 _ => Self::Generic,
90 }
91 }
92
93 pub const fn code(self) -> Option<i32> {
95 match self {
96 $(
97 $(Self::$variant => Some(libsqlite3_sys::$code),)?
98 )*
99 _ => None,
100 }
101 }
102 }
103
104 impl fmt::Display for ErrorKind {
105 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106 match self {
107 $(
108 Self::$variant => f.write_str($message),
109 )*
110 }
111 }
112 }
113
114 impl From<ErrorKind> for io::ErrorKind {
115 fn from(v: ErrorKind) -> io::ErrorKind {
116 match v {
117 $(
118 $(ErrorKind::$variant => io::ErrorKind::$error_kind,)?
119 )*
120 _ => io::ErrorKind::Other,
121 }
122 }
123 }
124 };
125}
126
127error_kind! {
128 #[message = "generic error"]
130 Generic = SQLITE_ERROR,
131
132 #[message = "internal malfunction"]
134 InternalMalfunction = SQLITE_INTERNAL,
135
136 #[message = "permission denied"]
138 #[io = PermissionDenied]
139 PermissionDenied = SQLITE_PERM,
140
141 #[message = "operation aborted"]
143 #[io = Interrupted]
144 OperationAborted = SQLITE_ABORT,
145
146 #[message = "database busy"]
148 DatabaseBusy = SQLITE_BUSY,
149
150 #[message = "database locked"]
153 DatabaseLocked = SQLITE_LOCKED,
154
155 #[message = "out of memory"]
157 #[io = OutOfMemory]
158 OutOfMemory = SQLITE_NOMEM,
159
160 #[message = "database is read only"]
162 #[io = PermissionDenied]
163 ReadOnly = SQLITE_READONLY,
164
165 #[message = "operation interrupted"]
167 #[io = Interrupted]
168 OperationInterrupted = SQLITE_INTERRUPT,
169
170 #[message = "i/o error"]
172 SystemIoFailure = SQLITE_IOERR,
173
174 #[message = "corrupted database"]
176 #[io = InvalidData]
177 DatabaseCorrupt = SQLITE_CORRUPT,
178
179 #[message = "database not found"]
181 #[io = NotFound]
182 NotFound = SQLITE_NOTFOUND,
183
184 #[message = "disk full"]
186 DiskFull = SQLITE_FULL,
187
188 #[message = "cannot open database"]
190 CannotOpen = SQLITE_CANTOPEN,
191
192 #[message = "file locking protocol error"]
194 FileLockingProtocolFailed = SQLITE_PROTOCOL,
195
196 #[message = "schema has changed"]
198 #[io = InvalidData]
199 SchemaChanged = SQLITE_SCHEMA,
200
201 #[message = "string or blob is too large"]
203 #[io = InvalidData]
204 TooLarge = SQLITE_TOOBIG,
205
206 #[message = "constraint violation"]
208 #[io = InvalidData]
209 ConstraintViolation = SQLITE_CONSTRAINT,
210
211 #[message = "datatype mismatch"]
213 #[io = InvalidData]
214 DatatypeMismatch = SQLITE_MISMATCH,
215
216 #[message = "library misuse"]
218 #[io = InvalidInput]
219 Misuse = SQLITE_MISUSE,
220
221 #[message = "lfs not supported"]
223 #[io = Unsupported]
224 LfsUnsupported = SQLITE_NOLFS,
225
226 #[message = "unauthorized statement"]
228 #[io = PermissionDenied]
229 Unauthorized = SQLITE_AUTH,
230
231 #[message = "out of range"]
233 #[io = InvalidInput]
234 OutOfRange = SQLITE_RANGE,
235
236 #[message = "not a database"]
238 #[io = NotFound]
239 NotADatabase = SQLITE_NOTADB,
240
241 #[message = "invalid database path"]
243 #[io = InvalidInput]
244 InvalidPath,
245
246 #[message = "connection was closed"]
248 #[io = BrokenPipe]
249 ConnectionClosed,
250
251 #[message = "too many statements"]
253 #[io = InvalidInput]
254 TooManyStatements,
255}