diff options
Diffstat (limited to '')
| -rw-r--r-- | wyrd_sqlite/src/error.rs | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/wyrd_sqlite/src/error.rs b/wyrd_sqlite/src/error.rs new file mode 100644 index 0000000..281862f --- /dev/null +++ b/wyrd_sqlite/src/error.rs @@ -0,0 +1,84 @@ +use std::{ + ffi::{CStr, NulError, c_int}, + fmt::{self, Display, Formatter}, + str::Utf8Error, +}; + +use crate::{FromSql, ffi}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Sqlite(#[from] SqliteError), + #[error("input contained no SQL")] + EmptyStatement, + #[error(transparent)] + Nul(#[from] NulError), + #[error(transparent)] + Utf8(#[from] Utf8Error), + #[error("invalid column index {0}")] + InvalidColumn(c_int), + #[error("execute returned results")] + ExecReturnedRows, + #[error("multiple statements provided")] + MultipleStatements, +} + +#[derive(Debug)] +pub struct SqliteError { + code: c_int, + msg: Option<String>, +} + +impl Error { + pub(crate) fn from_code(code: c_int) -> Self { + SqliteError { code, msg: None }.into() + } + + pub(crate) fn from_db(db: *mut ffi::sqlite3) -> Self { + // SAFETY: sqlite has checks to handle if db is null or dangling, so these shouldn't cause + // ub for any input + let (code, c_msg) = unsafe { (ffi::sqlite3_errcode(db), ffi::sqlite3_errmsg(db)) }; + + let msg = if c_msg.is_null() { + None + } else { + Some( + // SAFETY: as long as c_msg is non-null, sqlite shouldn't be giving us bad strings + unsafe { CStr::from_ptr(c_msg) } + .to_string_lossy() + .to_string(), + ) + }; + + SqliteError { code, msg }.into() + } +} + +fn errstr(code: c_int) -> &'static str { + // SAFETY: `sqlite3_errstr` always returns a valid null-terminated static string + unsafe { CStr::from_ptr(ffi::sqlite3_errstr(code)) } + .to_str() + .expect("sqlite errors should be valid utf8") +} + +impl Display for SqliteError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let msg = self.msg.as_deref().unwrap_or(errstr(self.code)); + write!(f, "{msg} ({})", self.code) + } +} + +impl std::error::Error for SqliteError {} + +pub type Result<T> = std::result::Result<T, Error>; + +#[derive(Debug, thiserror::Error)] +pub enum GetError<T: FromSql> { + #[error(transparent)] + Sqlite(#[from] Error), + #[error(transparent)] + FromSql(T::Error), +} + +pub type GetResult<T> = std::result::Result<T, GetError<T>>; |