1use thiserror::Error;
2
3#[derive(Debug, Error)]
5pub enum TunnelError {
6 #[error("stream closed")]
7 StreamClosed,
8
9 #[error("connection timeout")]
10 Timeout,
11
12 #[error("authentication failed: {message}")]
13 AuthFailed { message: String },
14
15 #[error("protocol error: {message}")]
16 Protocol { message: String },
17
18 #[error("transport error: {0}")]
19 Transport(#[from] std::io::Error),
20
21 #[error("serialization error: {0}")]
22 Serialization(#[from] serde_json::Error),
23
24 #[error("session offline")]
25 SessionOffline,
26
27 #[error("{0}")]
28 Other(#[from] anyhow::Error),
29}
30
31impl TunnelError {
32 pub fn auth_failed(message: impl Into<String>) -> Self {
33 Self::AuthFailed {
34 message: message.into(),
35 }
36 }
37
38 pub fn protocol(message: impl Into<String>) -> Self {
39 Self::Protocol {
40 message: message.into(),
41 }
42 }
43}
44
45#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
47pub struct ErrorMessage {
48 #[serde(skip_serializing_if = "String::is_empty", default)]
49 pub code: String,
50 pub message: String,
51}
52
53impl ErrorMessage {
54 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
55 Self {
56 code: code.into(),
57 message: message.into(),
58 }
59 }
60}
61
62impl std::fmt::Display for ErrorMessage {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 if self.code.is_empty() {
65 write!(f, "{}", self.message)
66 } else {
67 write!(f, "[{}] {}", self.code, self.message)
68 }
69 }
70}
71
72pub trait MapTunnelErr<T> {
74 fn map_stream_closed(self) -> Result<T, TunnelError>;
76}
77
78impl<T, E> MapTunnelErr<T> for Result<T, E> {
79 fn map_stream_closed(self) -> Result<T, TunnelError> {
80 self.map_err(|_| TunnelError::StreamClosed)
81 }
82}