ic_response_codes/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::error::Error;
4use std::fmt::Display;
5
6/// Classifies why an API request or inter-canister call in the IC is rejected.
7///
8/// # Note
9///
10/// Zero (0) is not a valid reject code.
11/// Converting 0 into this enum will return an error.
12///
13/// See [Reject codes](https://internetcomputer.org/docs/current/references/ic-interface-spec/#reject-codes) for more details.
14#[repr(u32)]
15#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
16pub enum RejectCode {
17    /// Fatal system error, retry unlikely to be useful.
18    SysFatal = 1,
19    /// Transient system error, retry might be possible.
20    SysTransient = 2,
21    /// Invalid destination (e.g. canister/account does not exist).
22    DestinationInvalid = 3,
23    /// Explicit reject by the canister.
24    CanisterReject = 4,
25    /// Canister error (e.g., trap, no response).
26    CanisterError = 5,
27    /// Response unknown; system stopped waiting for it (e.g., timed out, or system under high load).
28    SysUnknown = 6,
29
30    /// Unrecognized reject code.
31    ///
32    /// # Note
33    ///
34    /// This variant is not part of the IC interface spec, and is used to represent
35    /// reject codes that are not recognized by the library.
36    ///
37    /// This variant is needed just in case the IC introduces new reject codes in the future.
38    /// If that happens, a Canister using existing library versions will still be able to convert
39    /// the new reject codes to this variant without panicking.
40    Unrecognized(u32),
41}
42
43impl Display for RejectCode {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        match self {
46            RejectCode::SysFatal => write!(f, "SysFatal(1)"),
47            RejectCode::SysTransient => write!(f, "SysTransient(2)"),
48            RejectCode::DestinationInvalid => write!(f, "DestinationInvalid(3)"),
49            RejectCode::CanisterReject => write!(f, "CanisterReject(4)"),
50            RejectCode::CanisterError => write!(f, "CanisterError(5)"),
51            RejectCode::SysUnknown => write!(f, "SysUnknown(6)"),
52            RejectCode::Unrecognized(code) => write!(f, "Unrecognized({})", code),
53        }
54    }
55}
56
57/// Error type for [`RejectCode`] conversion.
58///
59/// The only case where this error can occur is when trying to convert a 0 to a [`RejectCode`].
60#[derive(Clone, Copy, Debug)]
61pub struct ZeroIsInvalidRejectCode;
62
63impl Display for ZeroIsInvalidRejectCode {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        write!(f, "zero is invalid reject code")
66    }
67}
68
69impl Error for ZeroIsInvalidRejectCode {}
70
71impl TryFrom<u32> for RejectCode {
72    type Error = ZeroIsInvalidRejectCode;
73
74    fn try_from(code: u32) -> Result<Self, Self::Error> {
75        match code {
76            0 => Err(ZeroIsInvalidRejectCode),
77            1 => Ok(RejectCode::SysFatal),
78            2 => Ok(RejectCode::SysTransient),
79            3 => Ok(RejectCode::DestinationInvalid),
80            4 => Ok(RejectCode::CanisterReject),
81            5 => Ok(RejectCode::CanisterError),
82            6 => Ok(RejectCode::SysUnknown),
83            _ => Ok(RejectCode::Unrecognized(code)),
84        }
85    }
86}
87
88impl From<RejectCode> for u32 {
89    fn from(code: RejectCode) -> u32 {
90        match code {
91            RejectCode::SysFatal => 1,
92            RejectCode::SysTransient => 2,
93            RejectCode::DestinationInvalid => 3,
94            RejectCode::CanisterReject => 4,
95            RejectCode::CanisterError => 5,
96            RejectCode::SysUnknown => 6,
97            RejectCode::Unrecognized(code) => code,
98        }
99    }
100}
101
102impl PartialEq<u32> for RejectCode {
103    fn eq(&self, other: &u32) -> bool {
104        let self_as_u32: u32 = (*self).into();
105        self_as_u32 == *other
106    }
107}