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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
//! Ockam error code enumerations.
//!
//! These are deliberately abstract, and do not cover all possible errors in
//! detail. Some of the motivation behind this includes:
//!
//! 1. Allow code which wishes to categorize, filter, or otherwise handle errors
//!    to do so generically without forcing them to hard-code numeric values.
//! 2. To avoid each component needing to choose globally unique error numbers.
use serde::{Deserialize, Serialize};

/// A set of abstract error codes describing an error. See the [module-level
/// documentation](crate::error::codes) for details.
///
/// The fields of this struct are `pub` for matching, but you need to go through
/// one of the [constructor functions](ErrorCode::new) to create one of these
/// (and not a literal), as it is a `#[non_exhaustive]` type (which may change in
/// the future, since it's unclear if this provides value).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ErrorCode {
    // Maintenance note: Don't reorder these fields or cfg them out, it's
    // somewhat important for serialization (at least via BARE) that these are
    // the same in all configurations -- therefore, all items in this struct
    // should be reasonable to include on embedded as well (going any higher
    // than 64 bits seems likely to be too far in that context -- even this much
    // is perhaps pushing it).
    /// The [`Origin`] of this error.
    pub origin: Origin,
    /// The [`Kind`] of this error.
    pub kind: Kind,
    /// An additional identifying numeric payload, or 0 if none is relevant.
    ///
    /// For example, it would be reasonable for this field to hold:
    /// - HTTP status code
    /// - OS `errno`/`GetLastError` code
    /// - The exit status returned by a subprocess
    /// - A numeric error code from some other system
    /// - Et cetera.
    ///
    /// But should generally not be used to hold non-identifying metadata, such
    /// as the date, device IDs, as that information should be stored on the
    /// payload itself.
    ///
    /// Concretely: two `ErrorCode` with different `extra` values should
    /// identify types of errors.
    // TODO: is 32 bits okay on embedded? This puts us to 64 bits for the
    // structure in practice, but means that we'll alwasy be able to hold OS
    // errors, as well as `old_error::Error::code`.
    pub extra: i32,
}

impl ErrorCode {
    /// Construct the `ErrorCode` for an error.
    #[cold]
    pub fn new(origin: Origin, kind: Kind) -> Self {
        Self {
            origin,
            kind,
            extra: 0,
        }
    }
    /// Construct the `ErrorCode` for an error which contains an additional
    /// numeric payload.
    #[cold]
    pub fn new_with_extra(origin: Origin, kind: Kind, extra: i32) -> Self {
        Self {
            origin,
            kind,
            extra,
        }
    }

    /// Construct an error code with very little useful information
    #[cold]
    pub fn unknown() -> Self {
        Self {
            origin: Origin::Unknown,
            kind: Kind::Unknown,
            extra: 0,
        }
    }

    /// Attach an origin and/or kind to the error, without risk of overwriting more
    /// precise information value.
    #[must_use]
    pub fn update_unknown(
        mut self,
        o: impl Into<Option<Origin>>,
        k: impl Into<Option<Kind>>,
    ) -> Self {
        if let (Origin::Unknown, Some(o)) = (self.origin, o.into()) {
            self.origin = o;
        }
        if let (Kind::Unknown, Some(k)) = (self.kind, k.into()) {
            self.kind = k;
        }
        self
    }
}

/// Origin indicates the abstract source of an error.
///
/// Note that [`Error`](super::Error) should already contain precise origin
/// information (file, line) where the error originated from.
///
// Internal note: Once we stabilise the API, we should not remove these, just stop emitting them.
#[repr(u8)]
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Serialize, Deserialize)]
pub enum Origin {
    /// An error for which there is no way to determine a more specific origin.
    ///
    /// Eventually this should also be used for errors which, during
    /// deserialization, have an unknown `Origin` (for now this is too
    /// error-prone for various reasons).
    Unknown = 0,
    /// Reserved for errors emitted by applications using ockam.
    Application = 1,
    /// An error originating from the vault.
    Vault = 2,
    /// Errors emitted by the transport layer.
    Transport = 3,
    /// Errors from some part of the node implementation — the router or relay,
    /// for example.
    Node = 4,
    /// Errors from the surface API — for example: the FFI layer.
    Api = 5,
    /// Errors from within the identity-management code.
    Identity = 6,
    /// Errors from the secure channel implementation.
    Channel = 7,
    /// Errors occurring from the one of the key exchange implementations.
    KeyExchange = 8,
    /// An error which occurs in the executor (e.g. `ockam_executor`, since
    /// `tokio` errors will likely come from elsewhere).
    Executor = 9,
    /// Other errors from within `ockam` or `ockam_core`.
    Core = 10,
    /// Ockam protocol crate
    Ockam = 11,
    /// Errors from other sources, such as libraries extending `ockam`.
    ///
    /// Note: The actual source (file, line, ...) will (hopefully) be available
    /// on the error itself, as one of the members of the payload.
    Other = 12,
    // This is a `#[non_exhaustive]` enum — we're free to add more variants
    // here. Do not add any which contain payloads (it should stay a "C style
    // enum"). Payload information should be added to the error itself.
}

/// Category indicates "what went wrong", in abstract terms.
///
/// # Choosing a `Kind`
///
/// - [`Kind::Io`], [`Kind::Protocol`], and [`Kind::Other`] should only be used
///   if there's no more specific option.
///
///     For example, a network timeout is a type of IO error, however it should
///     use [`Kind::Timeout`] rather than [`Kind::Io`].
///
/// - [`Kind::Invalid`] should be used when the input will never be valid (at
///   least in this version of the software), rather than input which is invalid
///   because of the current system state.
///
///     For example, an unknown identifier should use [`Kind::NotFound`] (for
///     example `ockam_vault_core`'s `Secret`) rather than [`Kind::Invalid`],
///
/// - [`Kind::Cancelled`], [`Kind::Timeout`], and [`Kind::Shutdown`] all sound
///   similar, but:
///
///     - [`Kind::Timeout`] should be used to map operations which timeout
///       externally, such as network requests. These may succeed if retried.
///
///     - [`Kind::Shutdown`] should be used to indicate the operation failed due
///       to the node shutting down.
///
///     - [`Kind::Cancelled`] is used when a request to cancel the operation
///       comes in while the operation is in progress.
#[repr(u8)]
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Kind {
    /// Indicates that there is no way to determine a more specific kind.
    // Internal note: We should not emit this from within the
    // `build-trust/ockam` crates.
    Unknown = 0,

    /// Used for serious internal errors, including panics.
    ///
    /// This generally should not be used unless we'd accept a bug report for
    /// the error.
    Internal = 1,

    /// The input was fundamentally invalid.
    ///
    /// For example, this is appropriate to use when:
    ///
    /// - A null pointer being passed as input in the FFI.
    /// - A string is used as input which is not UTF-8.
    /// - Parse failures of various kinds, but be sure to include more specific
    ///   information in the [`Error`](crate::Error) payload.
    ///
    /// Note that it is not appropriate for input which is invalid only due to
    /// the current system state.
    // Internal note: Check if there's a more specific `Kind` before using this
    // — it should mostly be `Origin
    Invalid = 2,

    /// The requested operation is not supported/implemented by that component.
    ///
    /// For example, this is appropriate for a component (for example, a custom
    /// Vault) which do not implement the entire API surface.
    Unsupported = 3,

    /// Some referenced entity was not found.
    ///
    /// For example, this may be appropriate for:
    ///
    /// - A vault which does not recognize a `Secret` which it recieves.
    /// - FFI that recieves an integer `handle` that does not belong to any
    ///   known entity.
    /// - Local [`Address`](crate::Address) which don't correspond to any known
    ///   `Worker` or `Processor`.
    /// - [`Address`](crate::Address) with a transport of an unknown or
    ///   unsupported type.
    ///
    /// Information about what exactly it is that could not be located should be
    /// available on the [`Error`](crate::Error) itself.
    NotFound = 4,

    /// The operation failed because
    ///
    /// For example, this may be appropriate for:
    ///
    /// - A vault which does not recognize a `Secret` which it recieves.
    /// - FFI that recieves an integer `handle` that does not belong to any
    ///   known entity.
    /// - Local [`Address`](crate::Address) which don't correspond to any known
    ///   `Worker` or `Processor`.
    /// - [`Address`](crate::Address) with a transport of an unknown or
    ///   unsupported type.
    ///
    /// Information about what exactly it is that could not be located should be
    /// available on the [`Error`](crate::Error) itself.
    AlreadyExists = 5,

    /// Indicates that some resource has been exhausted, or would be exhausted
    /// if the request were to be fulfilled.
    ///
    /// The resource in question could be memory, open file descriptors,
    /// storage, quota, simultaneous in-flight messages or tasks...
    ///
    /// Information about which resource it was that was exhausted should be
    /// available on the [`Error`](crate::Error) itself.
    ResourceExhausted = 6,

    /// An API was misused in some unspecific fashion.
    ///
    /// This is mostly intended for FFI and other non-Rust bindings — for
    /// example, it would be appropriate to map [`core::cell::BorrowError`] to
    /// this.
    // Internal note: Check if there's a more specific `Kind` before using this.
    Misuse = 7,

    /// Indicates the operation failed due to a cancellation request.
    ///
    /// See the type documentation on the difference between this,
    /// [`Kind::Shutdown`] and [`Kind::Timeout`].
    Cancelled = 8,

    /// Indicates that the operation failed due to the node shutting down.
    ///
    /// See the type documentation on the difference between this,
    /// [`Kind::Cancelled`] and [`Kind::Timeout`].
    Shutdown = 9,

    /// Indicates that the operation failed due to an external operation timing
    /// out, such as a network request, which may succeed if retried.
    ///
    /// See the type documentation on the difference between this,
    /// [`Kind::Shutdown`] and [`Kind::Cancelled`].
    Timeout = 10,

    /// Indicates an operation failed due to simultaneous attempts to modify a
    /// resource.
    Conflict = 11,

    /// Indicates an a failure to deserialize a message (or in rare cases,
    /// failure to serialize).
    Serialization = 12,

    /// Indicates some other I/O error.
    ///
    /// Specifics should be available on error payload.
    // Internal note: Check if there's a more specific `Kind` before using this.
    Io = 13,

    /// Indicates some other I/O error.
    ///
    /// Specifics should be available on error payload.
    // Internal note: Check if there's a more specific `Kind` before using this.
    Protocol = 14,

    /// Indicates an error that
    ///
    /// Specifics should be available on error payload.
    // Internal note: Check if there's a more specific `Kind` before using this.
    Other = 15,
    // This is a `#[non_exhaustive]` enum — we're free to add more variants
    // here. Do not add any which contain payloads (it should stay a "C style
    // enum"). Payload information should be added to the error itself.
    //
    // That said, until we finish migrating over to this, it's expected that
    // we'll need to add several new variants to all of these.
}

// Helper macro for converting a number into an enum variant with that value.
// Variants do not need to be contiguous. Requires listing the error variants
// again, but forces a compile-time error if the list is missing a variant.
macro_rules! from_prim {
    ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{
        // Force a compile error if the list gets out of date.
        const _: fn(e: $Enum) = |e: $Enum| match e {
            $($Enum::$Variant => ()),*
        };
        match $prim {
            $(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)*
            _ => None,
        }
    }}
}

impl Origin {
    /// Attempt to convert a numeric value into an `Origin`.
    ///
    /// `From<u8>` is also implemented, replacing unknown inputs with
    /// `Self::Unknown`.
    #[track_caller]
    pub fn from_u8(n: u8) -> Option<Self> {
        from_prim!(n => Origin {
            Unknown,
            Application,
            Vault,
            Transport,
            Node,
            Api,
            Identity,
            Channel,
            KeyExchange,
            Executor,
            Core,
            Ockam,
            Other,
        })
    }
}

impl From<u8> for Origin {
    #[track_caller]
    fn from(src: u8) -> Self {
        match Self::from_u8(src) {
            Some(n) => n,
            None => {
                warn!("Unknown error origin: {}", src);
                Self::Unknown
            }
        }
    }
}

impl Kind {
    /// Attempt to construct a `Kind` from the numeric value.
    pub fn from_u8(n: u8) -> Option<Self> {
        from_prim!(n => Kind {
            Unknown,
            Internal,
            Invalid,
            Unsupported,
            NotFound,
            AlreadyExists,
            ResourceExhausted,
            Misuse,
            Cancelled,
            Shutdown,
            Timeout,
            Conflict,
            Io,
            Protocol,
            Serialization,
            Other
        })
    }
}

impl From<u8> for Kind {
    #[track_caller]
    fn from(src: u8) -> Self {
        match Self::from_u8(src) {
            Some(n) => n,
            None => {
                warn!("Unknown error origin: {}", src);
                Self::Unknown
            }
        }
    }
}

impl core::fmt::Display for ErrorCode {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        // Kind of halfway between debug and display, TBH, but it's only used in
        // the Error debug output.
        write!(f, "[Origin::{:?}; Kind::{:?}", self.origin, self.kind,)?;
        if self.extra != 0 {
            write!(f, "; code = {}]", self.extra)
        } else {
            write!(f, "]")
        }
    }
}