Skip to main content

stackforge_core/
error.rs

1//! Error types for the stackforge-core crate.
2
3use crate::layer::LayerKind;
4use thiserror::Error;
5
6/// Errors that can occur during packet operations.
7#[derive(Debug, Error)]
8pub enum PacketError {
9    /// The packet buffer is too short to contain the expected data.
10    #[error("buffer too short: expected at least {expected} bytes, got {actual}")]
11    BufferTooShort { expected: usize, actual: usize },
12
13    /// Invalid field value encountered during parsing.
14    #[error("invalid field value: {0}")]
15    InvalidField(String),
16
17    /// The layer type is not supported or recognized.
18    #[error("unsupported layer type: {0}")]
19    UnsupportedLayer(String),
20
21    /// Attempted to access a layer that doesn't exist in the packet.
22    #[error("layer not found: {0:?}")]
23    LayerNotFound(LayerKind),
24
25    /// Checksum verification failed.
26    #[error("checksum mismatch: expected {expected:#06x}, got {actual:#06x}")]
27    ChecksumMismatch { expected: u16, actual: u16 },
28
29    /// Invalid protocol number.
30    #[error("invalid protocol number: {0}")]
31    InvalidProtocol(u8),
32
33    /// Failed to parse layer at the given offset.
34    #[error("parse error at offset {offset}: {message}")]
35    ParseError { offset: usize, message: String },
36
37    /// Field access error.
38    #[error("field error: {0}")]
39    FieldError(#[from] crate::layer::field::FieldError),
40
41    /// Layer binding not found.
42    #[error("no binding found for {lower:?} -> {upper:?}")]
43    BindingNotFound { lower: LayerKind, upper: LayerKind },
44
45    /// Neighbor resolution failed.
46    #[error("failed to resolve neighbor for {0}")]
47    NeighborResolutionFailed(String),
48
49    /// Invalid MAC address.
50    #[error("invalid MAC address: {0}")]
51    InvalidMac(String),
52
53    /// Invalid IP address.
54    #[error("invalid IP address: {0}")]
55    InvalidIp(String),
56
57    /// Operation not supported.
58    #[error("operation not supported: {0}")]
59    NotSupported(String),
60
61    /// Timeout error.
62    #[error("operation timed out after {0} ms")]
63    Timeout(u64),
64
65    /// I/O error wrapper.
66    #[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    /// Create a buffer too short error.
78    pub fn buffer_too_short(expected: usize, actual: usize) -> Self {
79        Self::BufferTooShort { expected, actual }
80    }
81
82    /// Create a parse error.
83    pub fn parse_error(offset: usize, message: impl Into<String>) -> Self {
84        Self::ParseError {
85            offset,
86            message: message.into(),
87        }
88    }
89
90    /// Create a binding not found error.
91    pub fn binding_not_found(lower: LayerKind, upper: LayerKind) -> Self {
92        Self::BindingNotFound { lower, upper }
93    }
94
95    /// Check if this is a recoverable error.
96    pub fn is_recoverable(&self) -> bool {
97        matches!(
98            self,
99            Self::BufferTooShort { .. } | Self::Timeout(_) | Self::NeighborResolutionFailed(_)
100        )
101    }
102}
103
104/// Result type alias for packet operations.
105pub type Result<T> = std::result::Result<T, PacketError>;
106
107/// Extension trait for Result types.
108pub trait ResultExt<T> {
109    /// Convert to PacketError with context.
110    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}