ab_contracts_common/
error.rs

1use derive_more::Display;
2
3#[derive(Debug, Display, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
4pub struct UnknownContractErrorCode(u8);
5
6impl UnknownContractErrorCode {
7    /// Get the inner error code
8    #[inline(always)]
9    pub const fn code(self) -> u8 {
10        self.0
11    }
12}
13
14#[derive(Debug, Display, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
15pub struct CustomContractErrorCode(u64);
16
17impl CustomContractErrorCode {
18    /// Get the inner error code
19    #[inline(always)]
20    pub const fn code(self) -> u64 {
21        self.0
22    }
23}
24
25#[derive(Debug, Display, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
26pub enum ContractError {
27    BadInput,
28    BadOutput,
29    Forbidden,
30    NotFound,
31    Conflict,
32    InternalError,
33    NotImplemented,
34    Unknown(UnknownContractErrorCode),
35    Custom(CustomContractErrorCode),
36}
37
38impl From<CustomContractErrorCode> for ContractError {
39    #[inline(always)]
40    fn from(error: CustomContractErrorCode) -> Self {
41        Self::Custom(error)
42    }
43}
44
45impl ContractError {
46    /// Create contract error with a custom error code.
47    ///
48    /// Code must be larger than `u8::MAX` or `None` will be returned.
49    #[inline(always)]
50    pub const fn new_custom_code(code: u64) -> Option<Self> {
51        if code > u8::MAX as u64 {
52            Some(Self::Custom(CustomContractErrorCode(code)))
53        } else {
54            None
55        }
56    }
57
58    /// Convert contact error into contract exit code.
59    ///
60    /// Mosty useful for low-level code.
61    #[inline(always)]
62    pub const fn exit_code(self) -> ExitCode {
63        ExitCode(match self {
64            Self::BadInput => 1,
65            Self::BadOutput => 2,
66            Self::Forbidden => 3,
67            Self::NotFound => 4,
68            Self::Conflict => 5,
69            Self::InternalError => 6,
70            Self::NotImplemented => 7,
71            Self::Unknown(unknown) => unknown.code() as u64,
72            Self::Custom(custom) => custom.code(),
73        })
74    }
75}
76
77#[derive(Debug, Display, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
78#[repr(C)]
79#[must_use = "Code can be Ok or one of the errors, consider converting to Result<(), ContractCode>"]
80pub struct ExitCode(u64);
81
82impl From<ContractError> for ExitCode {
83    #[inline(always)]
84    fn from(error: ContractError) -> Self {
85        error.exit_code()
86    }
87}
88
89impl From<Result<(), ContractError>> for ExitCode {
90    #[inline(always)]
91    fn from(error: Result<(), ContractError>) -> Self {
92        match error {
93            Ok(()) => Self(0),
94            Err(error) => error.exit_code(),
95        }
96    }
97}
98
99impl From<ExitCode> for Result<(), ContractError> {
100    #[inline(always)]
101    fn from(value: ExitCode) -> Self {
102        Err(match value.0 {
103            0 => {
104                return Ok(());
105            }
106            1 => ContractError::BadInput,
107            2 => ContractError::BadOutput,
108            3 => ContractError::Forbidden,
109            4 => ContractError::NotFound,
110            5 => ContractError::Conflict,
111            6 => ContractError::InternalError,
112            7 => ContractError::NotImplemented,
113            8..=255 => ContractError::Unknown(UnknownContractErrorCode(
114                u8::try_from(value.0).expect("All matched values are within `u8` range; qed"),
115            )),
116            code => ContractError::Custom(CustomContractErrorCode(code)),
117        })
118    }
119}
120
121impl ExitCode {
122    /// Exit code indicating success
123    #[inline(always)]
124    pub fn ok() -> Self {
125        Self(0)
126    }
127
128    /// Convert into `u64`
129    #[inline(always)]
130    pub const fn into_u64(self) -> u64 {
131        self.0
132    }
133}