1use thiserror::Error;
15
16use crate::pdu::AkkaPdu;
17use crate::transport::TransportError;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21#[non_exhaustive]
22pub enum RemoteErrorKind {
23 Handshake,
24 Quarantined,
25 Tombstoned,
26 UnknownPdu,
27 Codec,
28 Transport,
29 Closed,
30 Timeout,
31 BackPressure,
33 Other,
35}
36
37impl RemoteErrorKind {
38 pub fn as_str(self) -> &'static str {
39 match self {
40 Self::Handshake => "handshake",
41 Self::Quarantined => "quarantined",
42 Self::Tombstoned => "tombstoned",
43 Self::UnknownPdu => "unknown_pdu",
44 Self::Codec => "codec",
45 Self::Transport => "transport",
46 Self::Closed => "closed",
47 Self::Timeout => "timeout",
48 Self::BackPressure => "back_pressure",
49 Self::Other => "other",
50 }
51 }
52}
53
54#[derive(Debug, Error)]
59#[error("{kind:?}: {message}")]
60#[non_exhaustive]
61pub struct RemoteError {
62 pub kind: RemoteErrorKind,
63 pub message: String,
64}
65
66impl RemoteError {
67 pub fn new(kind: RemoteErrorKind, message: impl Into<String>) -> Self {
68 Self { kind, message: message.into() }
69 }
70
71 pub fn unknown_pdu(pdu: &AkkaPdu) -> Self {
75 Self::new(RemoteErrorKind::UnknownPdu, format!("unexpected PDU: {pdu:?}"))
76 }
77
78 pub fn quarantined(target: impl std::fmt::Display) -> Self {
79 Self::new(RemoteErrorKind::Quarantined, format!("{target} is quarantined"))
80 }
81
82 pub fn tombstoned(target: impl std::fmt::Display) -> Self {
83 Self::new(RemoteErrorKind::Tombstoned, format!("{target} is tombstoned"))
84 }
85}
86
87impl From<TransportError> for RemoteError {
88 fn from(e: TransportError) -> Self {
89 let (kind, msg) = match &e {
90 TransportError::HandshakeRejected(s) => (RemoteErrorKind::Handshake, s.clone()),
91 TransportError::Closed => (RemoteErrorKind::Closed, "transport closed".into()),
92 other => (RemoteErrorKind::Transport, other.to_string()),
93 };
94 Self::new(kind, msg)
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn from_transport_handshake_rejected() {
104 let t = TransportError::HandshakeRejected("bad cookie".into());
105 let r: RemoteError = t.into();
106 assert_eq!(r.kind, RemoteErrorKind::Handshake);
107 assert!(r.message.contains("bad cookie"));
108 }
109
110 #[test]
111 fn quarantined_constructs() {
112 let r = RemoteError::quarantined("akka.tcp://Sys@host:7355");
113 assert_eq!(r.kind, RemoteErrorKind::Quarantined);
114 assert!(r.message.contains("host:7355"));
115 }
116
117 #[test]
118 fn kind_strings_stable() {
119 assert_eq!(RemoteErrorKind::Handshake.as_str(), "handshake");
120 assert_eq!(RemoteErrorKind::UnknownPdu.as_str(), "unknown_pdu");
121 }
122}