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 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
use asyncified::Asyncified;
use std::path::Path;
// re-export rusqlite types.
pub use rusqlite;
/// A result returned from calls to [`Connection`].
pub type Result<T> = std::result::Result<T, Error>;
/// A handle which allows access to the underlying [`rusqlite::Connection`]
/// via [`Connection::call()`].
#[derive(Debug, Clone)]
pub struct Connection {
// None if connection is closed, else Some(connection).
conn: Asyncified<Option<rusqlite::Connection>>
}
impl Connection {
/// Open a new connection to an SQLite database. If a database does not exist at the
/// path, one is created.
///
/// # Failure
///
/// Will return `Err` if `path` cannot be converted to a C-compatible string
/// or if the underlying SQLite open call fails.
pub async fn open<P: AsRef<Path>>(path: P) -> Result<Connection> {
let path = path.as_ref().to_owned();
let conn = Asyncified::new(move || rusqlite::Connection::open(path).map(Some)).await?;
Ok(Connection { conn })
}
/// Open a new connection to an in-memory SQLite database.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite open call fails.
pub async fn open_in_memory() -> Result<Connection> {
let conn = Asyncified::new(|| rusqlite::Connection::open_in_memory().map(Some)).await?;
Ok(Connection { conn })
}
/// Open a new connection to a SQLite database.
///
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// flag combinations.
///
/// # Failure
///
/// Will return `Err` if `path` cannot be converted to a C-compatible
/// string or if the underlying SQLite open call fails.
pub async fn open_with_flags<P: AsRef<Path>>(path: P, flags: rusqlite::OpenFlags) -> Result<Connection> {
let path = path.as_ref().to_owned();
let conn = Asyncified::new(move || rusqlite::Connection::open_with_flags(path, flags).map(Some)).await?;
Ok(Connection { conn })
}
/// Open a new connection to a SQLite database using the specific flags and
/// vfs name.
///
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// flag combinations.
///
/// # Failure
///
/// Will return `Err` if either `path` or `vfs` cannot be converted to a
/// C-compatible string or if the underlying SQLite open call fails.
pub async fn open_with_flags_and_vfs<P: AsRef<Path>>(
path: P,
flags: rusqlite::OpenFlags,
vfs: &str,
) -> Result<Connection> {
let path = path.as_ref().to_owned();
let vfs = vfs.to_owned();
let conn = Asyncified::new(move || rusqlite::Connection::open_with_flags_and_vfs(path, flags, &vfs).map(Some)).await?;
Ok(Connection { conn })
}
/// Open a new connection to an in-memory SQLite database.
///
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// flag combinations.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite open call fails.
pub async fn open_in_memory_with_flags(flags: rusqlite::OpenFlags) -> Result<Connection> {
Connection::open_with_flags(":memory:", flags).await
}
/// Open a new connection to an in-memory SQLite database using the specific
/// flags and vfs name.
///
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// flag combinations.
///
/// # Failure
///
/// Will return `Err` if `vfs` cannot be converted to a C-compatible
/// string or if the underlying SQLite open call fails.
pub async fn open_in_memory_with_flags_and_vfs(flags: rusqlite::OpenFlags, vfs: &str) -> Result<Connection> {
Connection::open_with_flags_and_vfs(":memory:", flags, vfs).await
}
/// Close the SQLite connection.
///
/// This is functionally equivalent to the `Drop` implementation for
/// [`Connection`] except that on failure, it returns the error. Unlike
/// the [`rusqlite`] version of this method, it does not need to consume
/// `self`.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub async fn close(&self) -> Result<()> {
self.conn.call(|conn| {
match conn.take() {
Some(c) => {
match c.close() {
Ok(_) => Ok(()),
Err((c, err)) => {
// close failed; replace the connection and
// return the error.
*conn = Some(c);
Err(Error::Rusqlite(err))
}
}
},
// Already closed!
None => Err(Error::ConnectionClosed)
}
}).await
}
/// Run some arbitrary function against the [`rusqlite::Connection`] and return the result.
///
/// # Failure
///
/// Will return Err if the connection is closed, or if the provided function returns an error.
pub async fn call<R, F>(&self, f: F) -> Result<R>
where
R: Send + 'static,
F: Send + 'static + FnOnce(&mut rusqlite::Connection) -> rusqlite::Result<R>
{
self.conn.call(|conn| {
match conn {
Some(conn) => Ok(f(conn)?),
None => Err(Error::ConnectionClosed)
}
}).await
}
}
/// Errors that can be emitted from this library.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// The connection to SQLite has been closed.
ConnectionClosed,
/// A `rusqlite` error occured.
Rusqlite(rusqlite::Error),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::ConnectionClosed => write!(f, "Connection closed"),
Error::Rusqlite(e) => write!(f, "Rusqlite error: {e}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::ConnectionClosed => None,
Error::Rusqlite(e) => Some(e),
}
}
}
impl From<rusqlite::Error> for Error {
fn from(value: rusqlite::Error) -> Self {
Error::Rusqlite(value)
}
}