audb-runtime 0.1.11

Runtime library for AuDB database applications with Manifold backend
Documentation
//! Error types for AuDB runtime
//!
//! This module defines the error types used throughout the runtime library.

use thiserror::Error;

/// Errors that can occur during query execution
///
/// This enum covers all possible runtime errors including connection issues,
/// query execution failures, type mismatches, and more.
#[derive(Error, Debug)]
pub enum QueryError {
    /// Database connection error
    #[error("Connection error: {message}")]
    ConnectionError { message: String },

    /// Query execution error
    #[error("Query execution failed: {message}")]
    ExecutionError { message: String },

    /// Query parsing error (should be rare, as parsing is done at compile time)
    #[error("Query parsing error: {message}")]
    ParseError { message: String },

    /// Type mismatch during result deserialization
    #[error("Type mismatch: expected {expected}, got {actual}")]
    TypeMismatch { expected: String, actual: String },

    /// Missing required field in result
    #[error("Missing required field: {field}")]
    MissingField { field: String },

    /// Transaction error
    #[error("Transaction error: {message}")]
    TransactionError { message: String },

    /// Unsupported query language
    #[error("Unsupported query language: {language}")]
    UnsupportedLanguage { language: String },

    /// Invalid parameter binding
    #[error("Invalid parameter binding: {message}")]
    ParameterError { message: String },

    /// Row not found
    #[error("Row not found")]
    RowNotFound,

    /// Multiple rows found when one was expected
    #[error("Expected one row, found {count}")]
    MultipleRowsFound { count: usize },

    /// I/O error
    #[error("I/O error: {0}")]
    IoError(#[from] std::io::Error),

    /// Serialization/deserialization error
    #[error("Serialization error: {message}")]
    SerializationError { message: String },

    /// Generic error for cases not covered above
    #[error("{0}")]
    Other(String),
}

impl QueryError {
    /// Create a connection error
    pub fn connection<S: Into<String>>(message: S) -> Self {
        QueryError::ConnectionError {
            message: message.into(),
        }
    }

    /// Create an execution error
    pub fn execution<S: Into<String>>(message: S) -> Self {
        QueryError::ExecutionError {
            message: message.into(),
        }
    }

    /// Create a parse error
    pub fn parse<S: Into<String>>(message: S) -> Self {
        QueryError::ParseError {
            message: message.into(),
        }
    }

    /// Create a type mismatch error
    pub fn type_mismatch<S: Into<String>>(expected: S, actual: S) -> Self {
        QueryError::TypeMismatch {
            expected: expected.into(),
            actual: actual.into(),
        }
    }

    /// Create a missing field error
    pub fn missing_field<S: Into<String>>(field: S) -> Self {
        QueryError::MissingField {
            field: field.into(),
        }
    }

    /// Create a transaction error
    pub fn transaction<S: Into<String>>(message: S) -> Self {
        QueryError::TransactionError {
            message: message.into(),
        }
    }

    /// Create a parameter error
    pub fn parameter<S: Into<String>>(message: S) -> Self {
        QueryError::ParameterError {
            message: message.into(),
        }
    }

    /// Create a serialization error
    pub fn serialization<S: Into<String>>(message: S) -> Self {
        QueryError::SerializationError {
            message: message.into(),
        }
    }

    /// Create a generic error
    pub fn other<S: Into<String>>(message: S) -> Self {
        QueryError::Other(message.into())
    }
}

/// Alias for Result with QueryError
pub type Result<T> = std::result::Result<T, QueryError>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_connection_error() {
        let err = QueryError::connection("Failed to connect");
        assert!(matches!(err, QueryError::ConnectionError { .. }));
        assert_eq!(err.to_string(), "Connection error: Failed to connect");
    }

    #[test]
    fn test_execution_error() {
        let err = QueryError::execution("Query failed");
        assert!(matches!(err, QueryError::ExecutionError { .. }));
    }

    #[test]
    fn test_type_mismatch() {
        let err = QueryError::type_mismatch("String", "Integer");
        assert!(matches!(err, QueryError::TypeMismatch { .. }));
        assert!(err.to_string().contains("String"));
        assert!(err.to_string().contains("Integer"));
    }

    #[test]
    fn test_missing_field() {
        let err = QueryError::missing_field("id");
        assert!(matches!(err, QueryError::MissingField { .. }));
        assert!(err.to_string().contains("id"));
    }

    #[test]
    fn test_row_not_found() {
        let err = QueryError::RowNotFound;
        assert_eq!(err.to_string(), "Row not found");
    }

    #[test]
    fn test_multiple_rows_found() {
        let err = QueryError::MultipleRowsFound { count: 5 };
        assert!(err.to_string().contains("5"));
    }

    #[test]
    fn test_unsupported_language() {
        let err = QueryError::UnsupportedLanguage {
            language: "gremlin".to_string(),
        };
        assert!(err.to_string().contains("gremlin"));
    }

    #[test]
    fn test_io_error_conversion() {
        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
        let err: QueryError = io_err.into();
        assert!(matches!(err, QueryError::IoError(_)));
    }

    #[test]
    fn test_result_alias() {
        let ok_result: Result<i32> = Ok(42);
        assert!(ok_result.is_ok());

        let err_result: Result<i32> = Err(QueryError::RowNotFound);
        assert!(err_result.is_err());
    }
}