1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! The crate `fuel-core-storage` contains storage types, primitives, tables used by `fuel-core`.
//! This crate doesn't contain the actual implementation of the storage. It works around the
//! `Database` and is used by services to provide a default implementation. Primitives
//! defined here are used by services but are flexible enough to customize the
//! logic when the `Database` is known.

#![deny(unused_crate_dependencies)]
#![deny(missing_docs)]

use fuel_core_types::services::executor::Error as ExecutorError;
use std::io::ErrorKind;

pub use fuel_vm_private::{
    fuel_storage::*,
    storage::InterpreterStorage,
};

pub mod iter;
pub mod tables;
#[cfg(feature = "test-helpers")]
pub mod test_helpers;
pub mod transactional;

pub use fuel_vm_private::storage::{
    ContractsAssetKey,
    ContractsStateKey,
};

/// The storage result alias.
pub type Result<T> = core::result::Result<T, Error>;

#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
/// Error occurring during interaction with storage
pub enum Error {
    /// Error occurred during serialization or deserialization of the entity.
    #[error("error performing serialization or deserialization")]
    Codec,
    /// Error occurred during interaction with database.
    #[error("error occurred in the underlying datastore `{0}`")]
    DatabaseError(Box<dyn std::error::Error + Send + Sync>),
    /// This error should be created with `not_found` macro.
    #[error("resource of type `{0}` was not found at the: {1}")]
    NotFound(&'static str, &'static str),
    // TODO: Do we need this type at all?
    /// Unknown or not expected(by architecture) error.
    #[error(transparent)]
    Other(#[from] anyhow::Error),
}

impl From<Error> for std::io::Error {
    fn from(e: Error) -> Self {
        std::io::Error::new(ErrorKind::Other, e)
    }
}

impl From<Error> for ExecutorError {
    fn from(e: Error) -> Self {
        ExecutorError::StorageError(Box::new(e))
    }
}

/// The helper trait to work with storage errors.
pub trait IsNotFound {
    /// Return `true` if the error is [`Error::NotFound`].
    fn is_not_found(&self) -> bool;
}

impl IsNotFound for Error {
    fn is_not_found(&self) -> bool {
        matches!(self, Error::NotFound(_, _))
    }
}

impl<T> IsNotFound for Result<T> {
    fn is_not_found(&self) -> bool {
        match self {
            Err(err) => err.is_not_found(),
            _ => false,
        }
    }
}

/// Creates `StorageError::NotFound` error with file and line information inside.
///
/// # Examples
///
/// ```
/// use fuel_core_storage::not_found;
/// use fuel_core_storage::tables::Messages;
///
/// let string_type = not_found!("BlockId");
/// let mappable_type = not_found!(Messages);
/// let mappable_path = not_found!(fuel_core_storage::tables::Messages);
/// ```
#[macro_export]
macro_rules! not_found {
    ($name: literal) => {
        $crate::Error::NotFound($name, concat!(file!(), ":", line!()))
    };
    ($ty: path) => {
        $crate::Error::NotFound(
            ::core::any::type_name::<<$ty as $crate::Mappable>::OwnedValue>(),
            concat!(file!(), ":", line!()),
        )
    };
}

#[cfg(test)]
mod test {
    use crate::tables::Coins;

    #[test]
    fn not_found_output() {
        #[rustfmt::skip]
        assert_eq!(
            format!("{}", not_found!("BlockId")),
            format!("resource of type `BlockId` was not found at the: {}:{}", file!(), line!() - 1)
        );
        #[rustfmt::skip]
        assert_eq!(
            format!("{}", not_found!(Coins)),
            format!("resource of type `fuel_core_types::entities::coin::CompressedCoin` was not found at the: {}:{}", file!(), line!() - 1)
        );
    }
}