1mod async_connection;
2mod sync_wrapper;
3#[cfg(feature = "wal-pool")]
4mod wal_pool;
5
6pub use self::async_connection::AsyncConnection;
7pub use self::async_connection::AsyncConnectionBuilder;
8pub use self::sync_wrapper::SyncWrapper;
9#[cfg(feature = "wal-pool")]
10pub use self::wal_pool::WalPool;
11#[cfg(feature = "wal-pool")]
12pub use self::wal_pool::WalPoolBuilder;
13pub use rusqlite;
14
15#[non_exhaustive]
17#[derive(Debug)]
18pub enum Error {
19 Rusqlite(rusqlite::Error),
21
22 Aborted,
26
27 AccessPanic(SyncWrapper<Box<dyn std::any::Any + Send + 'static>>),
32
33 #[cfg(feature = "wal-pool")]
35 InvalidJournalMode(String),
36
37 Generic(&'static str),
39}
40
41impl std::fmt::Display for Error {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 Self::Rusqlite(error) => error.fmt(f),
45 Self::Aborted => "the connection thread aborted the request".fmt(f),
46 Self::AccessPanic(_) => "a connection access panicked".fmt(f),
47 Self::Generic(message) => message.fmt(f),
48
49 #[cfg(feature = "wal-pool")]
50 Self::InvalidJournalMode(journal_mode) => {
51 write!(
52 f,
53 "failed to set journal_mode to WAL, journal_mode is \"{journal_mode}\""
54 )
55 }
56 }
57 }
58}
59
60impl std::error::Error for Error {
61 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
62 match self {
63 Self::Rusqlite(error) => Some(error),
64 Self::Aborted => None,
65 Self::AccessPanic(_) => None,
66 Self::Generic(_) => None,
67
68 #[cfg(feature = "wal-pool")]
69 Self::InvalidJournalMode(_) => None,
70 }
71 }
72}
73
74impl From<rusqlite::Error> for Error {
75 fn from(error: rusqlite::Error) -> Self {
76 Self::Rusqlite(error)
77 }
78}
79
80#[cfg(test)]
81mod test {
82 use super::*;
83 use std::path::Path;
84
85 pub const fn _assert_send<T>()
86 where
87 T: Send,
88 {
89 }
90 pub const fn _assert_sync<T>()
91 where
92 T: Sync,
93 {
94 }
95 pub const fn _assert_static_lifetime<T>()
96 where
97 T: 'static,
98 {
99 }
100
101 const _ERROR_IS_SEND: () = _assert_send::<Error>();
102 const _ERROR_IS_SYNC: () = _assert_sync::<Error>();
103 const _ERROR_HAS_A_STATIC_LIFETIME: () = _assert_static_lifetime::<Error>();
104
105 #[tokio::test]
106 async fn sanity() {
107 let temp_path = Path::new("test-temp");
108 std::fs::create_dir_all(temp_path).expect("failed to create temp dir");
109
110 let connection_error = AsyncConnection::builder()
111 .open(".")
112 .await
113 .expect_err("connection should not open on a directory");
114 assert!(matches!(connection_error, Error::Rusqlite(_)));
115
116 let connection_path = temp_path.join("sanity.db");
117 match std::fs::remove_file(&connection_path) {
118 Ok(()) => {}
119 Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
120 Err(error) => {
121 panic!("failed to remove old database: {error:?}");
122 }
123 }
124
125 let connection = AsyncConnection::builder()
126 .open(connection_path)
127 .await
128 .expect("connection should be open");
129
130 let _connection1 = connection.clone();
132
133 let panic_error = connection
135 .access(|_connection| panic!("the connection should survive the panic"))
136 .await
137 .expect_err("the access should have failed");
138
139 assert!(matches!(panic_error, Error::AccessPanic(_)));
140
141 let setup_sql = "PRAGMA foreign_keys = ON; CREATE TABLE USERS (id INTEGER PRIMARY KEY, first_name TEXT NOT NULL, last_name TEXT NOT NULL) STRICT;";
142 connection
143 .access(|connection| connection.execute_batch(setup_sql))
144 .await
145 .expect("failed to create tables")
146 .expect("failed to execute");
147
148 connection
149 .close()
150 .await
151 .expect("an error occured while closing");
152 }
153}