1use crate::types::ReturnCode;
4use std::io;
5use thiserror::Error;
6
7#[derive(Error, Debug)]
9pub enum SomeIpError {
10 #[error("I/O error: {0}")]
12 Io(#[from] io::Error),
13
14 #[error("Invalid header: {0}")]
16 InvalidHeader(String),
17
18 #[error("Unknown message type: 0x{0:02X}")]
20 UnknownMessageType(u8),
21
22 #[error("Unknown return code: 0x{0:02X}")]
24 UnknownReturnCode(u8),
25
26 #[error("Wrong protocol version: expected 0x01, got 0x{0:02X}")]
28 WrongProtocolVersion(u8),
29
30 #[error("Message too short: expected at least {expected} bytes, got {actual}")]
32 MessageTooShort { expected: usize, actual: usize },
33
34 #[error("Message length mismatch: header says {header_length} bytes, got {actual_length}")]
36 LengthMismatch {
37 header_length: u32,
38 actual_length: usize,
39 },
40
41 #[error("Payload too large: {size} bytes exceeds maximum of {max} bytes")]
43 PayloadTooLarge { size: usize, max: usize },
44
45 #[error("Protocol error: {0:?}")]
47 ProtocolError(ReturnCode),
48
49 #[error("Connection closed")]
51 ConnectionClosed,
52
53 #[error("Operation timed out")]
55 Timeout,
56
57 #[error("No response received for request (client={client_id:04X}, session={session_id:04X})")]
59 NoResponse { client_id: u16, session_id: u16 },
60}
61
62pub type Result<T> = std::result::Result<T, SomeIpError>;
64
65impl SomeIpError {
66 pub fn invalid_header(msg: impl Into<String>) -> Self {
68 Self::InvalidHeader(msg.into())
69 }
70
71 pub fn is_recoverable(&self) -> bool {
73 matches!(
74 self,
75 Self::Io(e) if e.kind() == io::ErrorKind::WouldBlock
76 || e.kind() == io::ErrorKind::TimedOut
77 || e.kind() == io::ErrorKind::Interrupted
78 ) || matches!(self, Self::Timeout)
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_error_display() {
88 let err = SomeIpError::UnknownMessageType(0xFF);
89 assert_eq!(format!("{err}"), "Unknown message type: 0xFF");
90
91 let err = SomeIpError::MessageTooShort {
92 expected: 16,
93 actual: 8,
94 };
95 assert_eq!(
96 format!("{err}"),
97 "Message too short: expected at least 16 bytes, got 8"
98 );
99 }
100
101 #[test]
102 fn test_from_io_error() {
103 let io_err = io::Error::new(io::ErrorKind::ConnectionRefused, "test");
104 let err: SomeIpError = io_err.into();
105 assert!(matches!(err, SomeIpError::Io(_)));
106 }
107}