1use std::{
2 fmt::{Display, Formatter, Result as FmtResult},
3 io::{self, ErrorKind},
4 result,
5};
6
7#[cfg(feature = "serde")]
8use rmp_serde::{decode::Error as RmpSerdeDecodeError, encode::Error as RmpSerdeEncodeError};
9use rmpv::{Value, decode::Error as RmpvDecodeError, encode::Error as RmpvEncodeError};
10use thiserror::Error;
11use tokio::task::JoinError;
12
13#[derive(Debug, Error)]
15pub enum ProtocolError {
16 #[error("Invalid message format")]
18 InvalidMessageFormat,
19
20 #[error("Empty message array")]
22 EmptyMessageArray,
23
24 #[error("Invalid message type: {0}")]
26 InvalidMessageType(u64),
27
28 #[error("Invalid {kind} message length")]
30 InvalidMessageLength {
31 kind: &'static str,
33 },
34
35 #[error("Invalid {kind} {field}")]
37 InvalidMessageField {
38 kind: &'static str,
40 field: &'static str,
42 },
43
44 #[error("Depth limit exceeded")]
46 DepthLimitExceeded,
47
48 #[error("No listener configured")]
50 ListenerNotConfigured,
51
52 #[error("Listener has no SocketAddr")]
54 MissingSocketAddr,
55
56 #[error("Expected exactly one parameter")]
58 ExpectedSingleParameter,
59
60 #[error("Resource already taken: {resource}")]
62 ResourceAlreadyTaken {
63 resource: &'static str,
65 },
66
67 #[error("Task '{task}' failed: {source}")]
69 TaskFailed {
70 task: &'static str,
72 #[source]
74 source: JoinError,
75 },
76
77 #[error("Unexpected response id: {id}")]
79 UnexpectedResponse {
80 id: u32,
82 },
83
84 #[error("Malformed message: {0}")]
86 MalformedMessage(String),
87}
88
89impl From<&str> for ProtocolError {
90 fn from(message: &str) -> Self {
91 Self::MalformedMessage(message.to_string())
92 }
93}
94
95impl From<String> for ProtocolError {
96 fn from(message: String) -> Self {
97 Self::MalformedMessage(message)
98 }
99}
100
101#[derive(Error, Debug)]
103pub enum RpcError {
104 #[error("I/O error: {0}")]
106 Io(io::Error),
107
108 #[error("Connection failed")]
110 Connect {
111 #[source]
113 source: io::Error,
114 },
115
116 #[error("Serialization error: {0}")]
118 Serialization(#[from] RmpvEncodeError),
119
120 #[error("Deserialization error: {0}")]
122 Deserialization(#[from] RmpvDecodeError),
123
124 #[cfg(feature = "serde")]
126 #[error("Request serialization error: {0}")]
127 RequestSerialization(#[from] RmpSerdeEncodeError),
128
129 #[cfg(feature = "serde")]
131 #[error("Response deserialization error: {0}")]
132 ResponseDeserialization(#[from] RmpSerdeDecodeError),
133
134 #[error(transparent)]
136 Protocol(#[from] ProtocolError),
137
138 #[error("Service error: {0}")]
140 Service(ServiceError),
141
142 #[error("Connection disconnected")]
144 Disconnect {
145 #[source]
147 source: Option<io::Error>,
148 },
149}
150
151#[derive(Error, Debug)]
158pub struct ServiceError {
159 pub name: String,
161 pub value: Value,
163}
164
165impl ServiceError {
166 pub fn method_not_found(method: &str) -> Self {
168 Self {
169 name: "MethodNotFound".to_string(),
170 value: Value::String(format!("Method '{}' not found", method).into()),
171 }
172 }
173}
174
175impl Display for ServiceError {
176 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
177 write!(f, "Service error {}: {:?}", self.name, self.value)
178 }
179}
180
181impl From<ServiceError> for Value {
182 fn from(error: ServiceError) -> Self {
183 Self::Map(vec![
184 (Self::String("name".into()), Self::String(error.name.into())),
185 (Self::String("value".into()), error.value),
186 ])
187 }
188}
189
190impl TryFrom<Value> for ServiceError {
191 type Error = Value;
192
193 fn try_from(value: Value) -> result::Result<Self, Self::Error> {
194 if let Value::Map(map) = &value {
195 let mut name = None;
196 let mut service_value = None;
197
198 for (key, entry) in map {
199 match key.as_str() {
200 Some("name") => {
201 name = entry.as_str().map(ToOwned::to_owned);
202 }
203 Some("value") => {
204 service_value = Some(entry.clone());
205 }
206 _ => {}
207 }
208 }
209
210 if let (Some(name), Some(service_value)) = (name, service_value) {
211 return Ok(Self {
212 name,
213 value: service_value,
214 });
215 }
216 }
217 Err(value)
218 }
219}
220
221impl RpcError {
222 pub(crate) fn task_failed(task: &'static str, source: JoinError) -> Self {
224 Self::Protocol(ProtocolError::TaskFailed { task, source })
225 }
226
227 pub(crate) fn resource_already_taken(resource: &'static str) -> Self {
229 Self::Protocol(ProtocolError::ResourceAlreadyTaken { resource })
230 }
231
232 pub(crate) fn from_remote_error_value(value: Value) -> Self {
238 match ServiceError::try_from(value) {
239 Ok(service_error) => Self::Service(service_error),
240 Err(Value::Map(map)) => Self::Service(ServiceError {
241 name: "UnknownError".to_string(),
242 value: Value::Map(map),
243 }),
244 Err(original_value) => Self::Service(ServiceError {
245 name: "RemoteError".to_string(),
246 value: original_value,
247 }),
248 }
249 }
250}
251
252impl From<io::Error> for RpcError {
253 fn from(error: io::Error) -> Self {
254 match error.kind() {
255 ErrorKind::UnexpectedEof
256 | ErrorKind::BrokenPipe
257 | ErrorKind::ConnectionAborted
258 | ErrorKind::ConnectionReset
259 | ErrorKind::NotConnected => Self::Disconnect {
260 source: Some(error),
261 },
262 _ => Self::Io(error),
263 }
264 }
265}
266
267pub type Result<T> = result::Result<T, RpcError>;
269
270#[cfg(test)]
271mod tests {
272 use futures::future::pending;
273
274 use super::*;
275
276 #[tokio::test]
277 async fn test_task_failed_wraps_join_error() {
278 let handle = tokio::spawn(async {
279 pending::<()>().await;
280 });
281 handle.abort();
282 let join_error = handle.await.unwrap_err();
283
284 let error = RpcError::task_failed("demo task", join_error);
285
286 match error {
287 RpcError::Protocol(ProtocolError::TaskFailed { task, source }) => {
288 assert_eq!(task, "demo task");
289 assert!(source.is_cancelled());
290 }
291 other => panic!("expected task failure, got {other:?}"),
292 }
293 }
294
295 #[test]
296 fn test_resource_already_taken_uses_protocol_error() {
297 let error = RpcError::resource_already_taken("message receiver");
298
299 match error {
300 RpcError::Protocol(ProtocolError::ResourceAlreadyTaken { resource }) => {
301 assert_eq!(resource, "message receiver");
302 }
303 other => panic!("expected resource-taken error, got {other:?}"),
304 }
305 }
306
307 #[test]
308 fn test_method_not_found_helper_uses_standard_shape() {
309 let error = ServiceError::method_not_found("missing");
310
311 assert_eq!(error.name, "MethodNotFound");
312 assert_eq!(
313 error.value,
314 Value::String("Method 'missing' not found".into())
315 );
316 }
317
318 #[test]
319 fn test_service_error_round_trip() {
320 let error = ServiceError {
321 name: "MethodNotFound".to_string(),
322 value: Value::from("missing"),
323 };
324
325 let encoded = Value::from(error);
326 let decoded = ServiceError::try_from(encoded).unwrap();
327
328 assert_eq!(decoded.name, "MethodNotFound");
329 assert_eq!(decoded.value, Value::from("missing"));
330 }
331
332 #[test]
333 fn test_service_error_try_from_requires_name_and_value() {
334 let missing_name = Value::Map(vec![(Value::from("value"), Value::from("missing"))]);
335 assert!(ServiceError::try_from(missing_name).is_err());
336
337 let missing_value = Value::Map(vec![(Value::from("name"), Value::from("SomeError"))]);
338 assert!(ServiceError::try_from(missing_value).is_err());
339 }
340
341 #[test]
342 fn test_from_remote_error_value_preserves_service_errors() {
343 let value = Value::Map(vec![
344 (Value::from("name"), Value::from("SomeError")),
345 (Value::from("value"), Value::from("payload")),
346 ]);
347
348 let error = RpcError::from_remote_error_value(value);
349
350 match error {
351 RpcError::Service(service_error) => {
352 assert_eq!(service_error.name, "SomeError");
353 assert_eq!(service_error.value, Value::from("payload"));
354 }
355 other => panic!("expected service error, got {other:?}"),
356 }
357 }
358
359 #[test]
360 fn test_from_remote_error_value_uses_fallback_names() {
361 let malformed_map = Value::Map(vec![(Value::from("value"), Value::from("payload"))]);
362 let error = RpcError::from_remote_error_value(malformed_map);
363
364 match error {
365 RpcError::Service(service_error) => {
366 assert_eq!(service_error.name, "UnknownError");
367 assert_eq!(
368 service_error.value,
369 Value::Map(vec![(Value::from("value"), Value::from("payload"),)])
370 );
371 }
372 other => panic!("expected service error, got {other:?}"),
373 }
374
375 let scalar_error = RpcError::from_remote_error_value(Value::from("boom"));
376 match scalar_error {
377 RpcError::Service(service_error) => {
378 assert_eq!(service_error.name, "RemoteError");
379 assert_eq!(service_error.value, Value::from("boom"));
380 }
381 other => panic!("expected service error, got {other:?}"),
382 }
383 }
384}