use std::{fmt, io};
#[derive(Clone, Debug)]
pub struct Error {
kind: ErrorKind,
message: String,
}
impl Error {
pub(crate) fn from_code(code: i32, message: Option<String>) -> Self {
Self {
kind: ErrorKind::from_code(code),
message: message.unwrap_or_else(|| ErrorKind::from_code(code).to_string()),
}
}
pub(crate) fn new(kind: ErrorKind, message: String) -> Self {
Self {
kind,
message,
}
}
pub(crate) fn background_task_failed() -> Self {
Self::new(ErrorKind::Generic, String::from("background task failed"))
}
pub fn kind(&self) -> ErrorKind {
self.kind
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "sqlite error: {} ({})", self.kind(), self.message())
}
}
impl std::error::Error for Error {}
impl From<Error> for io::Error {
fn from(v: Error) -> io::Error {
io::Error::new(v.kind.into(), v)
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Self {
kind,
message: kind.to_string(),
}
}
}
macro_rules! error_kind {
(
$(
$(#[doc = $doc:expr])*
#[message = $message:expr]
$(#[io = $error_kind:ident])?
$variant:ident $(= $code:ident)?,
)*
) => {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
$(
$(#[doc = $doc])*
$variant,
)*
}
impl ErrorKind {
pub const fn from_code(code: i32) -> Self {
match code & 0xFF {
$(
$(libsqlite3_sys::$code => Self::$variant,)?
)*
_ => Self::Generic,
}
}
pub const fn code(self) -> Option<i32> {
match self {
$(
$(Self::$variant => Some(libsqlite3_sys::$code),)?
)*
_ => None,
}
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
$(
Self::$variant => f.write_str($message),
)*
}
}
}
impl From<ErrorKind> for io::ErrorKind {
fn from(v: ErrorKind) -> io::ErrorKind {
match v {
$(
$(ErrorKind::$variant => io::ErrorKind::$error_kind,)?
)*
_ => io::ErrorKind::Other,
}
}
}
};
}
error_kind! {
#[message = "generic error"]
Generic = SQLITE_ERROR,
#[message = "internal malfunction"]
InternalMalfunction = SQLITE_INTERNAL,
#[message = "permission denied"]
#[io = PermissionDenied]
PermissionDenied = SQLITE_PERM,
#[message = "operation aborted"]
#[io = Interrupted]
OperationAborted = SQLITE_ABORT,
#[message = "database busy"]
DatabaseBusy = SQLITE_BUSY,
#[message = "database locked"]
DatabaseLocked = SQLITE_LOCKED,
#[message = "out of memory"]
#[io = OutOfMemory]
OutOfMemory = SQLITE_NOMEM,
#[message = "database is read only"]
#[io = PermissionDenied]
ReadOnly = SQLITE_READONLY,
#[message = "operation interrupted"]
#[io = Interrupted]
OperationInterrupted = SQLITE_INTERRUPT,
#[message = "i/o error"]
SystemIoFailure = SQLITE_IOERR,
#[message = "corrupted database"]
#[io = InvalidData]
DatabaseCorrupt = SQLITE_CORRUPT,
#[message = "database not found"]
#[io = NotFound]
NotFound = SQLITE_NOTFOUND,
#[message = "disk full"]
DiskFull = SQLITE_FULL,
#[message = "cannot open database"]
CannotOpen = SQLITE_CANTOPEN,
#[message = "file locking protocol error"]
FileLockingProtocolFailed = SQLITE_PROTOCOL,
#[message = "schema has changed"]
#[io = InvalidData]
SchemaChanged = SQLITE_SCHEMA,
#[message = "string or blob is too large"]
#[io = InvalidData]
TooLarge = SQLITE_TOOBIG,
#[message = "constraint violation"]
#[io = InvalidData]
ConstraintViolation = SQLITE_CONSTRAINT,
#[message = "datatype mismatch"]
#[io = InvalidData]
DatatypeMismatch = SQLITE_MISMATCH,
#[message = "library misuse"]
#[io = InvalidInput]
Misuse = SQLITE_MISUSE,
#[message = "lfs not supported"]
#[io = Unsupported]
LfsUnsupported = SQLITE_NOLFS,
#[message = "unauthorized statement"]
#[io = PermissionDenied]
Unauthorized = SQLITE_AUTH,
#[message = "out of range"]
#[io = InvalidInput]
OutOfRange = SQLITE_RANGE,
#[message = "not a database"]
#[io = NotFound]
NotADatabase = SQLITE_NOTADB,
#[message = "invalid database path"]
#[io = InvalidInput]
InvalidPath,
#[message = "connection was closed"]
#[io = BrokenPipe]
ConnectionClosed,
#[message = "too many statements"]
#[io = InvalidInput]
TooManyStatements,
}