Skip to main content

ts_runtime/
error.rs

1use core::fmt::{Formatter, Write};
2
3use kameo::{
4    Actor,
5    actor::{ActorRef, WeakActorRef},
6    error::SendError,
7};
8
9pub(crate) trait ResultExt {
10    type T;
11
12    /// Attach actor info to this result if it's an error.
13    fn with_actor_info(self, aref: impl Into<ActorInfo>) -> Result<Self::T, Error>;
14}
15
16impl<T, E> ResultExt for Result<T, E>
17where
18    E: Into<Error>,
19{
20    type T = T;
21
22    fn with_actor_info(self, aref: impl Into<ActorInfo>) -> Result<T, Error> {
23        self.map_err(|e| e.into().with_actor_info(aref))
24    }
25}
26
27/// Errors that may occur while executing or interacting with the runtime.
28#[derive(Debug, Copy, Clone, PartialEq, Eq)]
29pub struct Error {
30    /// Kind of error this is.
31    pub kind: ErrorKind,
32
33    /// Rust type name of sent message that caused the error.
34    ///
35    /// May not be populated if this was not generated as a result of sending a message.
36    pub message_ty: Option<&'static str>,
37
38    /// Actor to which the message was being sent (optional).
39    ///
40    /// May not be populated if either this wasn't from an actor message, or the actor ref
41    /// wasn't available when the error was constructed.
42    pub target_actor: Option<ActorInfo>,
43}
44
45impl Error {
46    /// Attach information about a destination actor to this error.
47    ///
48    /// Typically, `aref` will be `&ActorRef`.
49    pub fn with_actor_info(self, aref: impl Into<ActorInfo>) -> Self {
50        Self {
51            target_actor: Some(aref.into()),
52            ..self
53        }
54    }
55}
56
57impl core::fmt::Display for Error {
58    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
59        // format is "sending MESSAGE_TYPE to ACTOR: ERROR_DESC", the complexity is to account for
60        // independently-optional fields
61
62        if let Some(ty) = self.message_ty {
63            write!(f, "sending {ty}")?;
64
65            if self.target_actor.is_some() {
66                f.write_char(' ')?;
67            }
68        }
69
70        if let Some(actor) = &self.target_actor {
71            write!(f, "{actor}")?;
72        }
73
74        if self.message_ty.is_some() || self.target_actor.is_some() {
75            f.write_str(": ")?;
76        }
77
78        self.kind.fmt(f)
79    }
80}
81
82impl core::error::Error for Error {}
83
84impl<M, E> From<SendError<M, E>> for Error {
85    fn from(err: SendError<M, E>) -> Self {
86        Self {
87            kind: err.into(),
88            message_ty: Some(core::any::type_name::<M>()),
89            target_actor: None,
90        }
91    }
92}
93
94/// Info about an actor that caused an error.
95#[derive(Debug, Copy, Clone, PartialEq, Eq)]
96pub struct ActorInfo {
97    /// The Rust type name of the actor.
98    pub ty: &'static str,
99
100    /// The actor's unique id.
101    pub id: kameo::actor::ActorId,
102}
103
104impl<A> From<&ActorRef<A>> for ActorInfo
105where
106    A: Actor,
107{
108    fn from(aref: &ActorRef<A>) -> Self {
109        Self {
110            ty: core::any::type_name::<A>(),
111            id: aref.id(),
112        }
113    }
114}
115
116impl<A> From<ActorRef<A>> for ActorInfo
117where
118    A: Actor,
119{
120    fn from(aref: ActorRef<A>) -> Self {
121        Self::from(&aref)
122    }
123}
124
125impl<A> From<&WeakActorRef<A>> for ActorInfo
126where
127    A: Actor,
128{
129    fn from(aref: &WeakActorRef<A>) -> Self {
130        Self {
131            ty: core::any::type_name::<A>(),
132            id: aref.id(),
133        }
134    }
135}
136
137impl<A> From<WeakActorRef<A>> for ActorInfo
138where
139    A: Actor,
140{
141    fn from(aref: WeakActorRef<A>) -> Self {
142        Self::from(&aref)
143    }
144}
145
146impl core::fmt::Display for ActorInfo {
147    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
148        write!(f, "{}({})", self.ty, self.id)
149    }
150}
151
152/// Kinds of errors that may occur while running the runtime.
153#[derive(Debug, Copy, Clone, PartialEq, Eq)]
154pub enum ErrorKind {
155    /// An actor that was expected to be available was unreachable, probably because it
156    /// either panicked or exited. It's unlikely this operation can be retried successfully.
157    ActorGone,
158
159    /// The actor replied with an error.
160    ReplyErr,
161
162    /// The target actor's mailbox was full.
163    MailboxFull,
164
165    /// An operation timed out.
166    Timeout,
167
168    /// A netstack-only operation was attempted on a runtime running in TUN transport mode (no
169    /// userspace application netstack exists in that mode).
170    UnsupportedInTunMode,
171
172    /// TUN transport mode was requested but the TUN device could not be created (e.g. missing
173    /// root/CAP_NET_ADMIN), or the runtime was built without the `tun` feature.
174    TunUnavailable,
175}
176
177impl core::fmt::Display for ErrorKind {
178    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
179        match self {
180            Self::ActorGone => write!(f, "expected actor is unreachable"),
181            Self::ReplyErr => write!(f, "actor replied with an error"),
182            Self::MailboxFull => write!(f, "actor's mailbox was full"),
183            Self::Timeout => write!(f, "operation timed out"),
184            Self::UnsupportedInTunMode => {
185                write!(f, "netstack operation unsupported in TUN transport mode")
186            }
187            Self::TunUnavailable => write!(f, "TUN transport unavailable"),
188        }
189    }
190}
191
192impl<M, E> From<SendError<M, E>> for ErrorKind {
193    fn from(err: SendError<M, E>) -> Self {
194        match err {
195            SendError::ActorNotRunning(_) | SendError::ActorStopped => ErrorKind::ActorGone,
196            SendError::HandlerError(_) => ErrorKind::ReplyErr,
197            SendError::MailboxFull(..) => ErrorKind::MailboxFull,
198            SendError::Timeout(_) => ErrorKind::Timeout,
199        }
200    }
201}