saola-quaint 0.2.0-alpha.14

An abstraction layer for SQL databases (PostgreSQL, MySQL, SQLite, MSSQL)
use crate::error::*;

#[derive(Debug)]
pub struct SqliteError {
    pub extended_code: i32,
    pub message: Option<String>,
}

#[cfg(not(feature = "sqlite-native"))]
impl std::fmt::Display for SqliteError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Error code {}", self.extended_code)
    }
}

impl std::error::Error for SqliteError {}

impl SqliteError {
    pub fn new(extended_code: i32, message: Option<String>) -> Self {
        Self { extended_code, message }
    }

    pub fn primary_code(&self) -> i32 {
        self.extended_code & 0xFF
    }
}

impl From<SqliteError> for Error {
    fn from(error: SqliteError) -> Self {
        match error {
            SqliteError {
                extended_code: super::ffi::SQLITE_CONSTRAINT_UNIQUE | super::ffi::SQLITE_CONSTRAINT_PRIMARYKEY,
                message: Some(description),
            } => {
                let constraint = description
                    .split(": ")
                    .nth(1)
                    .map(|s| s.split(", "))
                    .map(|i| i.flat_map(|s| s.split('.').next_back()))
                    .map(DatabaseConstraint::fields)
                    .unwrap_or(DatabaseConstraint::CannotParse);

                let kind = ErrorKind::UniqueConstraintViolation { constraint };
                let mut builder = Error::builder(kind);

                builder.set_original_code(error.extended_code.to_string());
                builder.set_original_message(description);

                builder.build()
            }

            SqliteError {
                extended_code: super::ffi::SQLITE_CONSTRAINT_NOTNULL,
                message: Some(description),
            } => {
                let constraint = description
                    .split(": ")
                    .nth(1)
                    .map(|s| s.split(", "))
                    .map(|i| i.flat_map(|s| s.split('.').next_back()))
                    .map(DatabaseConstraint::fields)
                    .unwrap_or(DatabaseConstraint::CannotParse);

                let kind = ErrorKind::NullConstraintViolation { constraint };
                let mut builder = Error::builder(kind);

                builder.set_original_code(error.extended_code.to_string());
                builder.set_original_message(description);

                builder.build()
            }

            SqliteError {
                extended_code: super::ffi::SQLITE_CONSTRAINT_FOREIGNKEY | super::ffi::SQLITE_CONSTRAINT_TRIGGER,
                message: Some(description),
            } => {
                let mut builder = Error::builder(ErrorKind::ForeignKeyConstraintViolation {
                    constraint: DatabaseConstraint::ForeignKey,
                });

                builder.set_original_code(error.extended_code.to_string());
                builder.set_original_message(description);

                builder.build()
            }

            SqliteError { extended_code, message } if error.primary_code() == super::ffi::SQLITE_BUSY => {
                let mut builder = Error::builder(ErrorKind::SocketTimeout);
                builder.set_original_code(format!("{extended_code}"));

                if let Some(description) = message {
                    builder.set_original_message(description);
                }

                builder.build()
            }

            SqliteError {
                extended_code,
                ref message,
            } => match message {
                Some(d) if d.starts_with("no such table") => {
                    let table = d.split(": ").last().into();
                    let kind = ErrorKind::TableDoesNotExist { table };

                    let mut builder = Error::builder(kind);
                    builder.set_original_code(format!("{extended_code}"));
                    builder.set_original_message(d);

                    builder.build()
                }
                Some(d) if d.contains("has no column named") => {
                    let column = d.split(" has no column named ").last().into();
                    let kind = ErrorKind::ColumnNotFound { column };

                    let mut builder = Error::builder(kind);
                    builder.set_original_code(format!("{extended_code}"));
                    builder.set_original_message(d);

                    builder.build()
                }
                Some(d) if d.starts_with("no such column: ") => {
                    let column = d.split("no such column: ").last().into();
                    let kind = ErrorKind::ColumnNotFound { column };

                    let mut builder = Error::builder(kind);
                    builder.set_original_code(format!("{extended_code}"));
                    builder.set_original_message(d);

                    builder.build()
                }
                _ => {
                    let description = message.as_ref().map(|d| d.to_string());
                    let mut builder = Error::builder(ErrorKind::QueryError(error.into()));
                    builder.set_original_code(format!("{extended_code}"));

                    if let Some(description) = description {
                        builder.set_original_message(description);
                    }

                    builder.build()
                }
            },
        }
    }
}