1use std::fmt;
7use thiserror::Error;
8
9pub type Result<T> = std::result::Result<T, KcpError>;
10
11#[derive(Error, Debug)]
14pub enum KcpError {
15 #[error("I/O error: {0}")]
16 Io(#[from] std::io::Error),
17
18 #[error("Protocol error: {message}")]
19 Protocol { message: String },
20
21 #[error("Connection error: {kind}")]
22 Connection { kind: ConnectionError },
23
24 #[error("Operation timed out after {timeout_ms}ms")]
25 Timeout { timeout_ms: u64 },
26
27 #[error("Buffer error: {message}")]
28 Buffer { message: String },
29
30 #[error("Configuration error: {message}")]
31 Config { message: String },
32
33 #[error("Session error: {message}")]
34 Session { message: String },
35
36 #[error("Internal error: {message}")]
37 Internal { message: String },
38}
39
40#[derive(Debug, Clone)]
41pub enum ConnectionError {
42 Closed,
43 Reset,
44 Refused,
45 Lost,
46 InvalidConv,
47 HandshakeFailed,
48 Timeout,
49 NotConnected,
50}
51
52impl fmt::Display for ConnectionError {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 Self::Closed => write!(f, "connection closed"),
56 Self::Reset => write!(f, "connection reset by peer"),
57 Self::Refused => write!(f, "connection refused"),
58 Self::Lost => write!(f, "connection lost"),
59 Self::InvalidConv => write!(f, "invalid conversation ID"),
60 Self::HandshakeFailed => write!(f, "handshake failed"),
61 Self::Timeout => write!(f, "connection timed out"),
62 Self::NotConnected => write!(f, "not connected"),
63 }
64 }
65}
66
67impl From<kcp_core::KcpCoreError> for KcpError {
70 fn from(e: kcp_core::KcpCoreError) -> Self {
71 match e {
72 kcp_core::KcpCoreError::Protocol { message } => Self::Protocol { message },
73 kcp_core::KcpCoreError::Buffer { message } => Self::Buffer { message },
74 kcp_core::KcpCoreError::ConnectionLost => {
75 Self::Connection { kind: ConnectionError::Lost }
76 }
77 }
78 }
79}
80
81impl KcpError {
84 pub fn protocol(message: impl Into<String>) -> Self {
85 Self::Protocol { message: message.into() }
86 }
87
88 pub fn connection(kind: ConnectionError) -> Self {
89 Self::Connection { kind }
90 }
91
92 pub fn timeout(timeout_ms: u64) -> Self {
93 Self::Timeout { timeout_ms }
94 }
95
96 pub fn buffer(message: impl Into<String>) -> Self {
97 Self::Buffer { message: message.into() }
98 }
99
100 pub fn config(message: impl Into<String>) -> Self {
101 Self::Config { message: message.into() }
102 }
103
104 pub fn session(message: impl Into<String>) -> Self {
105 Self::Session { message: message.into() }
106 }
107
108 pub fn internal(message: impl Into<String>) -> Self {
109 Self::Internal { message: message.into() }
110 }
111}
112
113impl KcpError {
116 pub fn is_recoverable(&self) -> bool {
117 match self {
118 Self::Io(e) => matches!(
119 e.kind(),
120 std::io::ErrorKind::WouldBlock
121 | std::io::ErrorKind::TimedOut
122 | std::io::ErrorKind::Interrupted
123 ),
124 Self::Timeout { .. } | Self::Buffer { .. } => true,
125 _ => false,
127 }
128 }
129
130 pub fn is_connection_error(&self) -> bool {
131 matches!(self, Self::Connection { .. })
132 }
133
134 pub fn is_fatal(&self) -> bool {
135 matches!(
136 self,
137 Self::Connection {
138 kind: ConnectionError::Lost | ConnectionError::Closed | ConnectionError::Reset
139 } | Self::Internal { .. }
140 )
141 }
142
143 pub fn is_closed(&self) -> bool {
144 match self {
145 Self::Connection { kind } => matches!(
146 kind,
147 ConnectionError::Closed | ConnectionError::Reset | ConnectionError::Refused
148 ),
149 Self::Io(e) => matches!(
150 e.kind(),
151 std::io::ErrorKind::BrokenPipe
152 | std::io::ErrorKind::ConnectionAborted
153 | std::io::ErrorKind::ConnectionRefused
154 | std::io::ErrorKind::ConnectionReset
155 | std::io::ErrorKind::UnexpectedEof
156 ),
157 _ => false,
158 }
159 }
160}