ockam_node/
error.rs

1use crate::tokio::sync::mpsc::error::SendError;
2use core::fmt;
3use core::time::Duration;
4use ockam_core::{
5    compat::error::Error as StdError,
6    errcode::{Kind, Origin},
7    Address, Error,
8};
9
10/// Enumeration of error causes in ockam_node
11#[allow(clippy::enum_variant_names)]
12#[derive(Clone, Debug)]
13pub enum NodeError {
14    /// An address operation failed
15    ///
16    /// An address either refers to a Worker or a Processor
17    Address(Address),
18    /// A data retrieval operation failed
19    Data,
20    /// A failure occurred because of invalid node state
21    NodeState(NodeReason),
22    /// A failure occurred because of invalid worker state
23    WorkerState(WorkerReason),
24    /// A failure occurred because of invalid address router state
25    RouterState(RouterReason),
26}
27
28impl NodeError {
29    /// Turn a NodeError into a Kind::NotFound ockam_core::Error
30    #[track_caller]
31    pub fn not_found(self) -> Error {
32        Error::new(Origin::Node, Kind::NotFound, self)
33    }
34    /// Turn a NodeError into a Kind::AlreadyExists ockam_core::Error
35    #[track_caller]
36    pub fn already_exists(self) -> Error {
37        Error::new(Origin::Node, Kind::AlreadyExists, self)
38    }
39    /// Turn a NodeError into a Kind::Conflict ockam_core::Error
40    #[track_caller]
41    pub fn conflict(self) -> Error {
42        Error::new(Origin::Node, Kind::Conflict, self)
43    }
44    /// Turn a NodeError into a Kind::Internal ockam_core::Error
45    #[track_caller]
46    pub fn internal(self) -> Error {
47        Error::new(Origin::Node, Kind::Internal, self)
48    }
49    /// Create an ockam_core::Error based on a tokio::SendError
50    #[track_caller]
51    pub(crate) fn from_send_err<T: fmt::Debug>(err: SendError<T>) -> Error {
52        Error::new(
53            Origin::Node,
54            Kind::Internal,
55            NodeError::NodeState(NodeReason::Unknown),
56        )
57        .context("SendError", err)
58    }
59
60    /// Create an ockam_core::Error an elapsed timeout
61    #[track_caller]
62    pub(crate) fn with_timeout(self, duration: Duration) -> Error {
63        Error::new(
64            Origin::Node,
65            Kind::Timeout,
66            format!("timeout: {duration:?}"),
67        )
68        .context("Type", self)
69    }
70}
71
72impl StdError for NodeError {}
73
74impl fmt::Display for NodeError {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(
77            f,
78            "{}",
79            match self {
80                Self::Address(addr) => format!("operation failed for address {}", addr),
81                Self::Data => "failed to load data".into(),
82                Self::NodeState(reason) => format!("failed because node state: {}", reason),
83                Self::WorkerState(reason) => format!("failed because worker state: {}", reason),
84                Self::RouterState(reason) => format!("failed because router state: {}", reason),
85            }
86        )
87    }
88}
89
90/// Reasons why adding an external router has failed
91#[allow(clippy::enum_variant_names)]
92#[derive(Clone, Copy, Debug)]
93pub enum RouterReason {
94    /// A duplicate router was registered
95    Duplicate,
96    /// The provided router address type is not valid
97    InvalidAddrType,
98    /// Empty Address Set
99    EmptyAddressSet,
100}
101
102impl fmt::Display for RouterReason {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        write!(
105            f,
106            "{}",
107            match self {
108                Self::Duplicate => "a router for this type already exists",
109                Self::InvalidAddrType => "you can not register router for this address type",
110                Self::EmptyAddressSet => "address set cannot be empty",
111            }
112        )
113    }
114}
115
116/// Reasons why a generic Ockam Node operation has failed
117///
118/// This includes many of the internal I/O failures
119#[allow(clippy::enum_variant_names)]
120#[derive(Clone, Copy, Debug)]
121pub enum NodeReason {
122    // TODO: currently we just tag all I/O events as "Unknown" but in
123    // the future we should collect more information about WHY a
124    // certain I/O operation failed.
125    /// The node is in an unknown state
126    Unknown,
127    /// The node is shutting down
128    Shutdown,
129    /// The node has been corrupted
130    Corrupt,
131}
132
133impl fmt::Display for NodeReason {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        write!(
136            f,
137            "{}",
138            match self {
139                Self::Unknown => "unknown node state",
140                Self::Shutdown => "ockam node is shutting down",
141                Self::Corrupt => "ockam node is corrupt and can not be recovered",
142            }
143        )
144    }
145}
146
147/// Reasons why a worker operation has failed
148#[allow(clippy::enum_variant_names)]
149#[derive(Clone, Copy, Debug)]
150pub enum WorkerReason {
151    /// The worker is shutting down
152    Shutdown,
153    /// The worker is faulty and waiting for supervisor commands
154    Faulty,
155    /// The worker is otherwise corrupt and can not be recovered
156    Corrupt,
157    /// Couldn't send shutdown signal
158    CtrlChannelError,
159}
160
161impl fmt::Display for WorkerReason {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        write!(
164            f,
165            "{}",
166            match self {
167                Self::Shutdown => "target worker is shutting down",
168                Self::Faulty => "target worker is faulty and waiting for supervisor",
169                Self::Corrupt => "target worker is corrupt and can not be recovered",
170                Self::CtrlChannelError => "target worker cannot receive shutdown signal",
171            }
172        )
173    }
174}