1use fvm_ipld_encoding::de::DeserializeOwned;
5use fvm_ipld_encoding::ipld_block::IpldBlock;
6use std::fmt::Display;
7
8use fvm_shared3::error::ExitCode;
9use thiserror::Error;
10
11#[derive(Error, Debug, Clone, PartialEq, Eq)]
13#[error("ActorError(exit_code: {exit_code:?}, msg: {msg})")]
14pub struct ActorError {
15 exit_code: ExitCode,
18 msg: String,
20}
21
22#[macro_export]
24macro_rules! actor_error_v10 {
25 ( $code:ident; $msg:expr ) => { $crate::v10::ActorError::$code($msg.to_string()) };
27
28 ( $code:ident; $msg:literal $(, $ex:expr)+ ) => {
30 $crate::v10::ActorError::$code(format!($msg, $($ex,)*))
31 };
32
33 ( $code:ident, $msg:expr ) => { $crate::actor_error_v10!($code; $msg) };
35
36 ( $code:ident, $msg:literal $(, $ex:expr)+ ) => {
38 $crate::actor_error_v10!($code; $msg $(, $ex)*)
39 };
40}
41
42impl ActorError {
43 pub fn unchecked(code: ExitCode, msg: String) -> Self {
46 Self {
47 exit_code: code,
48 msg,
49 }
50 }
51
52 pub fn illegal_argument(msg: String) -> Self {
53 Self {
54 exit_code: ExitCode::USR_ILLEGAL_ARGUMENT,
55 msg,
56 }
57 }
58 pub fn not_found(msg: String) -> Self {
59 Self {
60 exit_code: ExitCode::USR_NOT_FOUND,
61 msg,
62 }
63 }
64 pub fn forbidden(msg: String) -> Self {
65 Self {
66 exit_code: ExitCode::USR_FORBIDDEN,
67 msg,
68 }
69 }
70 pub fn insufficient_funds(msg: String) -> Self {
71 Self {
72 exit_code: ExitCode::USR_INSUFFICIENT_FUNDS,
73 msg,
74 }
75 }
76 pub fn illegal_state(msg: String) -> Self {
77 Self {
78 exit_code: ExitCode::USR_ILLEGAL_STATE,
79 msg,
80 }
81 }
82 pub fn serialization(msg: String) -> Self {
83 Self {
84 exit_code: ExitCode::USR_SERIALIZATION,
85 msg,
86 }
87 }
88 pub fn unhandled_message(msg: String) -> Self {
89 Self {
90 exit_code: ExitCode::USR_UNHANDLED_MESSAGE,
91 msg,
92 }
93 }
94 pub fn unspecified(msg: String) -> Self {
95 Self {
96 exit_code: ExitCode::USR_UNSPECIFIED,
97 msg,
98 }
99 }
100 pub fn assertion_failed(msg: String) -> Self {
101 Self {
102 exit_code: ExitCode::USR_ASSERTION_FAILED,
103 msg,
104 }
105 }
106
107 pub fn exit_code(&self) -> ExitCode {
109 self.exit_code
110 }
111
112 pub fn msg(&self) -> &str {
114 &self.msg
115 }
116
117 pub fn wrap(mut self, msg: impl AsRef<str>) -> Self {
119 self.msg = format!("{}: {}", msg.as_ref(), self.msg);
120 self
121 }
122}
123
124impl From<fvm_ipld_encoding::Error> for ActorError {
126 fn from(e: fvm_ipld_encoding::Error) -> Self {
127 Self {
128 exit_code: ExitCode::USR_SERIALIZATION,
129 msg: e.to_string(),
130 }
131 }
132}
133
134pub trait ActorContext<T> {
136 fn context<C>(self, context: C) -> Result<T, ActorError>
137 where
138 C: Display + 'static;
139
140 fn with_context<C, F>(self, f: F) -> Result<T, ActorError>
141 where
142 C: Display + 'static,
143 F: FnOnce() -> C;
144}
145
146impl<T> ActorContext<T> for Result<T, ActorError> {
147 fn context<C>(self, context: C) -> Result<T, ActorError>
148 where
149 C: Display + 'static,
150 {
151 self.map_err(|mut err| {
152 err.msg = format!("{}: {}", context, err.msg);
153 err
154 })
155 }
156
157 fn with_context<C, F>(self, f: F) -> Result<T, ActorError>
158 where
159 C: Display + 'static,
160 F: FnOnce() -> C,
161 {
162 self.map_err(|mut err| {
163 err.msg = format!("{}: {}", f(), err.msg);
164 err
165 })
166 }
167}
168
169pub trait AsActorError<T>: Sized {
171 fn exit_code(self, code: ExitCode) -> Result<T, ActorError>;
172
173 fn context_code<C>(self, code: ExitCode, context: C) -> Result<T, ActorError>
174 where
175 C: Display + 'static;
176
177 fn with_context_code<C, F>(self, code: ExitCode, f: F) -> Result<T, ActorError>
178 where
179 C: Display + 'static,
180 F: FnOnce() -> C;
181}
182
183impl<T, E: Display> AsActorError<T> for Result<T, E> {
185 fn exit_code(self, code: ExitCode) -> Result<T, ActorError> {
186 self.map_err(|err| ActorError {
187 exit_code: code,
188 msg: err.to_string(),
189 })
190 }
191
192 fn context_code<C>(self, code: ExitCode, context: C) -> Result<T, ActorError>
193 where
194 C: Display + 'static,
195 {
196 self.map_err(|err| ActorError {
197 exit_code: code,
198 msg: format!("{}: {}", context, err),
199 })
200 }
201
202 fn with_context_code<C, F>(self, code: ExitCode, f: F) -> Result<T, ActorError>
203 where
204 C: Display + 'static,
205 F: FnOnce() -> C,
206 {
207 self.map_err(|err| ActorError {
208 exit_code: code,
209 msg: format!("{}: {}", f(), err),
210 })
211 }
212}
213
214impl<T> AsActorError<T> for Option<T> {
215 fn exit_code(self, code: ExitCode) -> Result<T, ActorError> {
216 self.ok_or_else(|| ActorError {
217 exit_code: code,
218 msg: "None".to_string(),
219 })
220 }
221
222 fn context_code<C>(self, code: ExitCode, context: C) -> Result<T, ActorError>
223 where
224 C: Display + 'static,
225 {
226 self.ok_or_else(|| ActorError {
227 exit_code: code,
228 msg: context.to_string(),
229 })
230 }
231
232 fn with_context_code<C, F>(self, code: ExitCode, f: F) -> Result<T, ActorError>
233 where
234 C: Display + 'static,
235 F: FnOnce() -> C,
236 {
237 self.ok_or_else(|| ActorError {
238 exit_code: code,
239 msg: f().to_string(),
240 })
241 }
242}
243
244pub fn deserialize_block<T>(ret: Option<IpldBlock>) -> Result<T, ActorError>
245where
246 T: DeserializeOwned,
247{
248 ret.context_code(
249 ExitCode::USR_ASSERTION_FAILED,
250 "return expected".to_string(),
251 )?
252 .deserialize()
253 .exit_code(ExitCode::USR_SERIALIZATION)
254}