1use crate::layer::LayerKind;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
8pub enum PacketError {
9 #[error("buffer too short: expected at least {expected} bytes, got {actual}")]
11 BufferTooShort { expected: usize, actual: usize },
12
13 #[error("invalid field value: {0}")]
15 InvalidField(String),
16
17 #[error("unsupported layer type: {0}")]
19 UnsupportedLayer(String),
20
21 #[error("layer not found: {0:?}")]
23 LayerNotFound(LayerKind),
24
25 #[error("checksum mismatch: expected {expected:#06x}, got {actual:#06x}")]
27 ChecksumMismatch { expected: u16, actual: u16 },
28
29 #[error("invalid protocol number: {0}")]
31 InvalidProtocol(u8),
32
33 #[error("parse error at offset {offset}: {message}")]
35 ParseError { offset: usize, message: String },
36
37 #[error("field error: {0}")]
39 FieldError(#[from] crate::layer::field::FieldError),
40
41 #[error("no binding found for {lower:?} -> {upper:?}")]
43 BindingNotFound { lower: LayerKind, upper: LayerKind },
44
45 #[error("failed to resolve neighbor for {0}")]
47 NeighborResolutionFailed(String),
48
49 #[error("invalid MAC address: {0}")]
51 InvalidMac(String),
52
53 #[error("invalid IP address: {0}")]
55 InvalidIp(String),
56
57 #[error("operation not supported: {0}")]
59 NotSupported(String),
60
61 #[error("operation timed out after {0} ms")]
63 Timeout(u64),
64
65 #[error("I/O error: {0}")]
67 Io(String),
68}
69
70impl From<std::io::Error> for PacketError {
71 fn from(err: std::io::Error) -> Self {
72 PacketError::Io(err.to_string())
73 }
74}
75
76impl PacketError {
77 pub fn buffer_too_short(expected: usize, actual: usize) -> Self {
79 Self::BufferTooShort { expected, actual }
80 }
81
82 pub fn parse_error(offset: usize, message: impl Into<String>) -> Self {
84 Self::ParseError {
85 offset,
86 message: message.into(),
87 }
88 }
89
90 pub fn binding_not_found(lower: LayerKind, upper: LayerKind) -> Self {
92 Self::BindingNotFound { lower, upper }
93 }
94
95 pub fn is_recoverable(&self) -> bool {
97 matches!(
98 self,
99 Self::BufferTooShort { .. } | Self::Timeout(_) | Self::NeighborResolutionFailed(_)
100 )
101 }
102}
103
104pub type Result<T> = std::result::Result<T, PacketError>;
106
107pub trait ResultExt<T> {
109 fn with_context(self, context: impl FnOnce() -> String) -> Result<T>;
111}
112
113impl<T, E: std::fmt::Display> ResultExt<T> for std::result::Result<T, E> {
114 fn with_context(self, context: impl FnOnce() -> String) -> Result<T> {
115 self.map_err(|e| PacketError::InvalidField(format!("{}: {}", context(), e)))
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_error_display() {
125 let err = PacketError::BufferTooShort {
126 expected: 20,
127 actual: 10,
128 };
129 assert!(err.to_string().contains("20"));
130 assert!(err.to_string().contains("10"));
131 }
132
133 #[test]
134 fn test_error_helpers() {
135 let err = PacketError::buffer_too_short(100, 50);
136 assert!(matches!(err, PacketError::BufferTooShort { .. }));
137
138 let err = PacketError::parse_error(10, "invalid header");
139 assert!(matches!(err, PacketError::ParseError { .. }));
140 }
141
142 #[test]
143 fn test_is_recoverable() {
144 assert!(PacketError::Timeout(1000).is_recoverable());
145 assert!(!PacketError::InvalidProtocol(255).is_recoverable());
146 }
147}