fdb 0.3.1

FoundationDB Client API for Tokio
Documentation
//! Provides [`FdbError`] type, [`FdbResult`] type alias and error
//! constants.

use std::convert::TryInto;
use std::error::Error;
use std::fmt::{self, Display};

use crate::option::ErrorPredicate;

/// Error type for this crate.
///
/// Internally it wraps FDB [Error Codes]. Error codes from 100 thru'
/// 999 is generated by the binding layer and not the C API.
///
/// [Error Codes]: https://apple.github.io/foundationdb/api-error-codes.html
//
// NOTE: 0 cannot be used for `error_code`.
//
// 100 - `database` module
// 110 - `tuple` module
// 120 - `subspace` module
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct FdbError {
    /// FoundationDB error code `fdb_error_t`
    error_code: i32,
}

/// Error occurred while opening database.
pub const DATABASE_OPEN: i32 = 100;

/// Error occurred while getting a value from the tuple.
pub const TUPLE_GET: i32 = 110;

/// Error occurred extracting a [`Tuple`] value from [`Bytes`].
///
/// [`Tuple`]: crate::tuple::Tuple
/// [`Bytes`]: bytes::Bytes
pub const TUPLE_FROM_BYTES: i32 = 111;

/// Error occured when trying to pack [`Tuple`] containing an
/// incomplete [`Versionstamp`]. No incomplete [`Versionstamp`] found.
///
/// [`Tuple`]:  crate::tuple::Tuple
/// [`Versionstamp`]: crate::tuple::Versionstamp
pub const TUPLE_PACK_WITH_VERSIONSTAMP_NOT_FOUND: i32 = 112;

/// Error occured when trying to pack [`Tuple`] containing an
/// incomplete [`Versionstamp`]. Multiple incomplete [`Versionstamp`]
/// found.
///
/// [`Tuple`]:  crate::tuple::Tuple
/// [`Versionstamp`]: crate::tuple::Versionstamp
pub const TUPLE_PACK_WITH_VERSIONSTAMP_MULTIPLE_FOUND: i32 = 113;

/// Error occurred when calling [`strinc`], as the `prefix` supplied
/// is either empty or contains only `0xFF`.
///
/// [`strinc`]: crate::tuple::key_util::strinc
pub const TUPLE_KEY_UTIL_STRINC_ERROR: i32 = 114;

/// Error occured when trying to pack [`Subspace`] containing an
/// incomplete [`Versionstamp`]. Prefix contains an incomplete
/// [`Versionstamp`], which is not allowed.
///
/// [`Subspace`]:  crate::subspace::Subspace
/// [`Versionstamp`]: crate::tuple::Versionstamp
pub const SUBSPACE_PACK_WITH_VERSIONSTAMP_PREFIX_INCOMPLETE: i32 = 120;

/// Error occured when trying to unpack a key. The provided key is not
/// contained in the [`Subspace`].
///
/// [`Subspace`]:  crate::subspace::Subspace
pub const SUBSPACE_UNPACK_KEY_MISMATCH: i32 = 121;

/// Alias for [`Result`]`<T,`[`FdbError`]`>`
///
/// [`Result`]: std::result::Result
/// [`FdbError`]: crate::error::FdbError
pub type FdbResult<T> = Result<T, FdbError>;

impl FdbError {
    /// Create new [`FdbError`]
    pub fn new(err: i32) -> FdbError {
        FdbError { error_code: err }
    }

    /// Returns raw FDB error code
    pub fn code(self) -> i32 {
        self.error_code
    }

    /// Returns `true` if the error indicates the operations in the
    /// transactions should be retried because of transient error.
    pub fn is_retryable(&self) -> bool {
        unsafe {
            // `FDB_ERROR_PREDICATE_RETRYABLE` has a value `50000`
            // which can safely be converted into an `i32`.
            //
            // non-zero is `true`.
            fdb_sys::fdb_error_predicate(
                ErrorPredicate::Retryable.code().try_into().unwrap(),
                self.error_code,
            ) != 0
        }
    }

    /// Returns true if the error indicates the transaction may have
    /// succeeded, though not in a way the system can verify.
    pub fn is_maybe_committed(&self) -> bool {
        unsafe {
            // `FDB_ERROR_PREDICATE_MAYBE_COMMITTED` has a value
            // `50001` which can safely be converted into an `i32`.
            //
            // non-zero is `true`.
            fdb_sys::fdb_error_predicate(
                ErrorPredicate::MaybeCommitted.code().try_into().unwrap(),
                self.error_code,
            ) != 0
        }
    }

    /// Returns `true` if the error indicates the transaction has not
    /// committed, though in a way that can be retried.
    pub fn is_retryable_not_committed(&self) -> bool {
        unsafe {
            // `FDB_ERROR_PREDICATE_RETRYABLE_NOT_COMMITTED` has a
            // value `50002` which can safely be converted into an
            // `i32`.
            //
            // non-zero is `true`.
            fdb_sys::fdb_error_predicate(
                ErrorPredicate::RetryableNotCommitted
                    .code()
                    .try_into()
                    .unwrap(),
                self.error_code,
            ) != 0
        }
    }

    /// Returns `true` if the error is from a layer. Returns `false`
    /// if the error is a [C binding] error
    ///
    /// [C binding]: https://apple.github.io/foundationdb/api-error-codes.html
    pub(crate) fn layer_error(e: i32) -> bool {
        (100..=999).contains(&e)
    }
}

impl Error for FdbError {}

impl Display for FdbError {
    fn fmt<'a>(&self, f: &mut fmt::Formatter<'a>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

/// Converts `fdb_error_t` to `FdbResult`
pub(crate) fn check(err: fdb_sys::fdb_error_t) -> FdbResult<()> {
    if err == 0 {
        Ok(())
    } else {
        Err(FdbError::new(err))
    }
}