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 #[must_use]
79 pub fn buffer_too_short(expected: usize, actual: usize) -> Self {
80 Self::BufferTooShort { expected, actual }
81 }
82
83 pub fn parse_error(offset: usize, message: impl Into<String>) -> Self {
85 Self::ParseError {
86 offset,
87 message: message.into(),
88 }
89 }
90
91 #[must_use]
93 pub fn binding_not_found(lower: LayerKind, upper: LayerKind) -> Self {
94 Self::BindingNotFound { lower, upper }
95 }
96
97 #[must_use]
99 pub fn is_recoverable(&self) -> bool {
100 matches!(
101 self,
102 Self::BufferTooShort { .. } | Self::Timeout(_) | Self::NeighborResolutionFailed(_)
103 )
104 }
105}
106
107pub type Result<T> = std::result::Result<T, PacketError>;
109
110pub trait ResultExt<T> {
112 fn with_context(self, context: impl FnOnce() -> String) -> Result<T>;
114}
115
116impl<T, E: std::fmt::Display> ResultExt<T> for std::result::Result<T, E> {
117 fn with_context(self, context: impl FnOnce() -> String) -> Result<T> {
118 self.map_err(|e| PacketError::InvalidField(format!("{}: {}", context(), e)))
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_error_display() {
128 let err = PacketError::BufferTooShort {
129 expected: 20,
130 actual: 10,
131 };
132 assert!(err.to_string().contains("20"));
133 assert!(err.to_string().contains("10"));
134 }
135
136 #[test]
137 fn test_error_helpers() {
138 let err = PacketError::buffer_too_short(100, 50);
139 assert!(matches!(err, PacketError::BufferTooShort { .. }));
140
141 let err = PacketError::parse_error(10, "invalid header");
142 assert!(matches!(err, PacketError::ParseError { .. }));
143 }
144
145 #[test]
146 fn test_is_recoverable() {
147 assert!(PacketError::Timeout(1000).is_recoverable());
148 assert!(!PacketError::InvalidProtocol(255).is_recoverable());
149 }
150}