sqlint 0.2.2

An abstraction layer for SQL databases.
Documentation
use crate::error::*;
use libsqlite3_sys as ffi;
use rusqlite::types::FromSqlError;

impl From<rusqlite::Error> for Error {
    fn from(e: rusqlite::Error) -> Error {
        match e {
            rusqlite::Error::ToSqlConversionFailure(error) => match error.downcast::<Error>() {
                Ok(error) => *error,
                Err(error) => {
                    let mut builder = Error::builder(ErrorKind::QueryError(error));

                    builder.set_original_message("Could not interpret parameters in an SQLite query.");

                    builder.build()
                }
            },
            rusqlite::Error::InvalidQuery => {
                let mut builder = Error::builder(ErrorKind::QueryError(e.into()));

                builder.set_original_message(
                    "Could not interpret the query or its parameters. Check the syntax and parameter types.",
                );

                builder.build()
            }
            rusqlite::Error::ExecuteReturnedResults => {
                let mut builder = Error::builder(ErrorKind::QueryError(e.into()));
                builder.set_original_message("Execute returned results, which is not allowed in SQLite.");

                builder.build()
            }

            rusqlite::Error::QueryReturnedNoRows => Error::builder(ErrorKind::NotFound).build(),

            rusqlite::Error::SqliteFailure(
                ffi::Error { code: ffi::ErrorCode::ConstraintViolation, extended_code: 2067 },
                Some(description),
            ) => {
                let constraint = description
                    .split(": ")
                    .nth(1)
                    .map(|s| s.split(", "))
                    .map(|i| i.flat_map(|s| s.split('.').last()))
                    .map(DatabaseConstraint::fields)
                    .unwrap_or(DatabaseConstraint::CannotParse);

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

                builder.set_original_code("2067");
                builder.set_original_message(description);

                builder.build()
            }

            rusqlite::Error::SqliteFailure(
                ffi::Error { code: ffi::ErrorCode::ConstraintViolation, extended_code: 1555 },
                Some(description),
            ) => {
                let constraint = description
                    .split(": ")
                    .nth(1)
                    .map(|s| s.split(", "))
                    .map(|i| i.flat_map(|s| s.split('.').last()))
                    .map(DatabaseConstraint::fields)
                    .unwrap_or(DatabaseConstraint::CannotParse);

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

                builder.set_original_code("1555");
                builder.set_original_message(description);

                builder.build()
            }

            rusqlite::Error::SqliteFailure(
                ffi::Error { code: ffi::ErrorCode::ConstraintViolation, extended_code: 1299 },
                Some(description),
            ) => {
                let constraint = description
                    .split(": ")
                    .nth(1)
                    .map(|s| s.split(", "))
                    .map(|i| i.flat_map(|s| s.split('.').last()))
                    .map(DatabaseConstraint::fields)
                    .unwrap_or(DatabaseConstraint::CannotParse);

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

                builder.set_original_code("1299");
                builder.set_original_message(description);

                builder.build()
            }

            rusqlite::Error::SqliteFailure(
                ffi::Error { code: ffi::ErrorCode::ConstraintViolation, extended_code: 787 },
                Some(description),
            ) => {
                let mut builder = Error::builder(ErrorKind::ForeignKeyConstraintViolation {
                    constraint: DatabaseConstraint::ForeignKey,
                });

                builder.set_original_code("787");
                builder.set_original_message(description);

                builder.build()
            }

            rusqlite::Error::SqliteFailure(
                ffi::Error { code: ffi::ErrorCode::ConstraintViolation, extended_code: 1811 },
                Some(description),
            ) => {
                let mut builder = Error::builder(ErrorKind::ForeignKeyConstraintViolation {
                    constraint: DatabaseConstraint::ForeignKey,
                });

                builder.set_original_code("1811");
                builder.set_original_message(description);

                builder.build()
            }

            rusqlite::Error::SqliteFailure(
                ffi::Error { code: ffi::ErrorCode::DatabaseBusy, extended_code },
                description,
            ) => {
                let mut builder = Error::builder(ErrorKind::SocketTimeout);
                builder.set_original_code(format!("{extended_code}"));

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

                builder.build()
            }

            rusqlite::Error::SqliteFailure(ffi::Error { extended_code, .. }, ref description) => match description {
                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 = description.as_ref().map(|d| d.to_string());
                    let mut builder = Error::builder(ErrorKind::QueryError(e.into()));
                    builder.set_original_code(format!("{extended_code}"));

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

                    builder.build()
                }
            },
            e => Error::builder(ErrorKind::QueryError(e.into())).build(),
        }
    }
}

impl From<FromSqlError> for Error {
    fn from(e: FromSqlError) -> Error {
        Error::builder(ErrorKind::ColumnReadFailure(e.into())).build()
    }
}