ic_response_codes/
lib.rs

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
#![doc = include_str!("../README.md")]

use std::error::Error;

/// Classifies why an API request or inter-canister call in the IC is rejected.
///
/// # Note
///
/// Zero (0) is not a valid reject code.
/// Converting 0 into this enum will return an error.
///
/// See [Reject codes](https://internetcomputer.org/docs/current/references/ic-interface-spec/#reject-codes) for more details.
#[repr(u32)]
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum RejectCode {
    /// Fatal system error, retry unlikely to be useful.
    SysFatal = 1,
    /// Transient system error, retry might be possible.
    SysTransient = 2,
    /// Invalid destination (e.g. canister/account does not exist).
    DestinationInvalid = 3,
    /// Explicit reject by the canister.
    CanisterReject = 4,
    /// Canister error (e.g., trap, no response).
    CanisterError = 5,
    /// Response unknown; system stopped waiting for it (e.g., timed out, or system under high load).
    SysUnknown = 6,

    /// Unrecognized reject code.
    ///
    /// # Note
    ///
    /// This variant is not part of the IC interface spec, and is used to represent
    /// reject codes that are not recognized by the library.
    ///
    /// This variant is needed just in case the IC introduces new reject codes in the future.
    /// If that happens, a Canister using existing library versions will still be able to convert
    /// the new reject codes to this variant without panicking.
    Unrecognized(u32),
}

/// Error type for [`RejectCode`] conversion.
///
/// The only case where this error can occur is when trying to convert a 0 to a [`RejectCode`].
#[derive(Clone, Copy, Debug)]
pub struct ZeroIsInvalidRejectCode;

impl std::fmt::Display for ZeroIsInvalidRejectCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "zero is invalid reject code")
    }
}

impl Error for ZeroIsInvalidRejectCode {}

impl TryFrom<u32> for RejectCode {
    type Error = ZeroIsInvalidRejectCode;

    fn try_from(code: u32) -> Result<Self, Self::Error> {
        match code {
            0 => Err(ZeroIsInvalidRejectCode),
            1 => Ok(RejectCode::SysFatal),
            2 => Ok(RejectCode::SysTransient),
            3 => Ok(RejectCode::DestinationInvalid),
            4 => Ok(RejectCode::CanisterReject),
            5 => Ok(RejectCode::CanisterError),
            6 => Ok(RejectCode::SysUnknown),
            _ => Ok(RejectCode::Unrecognized(code)),
        }
    }
}

impl From<RejectCode> for u32 {
    fn from(code: RejectCode) -> u32 {
        match code {
            RejectCode::SysFatal => 1,
            RejectCode::SysTransient => 2,
            RejectCode::DestinationInvalid => 3,
            RejectCode::CanisterReject => 4,
            RejectCode::CanisterError => 5,
            RejectCode::SysUnknown => 6,
            RejectCode::Unrecognized(code) => code,
        }
    }
}

impl PartialEq<u32> for RejectCode {
    fn eq(&self, other: &u32) -> bool {
        let self_as_u32: u32 = (*self).into();
        self_as_u32 == *other
    }
}