foundationdb/
error.rs

1// Copyright 2018 foundationdb-rs developers, https://github.com/Clikengo/foundationdb-rs/graphs/contributors
2// Copyright 2013-2018 Apple, Inc and the FoundationDB project authors.
3//
4// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
5// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
6// http://opensource.org/licenses/MIT>, at your option. This file may not be
7// copied, modified, or distributed except according to those terms.
8
9//! Error types for the Fdb crate
10
11use crate::directory::DirectoryError;
12use crate::options;
13use crate::tuple::hca::HcaError;
14use crate::tuple::PackError;
15use foundationdb_sys as fdb_sys;
16use std::ffi::CStr;
17use std::fmt;
18use std::fmt::{Debug, Display, Formatter};
19
20pub(crate) fn eval(error_code: fdb_sys::fdb_error_t) -> FdbResult<()> {
21    let rust_code: i32 = error_code;
22    if rust_code == 0 {
23        Ok(())
24    } else {
25        Err(FdbError::from_code(error_code))
26    }
27}
28
29/// The Standard Error type of FoundationDB
30#[derive(Debug, Copy, Clone)]
31pub struct FdbError {
32    /// The FoundationDB error code
33    error_code: i32,
34}
35
36impl FdbError {
37    /// Converts from a raw foundationDB error code
38    pub fn from_code(error_code: fdb_sys::fdb_error_t) -> Self {
39        Self { error_code }
40    }
41
42    #[cfg(feature = "tenant-experimental")]
43    /// Converts from a raw foundationDB error code
44    pub(crate) fn new(error_code: i32) -> Self {
45        Self { error_code }
46    }
47
48    pub fn message(self) -> &'static str {
49        let error_str =
50            unsafe { CStr::from_ptr::<'static>(fdb_sys::fdb_get_error(self.error_code)) };
51        error_str
52            .to_str()
53            .expect("bad error string from FoundationDB")
54    }
55
56    fn is_error_predicate(self, predicate: options::ErrorPredicate) -> bool {
57        // This cast to `i32` isn't unnecessary in all configurations.
58        #[allow(clippy::unnecessary_cast)]
59        let check =
60            unsafe { fdb_sys::fdb_error_predicate(predicate.code() as i32, self.error_code) };
61
62        check != 0
63    }
64
65    /// Indicates the transaction may have succeeded, though not in a way the system can verify.
66    pub fn is_maybe_committed(self) -> bool {
67        self.is_error_predicate(options::ErrorPredicate::MaybeCommitted)
68    }
69
70    /// Indicates the operations in the transactions should be retried because of transient error.
71    pub fn is_retryable(self) -> bool {
72        self.is_error_predicate(options::ErrorPredicate::Retryable)
73    }
74
75    /// Indicates the transaction has not committed, though in a way that can be retried.
76    pub fn is_retryable_not_committed(self) -> bool {
77        self.is_error_predicate(options::ErrorPredicate::RetryableNotCommitted)
78    }
79
80    /// Raw foundationdb error code
81    pub fn code(self) -> i32 {
82        self.error_code
83    }
84}
85
86impl fmt::Display for FdbError {
87    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88        std::fmt::Display::fmt(&self.message(), f)
89    }
90}
91
92impl std::error::Error for FdbError {}
93
94/// Alias for `Result<..., FdbError>`
95pub type FdbResult<T = ()> = Result<T, FdbError>;
96
97/// This error represent all errors that can be throwed by `db.run`.
98/// Layer developers may use the `CustomError`.
99pub enum FdbBindingError {
100    NonRetryableFdbError(FdbError),
101    HcaError(HcaError),
102    DirectoryError(DirectoryError),
103    PackError(PackError),
104    /// A reference to the `RetryableTransaction` has been kept
105    ReferenceToTransactionKept,
106    /// A custom error that layer developers can use
107    CustomError(Box<dyn std::error::Error + Send + Sync>),
108}
109
110impl FdbBindingError {
111    /// Returns the underlying `FdbError`, if any.
112    pub fn get_fdb_error(&self) -> Option<FdbError> {
113        match *self {
114            Self::NonRetryableFdbError(error)
115            | Self::DirectoryError(DirectoryError::FdbError(error))
116            | Self::HcaError(HcaError::FdbError(error)) => Some(error),
117            Self::CustomError(ref error) => {
118                if let Some(e) = error.downcast_ref::<FdbError>() {
119                    Some(*e)
120                } else if let Some(e) = error.downcast_ref::<FdbBindingError>() {
121                    e.get_fdb_error()
122                } else {
123                    None
124                }
125            }
126            _ => None,
127        }
128    }
129}
130
131impl From<FdbError> for FdbBindingError {
132    fn from(e: FdbError) -> Self {
133        Self::NonRetryableFdbError(e)
134    }
135}
136
137impl From<HcaError> for FdbBindingError {
138    fn from(e: HcaError) -> Self {
139        Self::HcaError(e)
140    }
141}
142
143impl From<DirectoryError> for FdbBindingError {
144    fn from(e: DirectoryError) -> Self {
145        Self::DirectoryError(e)
146    }
147}
148
149impl FdbBindingError {
150    /// create a new custom error
151    pub fn new_custom_error(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
152        Self::CustomError(e)
153    }
154}
155
156impl Debug for FdbBindingError {
157    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
158        match self {
159            FdbBindingError::NonRetryableFdbError(err) => write!(f, "{err:?}"),
160            FdbBindingError::HcaError(err) => write!(f, "{err:?}"),
161            FdbBindingError::DirectoryError(err) => write!(f, "{err:?}"),
162            FdbBindingError::PackError(err) => write!(f, "{err:?}"),
163            FdbBindingError::ReferenceToTransactionKept => {
164                write!(f, "Reference to transaction kept")
165            }
166            FdbBindingError::CustomError(err) => write!(f, "{err:?}"),
167        }
168    }
169}
170
171impl Display for FdbBindingError {
172    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
173        std::fmt::Debug::fmt(&self, f)
174    }
175}
176
177impl std::error::Error for FdbBindingError {}