ractor/
errors.rs

1// Copyright (c) Sean Lawlor
2//
3// This source code is licensed under both the MIT license found in the
4// LICENSE-MIT file in the root directory of this source tree.
5
6//! Actor error types
7
8use std::fmt::Display;
9
10use crate::ActorName;
11
12/// Represents an actor's internal processing error
13pub type ActorProcessingErr = Box<dyn std::error::Error + Send + Sync + 'static>;
14
15/// Spawn errors starting an actor
16#[derive(Debug)]
17pub enum SpawnErr {
18    /// Actor panic'd or returned an error during startup
19    StartupFailed(ActorProcessingErr),
20    /// An actor cannot be started > 1 time
21    ActorAlreadyStarted,
22    /// The named actor is already registered in the registry
23    ActorAlreadyRegistered(ActorName),
24}
25
26impl std::error::Error for SpawnErr {
27    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
28        match &self {
29            Self::StartupFailed(inner) => Some(inner.as_ref()),
30            _ => None,
31        }
32    }
33}
34
35impl Display for SpawnErr {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        match self {
38            Self::StartupFailed(panic_msg) => {
39                if f.alternate() {
40                    write!(f, "Actor panicked during startup '{panic_msg:#}'")
41                } else {
42                    write!(f, "Actor panicked during startup '{panic_msg}'")
43                }
44            }
45            Self::ActorAlreadyStarted => {
46                write!(f, "Actor cannot be re-started more than once")
47            }
48            Self::ActorAlreadyRegistered(actor_name) => {
49                write!(
50                    f,
51                    "Actor '{actor_name}' is already registered in the actor registry"
52                )
53            }
54        }
55    }
56}
57
58impl From<crate::registry::ActorRegistryErr> for SpawnErr {
59    fn from(value: crate::registry::ActorRegistryErr) -> Self {
60        match value {
61            crate::registry::ActorRegistryErr::AlreadyRegistered(actor_name) => {
62                SpawnErr::ActorAlreadyRegistered(actor_name)
63            }
64        }
65    }
66}
67
68/// Actor processing loop errors
69#[derive(Debug)]
70pub enum ActorErr {
71    /// Actor had a task cancelled internally during processing
72    Cancelled,
73    /// Actor had an internal panic
74    Failed(ActorProcessingErr),
75}
76
77impl std::error::Error for ActorErr {
78    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
79        match &self {
80            Self::Failed(inner) => Some(inner.as_ref()),
81            _ => None,
82        }
83    }
84}
85
86impl Display for ActorErr {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        match self {
89            Self::Failed(panic_msg) => {
90                if f.alternate() {
91                    write!(f, "Actor panicked '{panic_msg:#}'")
92                } else {
93                    write!(f, "Actor panicked '{panic_msg}'")
94                }
95            }
96            Self::Cancelled => {
97                write!(f, "Actor operation cancelled")
98            }
99        }
100    }
101}
102
103/// A messaging error has occurred
104pub enum MessagingErr<T> {
105    /// The channel you're trying to send a message too has been dropped/closed.
106    /// If you're sending to an [crate::ActorCell] then that means the actor has died
107    /// (failure or not).
108    ///
109    /// Includes the message which failed to send so the caller can perform another operation
110    /// with the message if they want to.
111    SendErr(T),
112
113    /// The channel you're trying to receive from has had all the senders dropped
114    /// and is therefore closed
115    ChannelClosed,
116
117    /// Tried to send a message to an actor with an invalid actor type defined.
118    /// This happens if you have an [crate::ActorCell] which has the type id of its
119    /// handler and you try to use an alternate handler to send a message
120    InvalidActorType,
121}
122
123impl<T> MessagingErr<T> {
124    /// Map any message embedded within the error type. This is primarily useful
125    /// for normalizing an error value if the message is not needed.
126    pub fn map<F, U>(self, mapper: F) -> MessagingErr<U>
127    where
128        F: FnOnce(T) -> U,
129    {
130        match self {
131            MessagingErr::SendErr(err) => MessagingErr::SendErr(mapper(err)),
132            MessagingErr::ChannelClosed => MessagingErr::ChannelClosed,
133            MessagingErr::InvalidActorType => MessagingErr::InvalidActorType,
134        }
135    }
136}
137
138impl<T> std::fmt::Debug for MessagingErr<T> {
139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140        match self {
141            Self::SendErr(_) => write!(f, "SendErr"),
142            Self::ChannelClosed => write!(f, "RecvErr"),
143            Self::InvalidActorType => write!(f, "InvalidActorType"),
144        }
145    }
146}
147
148// SAFETY: This is required in order to map [MessagingErr] to
149// ActorProcessingErr which requires errors to be Sync.
150impl<T> std::error::Error for MessagingErr<T> {}
151
152// SAFETY: This bound will make the MessagingErr be marked as `Sync`,
153// even though all messages must only be `Send`. HOWEVER errors are generally
154// never read in a `Sync` required context, and this bound is only
155// required due to the auto-conversion to `std::error::Error``
156#[allow(unsafe_code)]
157unsafe impl<T> Sync for MessagingErr<T> {}
158
159impl<T> From<tokio::sync::mpsc::error::SendError<T>> for MessagingErr<T> {
160    fn from(e: tokio::sync::mpsc::error::SendError<T>) -> Self {
161        Self::SendErr(e.0)
162    }
163}
164impl<T> From<tokio::sync::mpsc::error::TrySendError<T>> for MessagingErr<T> {
165    fn from(e: tokio::sync::mpsc::error::TrySendError<T>) -> Self {
166        match e {
167            tokio::sync::mpsc::error::TrySendError::Closed(c) => Self::SendErr(c),
168            tokio::sync::mpsc::error::TrySendError::Full(c) => Self::SendErr(c),
169        }
170    }
171}
172
173impl<T> Display for MessagingErr<T> {
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        match self {
176            Self::ChannelClosed => {
177                write!(f, "Messaging failed because channel is closed")
178            }
179            Self::InvalidActorType => {
180                write!(f, "Messaging failed due to the provided actor type not matching the actor's properties")
181            }
182            Self::SendErr(_) => {
183                write!(f, "Messaging failed to enqueue the message to the specified actor, the actor is likely terminated")
184            }
185        }
186    }
187}
188
189/// Error types which can result from Ractor processes
190pub enum RactorErr<T> {
191    /// An error occurred spawning
192    Spawn(SpawnErr),
193    /// An error occurred in messaging (sending/receiving)
194    Messaging(MessagingErr<T>),
195    /// An actor encountered an error while processing (canceled or panicked)
196    Actor(ActorErr),
197    /// A timeout occurred
198    Timeout,
199}
200
201impl<T> RactorErr<T> {
202    /// Identify if the error has a message payload contained. If [true],
203    /// You can utilize `try_get_message` to consume the error and extract the message payload
204    /// quickly.
205    ///
206    /// Returns [true] if the error contains a message payload of type `T`, [false] otherwise.
207    pub fn has_message(&self) -> bool {
208        matches!(self, Self::Messaging(MessagingErr::SendErr(_)))
209    }
210    /// Try and extract the message payload from the contained error. This consumes the
211    /// [RactorErr] instance in order to not have require cloning the message payload.
212    /// Should be used in conjunction with `has_message` to not consume the error if not wanted
213    ///
214    /// Returns [Some(`T`)] if there is a message payload, [None] otherwise.
215    pub fn try_get_message(self) -> Option<T> {
216        if let Self::Messaging(MessagingErr::SendErr(msg)) = self {
217            Some(msg)
218        } else {
219            None
220        }
221    }
222
223    /// Map any message embedded within the error type. This is primarily useful
224    /// for normalizing an error value if the message is not needed.
225    pub fn map<F, U>(self, mapper: F) -> RactorErr<U>
226    where
227        F: FnOnce(T) -> U,
228    {
229        match self {
230            RactorErr::Spawn(err) => RactorErr::Spawn(err),
231            RactorErr::Messaging(err) => RactorErr::Messaging(err.map(mapper)),
232            RactorErr::Actor(err) => RactorErr::Actor(err),
233            RactorErr::Timeout => RactorErr::Timeout,
234        }
235    }
236}
237
238impl<T> std::fmt::Debug for RactorErr<T> {
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240        match self {
241            Self::Messaging(m) => write!(f, "Messaging({m:?})"),
242            Self::Actor(a) => write!(f, "Actor({a:?})"),
243            Self::Spawn(s) => write!(f, "Spawn({s:?})"),
244            Self::Timeout => write!(f, "Timeout"),
245        }
246    }
247}
248
249impl<T> std::error::Error for RactorErr<T> {}
250
251impl<T> From<SpawnErr> for RactorErr<T> {
252    fn from(value: SpawnErr) -> Self {
253        RactorErr::Spawn(value)
254    }
255}
256
257impl<T> From<MessagingErr<T>> for RactorErr<T> {
258    fn from(value: MessagingErr<T>) -> Self {
259        RactorErr::Messaging(value)
260    }
261}
262
263impl<T> From<ActorErr> for RactorErr<T> {
264    fn from(value: ActorErr) -> Self {
265        RactorErr::Actor(value)
266    }
267}
268
269impl<T, TResult> From<crate::rpc::CallResult<TResult>> for RactorErr<T> {
270    fn from(value: crate::rpc::CallResult<TResult>) -> Self {
271        match value {
272            crate::rpc::CallResult::SenderError => {
273                RactorErr::Messaging(MessagingErr::ChannelClosed)
274            }
275            crate::rpc::CallResult::Timeout => RactorErr::Timeout,
276            _ => panic!("A successful `CallResult` cannot be mapped to a `RactorErr`"),
277        }
278    }
279}
280
281impl<T> std::fmt::Display for RactorErr<T> {
282    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283        match self {
284            Self::Actor(actor_err) => {
285                if f.alternate() {
286                    write!(f, "{actor_err:#}")
287                } else {
288                    write!(f, "{actor_err}")
289                }
290            }
291            Self::Messaging(messaging_err) => {
292                if f.alternate() {
293                    write!(f, "{messaging_err:#}")
294                } else {
295                    write!(f, "{messaging_err}")
296                }
297            }
298            Self::Spawn(spawn_err) => {
299                if f.alternate() {
300                    write!(f, "{spawn_err:#}")
301                } else {
302                    write!(f, "{spawn_err}")
303                }
304            }
305            Self::Timeout => {
306                write!(f, "timeout")
307            }
308        }
309    }
310}