1pub type Result<T, E = EngineError> = std::result::Result<T, E>;
4
5#[derive(Debug, thiserror::Error)]
6pub enum EngineError {
7 #[error("{0}")]
9 InvalidArgument(String),
10 #[error("{0}")]
12 Failed(String),
13 #[error("{0}")]
15 Busy(String),
16 #[error(transparent)]
17 Sqlite(#[from] rusqlite::Error),
18 #[error(transparent)]
19 Io(#[from] std::io::Error),
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24#[repr(u32)]
25pub enum ErrorCode {
26 InvalidArgument = 1,
27 Failed = 2,
28 Storage = 3,
30 Busy = 6,
31}
32
33impl ErrorCode {
34 pub const fn value(self) -> u32 {
35 self as u32
36 }
37}
38
39impl EngineError {
40 pub fn invalid(message: impl Into<String>) -> Self {
41 Self::InvalidArgument(message.into())
42 }
43
44 pub fn failed(message: impl Into<String>) -> Self {
45 Self::Failed(message.into())
46 }
47
48 pub fn busy(message: impl Into<String>) -> Self {
49 Self::Busy(message.into())
50 }
51
52 pub fn code(&self) -> ErrorCode {
54 match self {
55 Self::InvalidArgument(_) => ErrorCode::InvalidArgument,
56 Self::Failed(_) => ErrorCode::Failed,
57 Self::Busy(_) => ErrorCode::Busy,
58 Self::Sqlite(_) | Self::Io(_) => ErrorCode::Storage,
59 }
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
69 fn error_codes_are_frozen() {
70 let cases = [
71 (EngineError::invalid("x"), 1),
72 (EngineError::failed("x"), 2),
73 (EngineError::Sqlite(rusqlite::Error::InvalidQuery), 3),
74 (EngineError::Io(std::io::Error::other("x")), 3),
75 (EngineError::busy("x"), 6),
76 ];
77 for (error, code) in cases {
78 assert_eq!(error.code().value(), code, "{error:?}");
79 }
80 }
81
82 #[test]
83 fn codes_keep_the_message() {
84 let error = EngineError::busy(crate::diagnostics::DICTIONARY_MAINTENANCE_BUSY);
85 assert_eq!(error.to_string(), "Dictionary maintenance busy");
86 assert_eq!(error.code(), ErrorCode::Busy);
87 }
88}