1use self::ErrorNumber as NShim;
16use self::SyscallError as EShim;
17use fvm_shared2::error::ErrorNumber as N2;
18use fvm_shared3::error::ErrorNumber as N3;
19use fvm_shared4::error::ErrorNumber as N4;
20use fvm2::kernel::SyscallError as E2;
21use fvm3::kernel::SyscallError as E3;
22use fvm4::kernel::SyscallError as E4;
23use num_traits::FromPrimitive;
24use std::fmt;
25use std::fmt::Debug;
26
27macro_rules! error_number {
28 ($($variant:ident),* $(,)?) => {
29 #[derive(Debug, Clone)]
30 pub enum ErrorNumber {
31 $($variant,)*
32 Unknown(u32),
34 }
35
36 impl From<N4> for ErrorNumber {
37 fn from(error: N4) -> Self {
38 match error {
39 $(N4::$variant => Self::$variant,)*
40 _ => Self::Unknown(error as u32),
41 }
42 }
43 }
44
45 impl fmt::Display for ErrorNumber {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 $(Self::$variant => std::fmt::Display::fmt(&N4::$variant, f),)*
49 Self::Unknown(u) => std::fmt::Debug::fmt(&u, f),
50 }
51 }
52 }
53 }
54}
55
56error_number! {
57 IllegalArgument,
58 IllegalOperation,
59 LimitExceeded,
60 AssertionFailed,
61 InsufficientFunds,
62 NotFound,
63 InvalidHandle,
64 IllegalCid,
65 IllegalCodec,
66 Serialization,
67 Forbidden,
68 BufferTooSmall,
69 ReadOnly,
70}
71
72impl From<N2> for ErrorNumber {
73 fn from(value: N2) -> Self {
74 let opt: Option<N4> = FromPrimitive::from_u32(value as u32);
75 match opt {
76 Some(err) => err.into(),
77 None => Self::Unknown(value as u32),
78 }
79 }
80}
81
82impl From<N3> for ErrorNumber {
83 fn from(value: N3) -> Self {
84 let opt: Option<N4> = FromPrimitive::from_u32(value as u32);
85 match opt {
86 Some(err) => err.into(),
87 None => Self::Unknown(value as u32),
88 }
89 }
90}
91
92#[derive(Debug, Clone, thiserror::Error)]
93#[error("syscall error: {message} (exit_code={number})")]
94pub struct SyscallError {
95 pub message: String,
96 pub number: NShim,
97}
98
99impl From<E2> for EShim {
100 fn from(value: E2) -> Self {
101 let E2(message, number) = value;
102 Self {
103 message,
104 number: number.into(),
105 }
106 }
107}
108
109impl From<E3> for EShim {
110 fn from(value: E3) -> Self {
111 let E3(message, number) = value;
112 Self {
113 message,
114 number: number.into(),
115 }
116 }
117}
118
119impl From<E4> for EShim {
120 fn from(value: E4) -> Self {
121 let E4(message, number) = value;
122 Self {
123 message,
124 number: number.into(),
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_n2_error_fmt() {
135 let shim: NShim = N2::IllegalArgument.into();
136
137 assert_eq!(format!("{shim}"), "illegal argument");
138 }
139
140 #[test]
141 fn test_n3_error_fmt() {
142 let shim: NShim = N3::ReadOnly.into();
143
144 assert_eq!(format!("{shim}"), "execution context is read-only");
145 }
146
147 #[test]
148 fn test_unknown_error_fmt() {
149 let shim: NShim = ErrorNumber::Unknown(23);
150
151 assert_eq!(format!("{shim}"), "23");
152 }
153}