/build/source/nativelink-error/src/lib.rs
Line | Count | Source |
1 | | // Copyright 2024 The NativeLink Authors. All rights reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | use core::convert::Into; |
16 | | |
17 | | use nativelink_metric::{ |
18 | | MetricFieldData, MetricKind, MetricPublishKnownKindData, MetricsComponent, |
19 | | }; |
20 | | use prost_types::TimestampError; |
21 | | use serde::{Deserialize, Serialize}; |
22 | | // Reexport of tonic's error codes which we use as "nativelink_error::Code". |
23 | | pub use tonic::Code; |
24 | | |
25 | | #[macro_export] |
26 | | macro_rules! make_err { |
27 | | ($code:expr, $($arg:tt)+) => {{ |
28 | | $crate::Error::new( |
29 | | $code, |
30 | | format!("{}", format_args!($($arg)+)), |
31 | | ) |
32 | | }}; |
33 | | } |
34 | | |
35 | | #[macro_export] |
36 | | macro_rules! make_input_err { |
37 | | ($($arg:tt)+) => {{ |
38 | | $crate::make_err!($crate::Code::InvalidArgument, $($arg)+) |
39 | | }}; |
40 | | } |
41 | | |
42 | | #[macro_export] |
43 | | macro_rules! error_if { |
44 | | ($cond:expr, $($arg:tt)+) => {{ |
45 | | if $cond { |
46 | | Err($crate::make_err!($crate::Code::InvalidArgument, $($arg)+))?; |
47 | | } |
48 | | }}; |
49 | | } |
50 | | |
51 | 0 | #[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] |
52 | | pub struct Error { |
53 | | #[serde(with = "CodeDef")] |
54 | | pub code: Code, |
55 | | pub messages: Vec<String>, |
56 | | } |
57 | | |
58 | | impl MetricsComponent for Error { |
59 | 0 | fn publish( |
60 | 0 | &self, |
61 | 0 | kind: MetricKind, |
62 | 0 | field_metadata: MetricFieldData, |
63 | 0 | ) -> Result<MetricPublishKnownKindData, nativelink_metric::Error> { |
64 | 0 | self.to_string().publish(kind, field_metadata) |
65 | 0 | } |
66 | | } |
67 | | |
68 | | impl Error { |
69 | 403 | pub fn new(code: Code, msg: String) -> Self { |
70 | 403 | let mut msgs = Vec::with_capacity(1); |
71 | 403 | if !msg.is_empty() { Branch (71:12): [True: 403, False: 0]
Branch (71:12): [Folded - Ignored]
|
72 | 403 | msgs.push(msg); |
73 | 403 | }0 |
74 | 403 | Self { |
75 | 403 | code, |
76 | 403 | messages: msgs, |
77 | 403 | } |
78 | 403 | } |
79 | | |
80 | | #[inline] |
81 | | #[must_use] |
82 | 2 | pub fn append<S: Into<String>>(mut self, msg: S) -> Self { |
83 | 2 | self.messages.push(msg.into()); |
84 | 2 | self |
85 | 2 | } |
86 | | |
87 | | #[must_use] |
88 | 2 | pub fn merge<E: Into<Self>>(mut self, other: E) -> Self { |
89 | 2 | let mut other: Self = other.into(); |
90 | 2 | // This will help with knowing which messages are tied to different errors. |
91 | 2 | self.messages.push("---".to_string()); |
92 | 2 | self.messages.append(&mut other.messages); |
93 | 2 | self |
94 | 2 | } |
95 | | |
96 | | #[must_use] |
97 | 17 | pub fn merge_option<T: Into<Self>, U: Into<Self>>( |
98 | 17 | this: Option<T>, |
99 | 17 | other: Option<U>, |
100 | 17 | ) -> Option<Self> { |
101 | 17 | if let Some(this4 ) = this { Branch (101:16): [Folded - Ignored]
Branch (101:16): [Folded - Ignored]
Branch (101:16): [True: 4, False: 13]
|
102 | 4 | if let Some(other0 ) = other { Branch (102:20): [Folded - Ignored]
Branch (102:20): [Folded - Ignored]
Branch (102:20): [True: 0, False: 4]
|
103 | 0 | return Some(this.into().merge(other)); |
104 | 4 | } |
105 | 4 | return Some(this.into()); |
106 | 13 | } |
107 | 13 | other.map(Into::into) |
108 | 17 | } |
109 | | |
110 | 1 | pub fn to_std_err(self) -> std::io::Error { |
111 | 1 | std::io::Error::new(self.code.into_error_kind(), self.messages.join(" : ")) |
112 | 1 | } |
113 | | |
114 | 8 | pub fn message_string(&self) -> String { |
115 | 8 | self.messages.join(" : ") |
116 | 8 | } |
117 | | } |
118 | | |
119 | | impl core::error::Error for Error {} |
120 | | |
121 | | impl From<Error> for nativelink_proto::google::rpc::Status { |
122 | 8 | fn from(val: Error) -> Self { |
123 | 8 | Self { |
124 | 8 | code: val.code as i32, |
125 | 8 | message: val.message_string(), |
126 | 8 | details: vec![], |
127 | 8 | } |
128 | 8 | } |
129 | | } |
130 | | |
131 | | impl From<nativelink_proto::google::rpc::Status> for Error { |
132 | 2 | fn from(val: nativelink_proto::google::rpc::Status) -> Self { |
133 | 2 | Self { |
134 | 2 | code: val.code.into(), |
135 | 2 | messages: vec![val.message], |
136 | 2 | } |
137 | 2 | } |
138 | | } |
139 | | |
140 | | impl core::fmt::Display for Error { |
141 | 8 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
142 | 8 | // A manual impl to reduce the noise of frequently empty fields. |
143 | 8 | let mut builder = f.debug_struct("Error"); |
144 | 8 | |
145 | 8 | builder.field("code", &self.code); |
146 | 8 | |
147 | 8 | if !self.messages.is_empty() { Branch (147:12): [True: 8, False: 0]
Branch (147:12): [Folded - Ignored]
|
148 | 8 | builder.field("messages", &self.messages); |
149 | 8 | }0 |
150 | | |
151 | 8 | builder.finish() |
152 | 8 | } |
153 | | } |
154 | | |
155 | | impl From<prost::DecodeError> for Error { |
156 | 0 | fn from(err: prost::DecodeError) -> Self { |
157 | 0 | make_err!(Code::Internal, "{}", err.to_string()) |
158 | 0 | } |
159 | | } |
160 | | |
161 | | impl From<prost::EncodeError> for Error { |
162 | 0 | fn from(err: prost::EncodeError) -> Self { |
163 | 0 | make_err!(Code::Internal, "{}", err.to_string()) |
164 | 0 | } |
165 | | } |
166 | | |
167 | | impl From<prost::UnknownEnumValue> for Error { |
168 | 0 | fn from(err: prost::UnknownEnumValue) -> Self { |
169 | 0 | make_err!(Code::Internal, "{}", err.to_string()) |
170 | 0 | } |
171 | | } |
172 | | |
173 | | impl From<core::num::TryFromIntError> for Error { |
174 | 0 | fn from(err: core::num::TryFromIntError) -> Self { |
175 | 0 | make_err!(Code::InvalidArgument, "{}", err.to_string()) |
176 | 0 | } |
177 | | } |
178 | | |
179 | | impl From<tokio::task::JoinError> for Error { |
180 | 0 | fn from(err: tokio::task::JoinError) -> Self { |
181 | 0 | make_err!(Code::Internal, "{}", err.to_string()) |
182 | 0 | } |
183 | | } |
184 | | |
185 | | impl From<core::num::ParseIntError> for Error { |
186 | 0 | fn from(err: core::num::ParseIntError) -> Self { |
187 | 0 | make_err!(Code::InvalidArgument, "{}", err.to_string()) |
188 | 0 | } |
189 | | } |
190 | | |
191 | | impl From<core::convert::Infallible> for Error { |
192 | 0 | fn from(_err: core::convert::Infallible) -> Self { |
193 | 0 | // Infallible is an error type that can never happen. |
194 | 0 | unreachable!(); |
195 | | } |
196 | | } |
197 | | |
198 | | impl From<TimestampError> for Error { |
199 | 0 | fn from(err: TimestampError) -> Self { |
200 | 0 | make_err!(Code::InvalidArgument, "{}", err) |
201 | 0 | } |
202 | | } |
203 | | |
204 | | impl From<std::io::Error> for Error { |
205 | 12 | fn from(err: std::io::Error) -> Self { |
206 | 12 | Self { |
207 | 12 | code: err.kind().into_code(), |
208 | 12 | messages: vec![err.to_string()], |
209 | 12 | } |
210 | 12 | } |
211 | | } |
212 | | |
213 | | impl From<fred::error::Error> for Error { |
214 | 0 | fn from(error: fred::error::Error) -> Self { |
215 | | use fred::error::ErrorKind::{ |
216 | | Auth, Backpressure, Canceled, Cluster, Config, IO, InvalidArgument, InvalidCommand, |
217 | | NotFound, Parse, Protocol, Routing, Sentinel, Timeout, Tls, Unknown, Url, |
218 | | }; |
219 | | |
220 | | // Conversions here are based on https://grpc.github.io/grpc/core/md_doc_statuscodes.html. |
221 | 0 | let code = match error.kind() { |
222 | 0 | Config | InvalidCommand | InvalidArgument | Url => Code::InvalidArgument, |
223 | 0 | IO | Protocol | Tls | Cluster | Parse | Sentinel | Routing => Code::Internal, |
224 | 0 | Auth => Code::PermissionDenied, |
225 | 0 | Canceled => Code::Aborted, |
226 | 0 | Unknown => Code::Unknown, |
227 | 0 | Timeout => Code::DeadlineExceeded, |
228 | 0 | NotFound => Code::NotFound, |
229 | 0 | Backpressure => Code::Unavailable, |
230 | | }; |
231 | | |
232 | 0 | make_err!(code, "{error}") |
233 | 0 | } |
234 | | } |
235 | | |
236 | | impl From<tonic::Status> for Error { |
237 | 2 | fn from(status: tonic::Status) -> Self { |
238 | 2 | make_err!(status.code(), "{}", status.to_string()) |
239 | 2 | } |
240 | | } |
241 | | |
242 | | impl From<Error> for tonic::Status { |
243 | 12 | fn from(val: Error) -> Self { |
244 | 12 | Self::new(val.code, val.messages.join(" : ")) |
245 | 12 | } |
246 | | } |
247 | | |
248 | | pub trait ResultExt<T> { |
249 | | fn err_tip_with_code<F, S>(self, tip_fn: F) -> Result<T, Error> |
250 | | where |
251 | | Self: Sized, |
252 | | S: ToString, |
253 | | F: (FnOnce(&Error) -> (Code, S)) + Sized; |
254 | | |
255 | | #[inline] |
256 | 276k | fn err_tip<F, S>(self, tip_fn: F) -> Result<T, Error> |
257 | 276k | where |
258 | 276k | Self: Sized, |
259 | 276k | S: ToString, |
260 | 276k | F: (FnOnce() -> S) + Sized, |
261 | 276k | { |
262 | 276k | self.err_tip_with_code(|e| (e.code, tip_fn())107 ) |
263 | 276k | } |
264 | | |
265 | 0 | fn merge<U>(self, _other: Result<U, Error>) -> Result<U, Error> |
266 | 0 | where |
267 | 0 | Self: Sized, |
268 | 0 | { |
269 | 0 | unreachable!(); |
270 | | } |
271 | | } |
272 | | |
273 | | impl<T, E: Into<Error>> ResultExt<T> for Result<T, E> { |
274 | | #[inline] |
275 | 275k | fn err_tip_with_code<F, S>(self, tip_fn: F) -> Result<T, Error> |
276 | 275k | where |
277 | 275k | Self: Sized, |
278 | 275k | S: ToString, |
279 | 275k | F: (FnOnce(&Error) -> (Code, S)) + Sized, |
280 | 275k | { |
281 | 275k | self.map_err(|e| { |
282 | 97 | let mut error: Error = e.into(); |
283 | 97 | let (code, message) = tip_fn(&error); |
284 | 97 | error.code = code; |
285 | 97 | error.messages.push(message.to_string()); |
286 | 97 | error |
287 | 97 | }) |
288 | 275k | } |
289 | | |
290 | 5.68k | fn merge<U>(self, other: Result<U, Error>) -> Result<U, Error> |
291 | 5.68k | where |
292 | 5.68k | Self: Sized, |
293 | 5.68k | { |
294 | 5.68k | if let Err(e17 ) = self { Branch (294:16): [Folded - Ignored]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 1, False: 5]
Branch (294:16): [True: 3, False: 37]
Branch (294:16): [True: 4, False: 259]
Branch (294:16): [True: 0, False: 1]
Branch (294:16): [True: 0, False: 1]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 1, False: 17]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 1.23k]
Branch (294:16): [True: 0, False: 1]
Branch (294:16): [True: 3, False: 3.95k]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 1]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 10]
Branch (294:16): [True: 0, False: 1]
Branch (294:16): [True: 2, False: 1]
Branch (294:16): [True: 0, False: 2]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 25]
Branch (294:16): [True: 0, False: 4]
Branch (294:16): [True: 1, False: 3]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [Folded - Ignored]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 1, False: 4]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 2]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 46]
Branch (294:16): [True: 0, False: 5]
Branch (294:16): [True: 0, False: 4]
Branch (294:16): [True: 0, False: 1]
Branch (294:16): [True: 0, False: 5]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 1, False: 46]
Branch (294:16): [True: 0, False: 4]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
Branch (294:16): [True: 0, False: 0]
|
295 | 17 | let mut e: Error = e.into(); |
296 | 17 | if let Err(other_err15 ) = other { Branch (296:20): [Folded - Ignored]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 1]
Branch (296:20): [True: 3, False: 0]
Branch (296:20): [True: 4, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 1, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 3, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 2, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 1, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [Folded - Ignored]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 1, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 1]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
Branch (296:20): [True: 0, False: 0]
|
297 | 15 | let mut other_err: Error = other_err; |
298 | 15 | // This will help with knowing which messages are tied to different errors. |
299 | 15 | e.messages.push("---".to_string()); |
300 | 15 | e.messages.append(&mut other_err.messages); |
301 | 15 | }2 |
302 | 17 | return Err(e); |
303 | 5.67k | } |
304 | 5.67k | other |
305 | 5.68k | } |
306 | | } |
307 | | |
308 | | impl<T> ResultExt<T> for Option<T> { |
309 | | #[inline] |
310 | 5.51k | fn err_tip_with_code<F, S>(self, tip_fn: F) -> Result<T, Error> |
311 | 5.51k | where |
312 | 5.51k | Self: Sized, |
313 | 5.51k | S: ToString, |
314 | 5.51k | F: (FnOnce(&Error) -> (Code, S)) + Sized, |
315 | 5.51k | { |
316 | 5.51k | self.ok_or_else(|| { |
317 | 17 | let mut error = Error { |
318 | 17 | code: Code::Internal, |
319 | 17 | messages: vec![], |
320 | 17 | }; |
321 | 17 | let (code, message) = tip_fn(&error); |
322 | 17 | error.code = code; |
323 | 17 | error.messages.push(message.to_string()); |
324 | 17 | error |
325 | 17 | }) |
326 | 5.51k | } |
327 | | } |
328 | | |
329 | | trait CodeExt { |
330 | | fn into_error_kind(self) -> std::io::ErrorKind; |
331 | | } |
332 | | |
333 | | impl CodeExt for Code { |
334 | 1 | fn into_error_kind(self) -> std::io::ErrorKind { |
335 | 1 | match self { |
336 | 0 | Self::Aborted => std::io::ErrorKind::Interrupted, |
337 | 0 | Self::AlreadyExists => std::io::ErrorKind::AlreadyExists, |
338 | 0 | Self::DeadlineExceeded => std::io::ErrorKind::TimedOut, |
339 | 0 | Self::InvalidArgument => std::io::ErrorKind::InvalidInput, |
340 | 0 | Self::NotFound => std::io::ErrorKind::NotFound, |
341 | 0 | Self::PermissionDenied => std::io::ErrorKind::PermissionDenied, |
342 | 0 | Self::Unavailable => std::io::ErrorKind::ConnectionRefused, |
343 | 1 | _ => std::io::ErrorKind::Other, |
344 | | } |
345 | 1 | } |
346 | | } |
347 | | |
348 | | trait ErrorKindExt { |
349 | | fn into_code(self) -> Code; |
350 | | } |
351 | | |
352 | | impl ErrorKindExt for std::io::ErrorKind { |
353 | 12 | fn into_code(self) -> Code { |
354 | 12 | match self { |
355 | 11 | Self::NotFound => Code::NotFound, |
356 | 0 | Self::PermissionDenied => Code::PermissionDenied, |
357 | | Self::ConnectionRefused | Self::ConnectionReset | Self::ConnectionAborted => { |
358 | 0 | Code::Unavailable |
359 | | } |
360 | 0 | Self::AlreadyExists => Code::AlreadyExists, |
361 | 0 | Self::InvalidInput | Self::InvalidData => Code::InvalidArgument, |
362 | 0 | Self::TimedOut => Code::DeadlineExceeded, |
363 | 0 | Self::Interrupted => Code::Aborted, |
364 | | Self::NotConnected |
365 | | | Self::AddrInUse |
366 | | | Self::AddrNotAvailable |
367 | | | Self::BrokenPipe |
368 | | | Self::WouldBlock |
369 | | | Self::WriteZero |
370 | | | Self::Other |
371 | 1 | | Self::UnexpectedEof => Code::Internal, |
372 | 0 | _ => Code::Unknown, |
373 | | } |
374 | 12 | } |
375 | | } |
376 | | |
377 | | // Serde definition for tonic::Code. See: https://serde.rs/remote-derive.html |
378 | | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] |
379 | | #[serde(remote = "Code")] |
380 | | pub enum CodeDef { |
381 | | Ok = 0, |
382 | | Cancelled = 1, |
383 | | Unknown = 2, |
384 | | InvalidArgument = 3, |
385 | | DeadlineExceeded = 4, |
386 | | NotFound = 5, |
387 | | AlreadyExists = 6, |
388 | | PermissionDenied = 7, |
389 | | ResourceExhausted = 8, |
390 | | FailedPrecondition = 9, |
391 | | Aborted = 10, |
392 | | OutOfRange = 11, |
393 | | Unimplemented = 12, |
394 | | Internal = 13, |
395 | | Unavailable = 14, |
396 | | DataLoss = 15, |
397 | | Unauthenticated = 16, |
398 | | // NOTE: Additional codes must be added to stores.rs in ErrorCodes and also |
399 | | // in both match statements in retry.rs. |
400 | | } |