sqlx-firebirdsql 0.1.0

Firebird SQL driver for SQLx
use std::borrow::Cow;
use std::error::Error as StdError;
use std::fmt::{self, Debug, Display, Formatter};

pub(crate) use sqlx_core::error::*;

/// An error returned from the Firebird database.
pub struct FirebirdDatabaseError {
    pub(crate) message: String,
    pub(crate) sql_code: i32,
}

impl FirebirdDatabaseError {
    pub(crate) fn from_firebirust(err: &firebirust::Error) -> Option<Self> {
        match err {
            firebirust::Error::FirebirdError(fb) => Some(Self {
                message: fb.message.clone(),
                sql_code: fb.sql_code,
            }),
            _ => None,
        }
    }

    /// The Firebird SQLCODE for this error.
    pub fn sql_code(&self) -> i32 {
        self.sql_code
    }

    /// The human-readable error message.
    pub fn message(&self) -> &str {
        &self.message
    }
}

impl Debug for FirebirdDatabaseError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("FirebirdDatabaseError")
            .field("sql_code", &self.sql_code)
            .field("message", &self.message)
            .finish()
    }
}

impl Display for FirebirdDatabaseError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "SQLCODE {}: {}", self.sql_code, self.message)
    }
}

impl StdError for FirebirdDatabaseError {}

impl DatabaseError for FirebirdDatabaseError {
    #[inline]
    fn message(&self) -> &str {
        &self.message
    }

    fn code(&self) -> Option<Cow<'_, str>> {
        Some(Cow::Owned(self.sql_code.to_string()))
    }

    #[doc(hidden)]
    fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static) {
        self
    }

    #[doc(hidden)]
    fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) {
        self
    }

    #[doc(hidden)]
    fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static> {
        self
    }

    fn kind(&self) -> ErrorKind {
        // Firebird ISC error codes (gdscode)
        // The sql_code is a legacy SQLCODE; we also check the message for patterns.
        match self.sql_code {
            // -803: unique constraint violation
            -803 => ErrorKind::UniqueViolation,
            // -530: foreign key constraint violation
            -530 => ErrorKind::ForeignKeyViolation,
            // -625: not null validation
            -625 => ErrorKind::NotNullViolation,
            // -297: check constraint violation
            -297 => ErrorKind::CheckViolation,
            _ => ErrorKind::Other,
        }
    }
}

/// Convert a firebirust error into a sqlx Error.
pub(crate) fn firebird_err(err: firebirust::Error) -> Error {
    match &err {
        firebirust::Error::FirebirdError(_) => {
            if let Some(db_err) = FirebirdDatabaseError::from_firebirust(&err) {
                Error::Database(Box::new(db_err))
            } else {
                Error::Protocol(format!("{:?}", err))
            }
        }
        firebirust::Error::IoError(_) => Error::Io(match err {
            firebirust::Error::IoError(e) => e,
            _ => unreachable!(),
        }),
        _ => Error::Protocol(format!("{:?}", err)),
    }
}