Skip to main content

flowparser_sflow/
error.rs

1use serde::de::{self, Visitor};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::fmt;
4
5/// Identifies the parsing phase or field where an error occurred.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7pub enum ParseContext {
8    DatagramHeader,
9    DatagramHeaderVersion,
10    AgentAddress,
11    SubAgentId,
12    SequenceNumber,
13    Uptime,
14    NumSamples,
15    SampleDataFormat,
16    SampleLength,
17    SampleData,
18    FlowSample,
19    CounterSample,
20    ExpandedFlowSample,
21    ExpandedCounterSample,
22    DiscardedPacket,
23}
24
25impl fmt::Display for ParseContext {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        let s = match self {
28            ParseContext::DatagramHeader => "datagram header",
29            ParseContext::DatagramHeaderVersion => "datagram header version",
30            ParseContext::AgentAddress => "agent address",
31            ParseContext::SubAgentId => "sub_agent_id",
32            ParseContext::SequenceNumber => "sequence_number",
33            ParseContext::Uptime => "uptime",
34            ParseContext::NumSamples => "num_samples",
35            ParseContext::SampleDataFormat => "sample data_format",
36            ParseContext::SampleLength => "sample length",
37            ParseContext::SampleData => "sample data",
38            ParseContext::FlowSample => "flow sample",
39            ParseContext::CounterSample => "counter sample",
40            ParseContext::ExpandedFlowSample => "expanded flow sample",
41            ParseContext::ExpandedCounterSample => "expanded counter sample",
42            ParseContext::DiscardedPacket => "discarded packet",
43        };
44        f.write_str(s)
45    }
46}
47
48/// Describes the category of a parse error.
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum ParseErrorKind {
51    InvalidAddressType,
52    NomError(nom::error::ErrorKind),
53}
54
55impl fmt::Display for ParseErrorKind {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        match self {
58            ParseErrorKind::InvalidAddressType => f.write_str("InvalidAddressType"),
59            ParseErrorKind::NomError(kind) => write!(f, "{kind:?}"),
60        }
61    }
62}
63
64impl Serialize for ParseErrorKind {
65    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
66        serializer.serialize_str(&self.to_string())
67    }
68}
69
70impl<'de> Deserialize<'de> for ParseErrorKind {
71    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
72        struct ParseErrorKindVisitor;
73
74        impl<'de> Visitor<'de> for ParseErrorKindVisitor {
75            type Value = ParseErrorKind;
76
77            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78                formatter.write_str("a ParseErrorKind string")
79            }
80
81            fn visit_str<E: de::Error>(self, value: &str) -> Result<ParseErrorKind, E> {
82                if value == "InvalidAddressType" {
83                    return Ok(ParseErrorKind::InvalidAddressType);
84                }
85                // Try to match nom ErrorKind variants by their Debug name
86                let kind = match value {
87                    "Tag" => nom::error::ErrorKind::Tag,
88                    "MapRes" => nom::error::ErrorKind::MapRes,
89                    "MapOpt" => nom::error::ErrorKind::MapOpt,
90                    "Alt" => nom::error::ErrorKind::Alt,
91                    "IsNot" => nom::error::ErrorKind::IsNot,
92                    "IsA" => nom::error::ErrorKind::IsA,
93                    "SeparatedList" => nom::error::ErrorKind::SeparatedList,
94                    "SeparatedNonEmptyList" => nom::error::ErrorKind::SeparatedNonEmptyList,
95                    "Many0" => nom::error::ErrorKind::Many0,
96                    "Many1" => nom::error::ErrorKind::Many1,
97                    "ManyTill" => nom::error::ErrorKind::ManyTill,
98                    "Count" => nom::error::ErrorKind::Count,
99                    "TakeUntil" => nom::error::ErrorKind::TakeUntil,
100                    "LengthValue" => nom::error::ErrorKind::LengthValue,
101                    "TagClosure" => nom::error::ErrorKind::TagClosure,
102                    "Alpha" => nom::error::ErrorKind::Alpha,
103                    "Digit" => nom::error::ErrorKind::Digit,
104                    "HexDigit" => nom::error::ErrorKind::HexDigit,
105                    "OctDigit" => nom::error::ErrorKind::OctDigit,
106                    "AlphaNumeric" => nom::error::ErrorKind::AlphaNumeric,
107                    "Space" => nom::error::ErrorKind::Space,
108                    "MultiSpace" => nom::error::ErrorKind::MultiSpace,
109                    "LengthValueFn" => nom::error::ErrorKind::LengthValueFn,
110                    "Eof" => nom::error::ErrorKind::Eof,
111                    "Switch" => nom::error::ErrorKind::Switch,
112                    "TagBits" => nom::error::ErrorKind::TagBits,
113                    "OneOf" => nom::error::ErrorKind::OneOf,
114                    "NoneOf" => nom::error::ErrorKind::NoneOf,
115                    "Char" => nom::error::ErrorKind::Char,
116                    "CrLf" => nom::error::ErrorKind::CrLf,
117                    "RegexpMatch" => nom::error::ErrorKind::RegexpMatch,
118                    "RegexpMatches" => nom::error::ErrorKind::RegexpMatches,
119                    "RegexpFind" => nom::error::ErrorKind::RegexpFind,
120                    "RegexpCapture" => nom::error::ErrorKind::RegexpCapture,
121                    "RegexpCaptures" => nom::error::ErrorKind::RegexpCaptures,
122                    "TakeWhile1" => nom::error::ErrorKind::TakeWhile1,
123                    "Complete" => nom::error::ErrorKind::Complete,
124                    "Fix" => nom::error::ErrorKind::Fix,
125                    "Escaped" => nom::error::ErrorKind::Escaped,
126                    "EscapedTransform" => nom::error::ErrorKind::EscapedTransform,
127                    "NonEmpty" => nom::error::ErrorKind::NonEmpty,
128                    "ManyMN" => nom::error::ErrorKind::ManyMN,
129                    "Not" => nom::error::ErrorKind::Not,
130                    "Permutation" => nom::error::ErrorKind::Permutation,
131                    "Verify" => nom::error::ErrorKind::Verify,
132                    "TakeTill1" => nom::error::ErrorKind::TakeTill1,
133                    "TakeWhileMN" => nom::error::ErrorKind::TakeWhileMN,
134                    "TooLarge" => nom::error::ErrorKind::TooLarge,
135                    "Many0Count" => nom::error::ErrorKind::Many0Count,
136                    "Many1Count" => nom::error::ErrorKind::Many1Count,
137                    "Float" => nom::error::ErrorKind::Float,
138                    "Satisfy" => nom::error::ErrorKind::Satisfy,
139                    "Fail" => nom::error::ErrorKind::Fail,
140                    _ => {
141                        return Err(de::Error::unknown_variant(
142                            value,
143                            &["InvalidAddressType", "<nom ErrorKind variant>"],
144                        ));
145                    }
146                };
147                Ok(ParseErrorKind::NomError(kind))
148            }
149        }
150
151        deserializer.deserialize_str(ParseErrorKindVisitor)
152    }
153}
154
155/// Errors that can occur when parsing sFlow datagrams.
156#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
157pub enum SflowError {
158    /// The input buffer is too short to read the expected field.
159    Incomplete {
160        /// Number of bytes available.
161        available: usize,
162        /// Expected number of bytes, when known.
163        expected: Option<usize>,
164        /// The parsing phase where the error occurred.
165        context: ParseContext,
166    },
167    /// The datagram version is not sFlow v5.
168    UnsupportedVersion {
169        /// The version number found in the datagram header.
170        version: u32,
171    },
172    /// A structural parse error at a known offset.
173    ParseError {
174        /// Byte offset from the start of the datagram.
175        offset: usize,
176        /// The parsing phase where the error occurred.
177        context: ParseContext,
178        /// The category of parse error.
179        kind: ParseErrorKind,
180    },
181    /// The datagram contains more samples than the configured limit.
182    TooManySamples {
183        /// Number of samples declared in the datagram header.
184        count: u32,
185        /// Configured maximum.
186        max: u32,
187    },
188}
189
190impl fmt::Display for SflowError {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        match self {
193            SflowError::Incomplete {
194                available,
195                expected: None,
196                context,
197            } => {
198                write!(
199                    f,
200                    "Incomplete data: only {available} bytes available ({context})"
201                )
202            }
203            SflowError::Incomplete {
204                available,
205                expected: Some(exp),
206                context,
207            } => {
208                write!(
209                    f,
210                    "Incomplete data: only {available} bytes available, expected {exp} ({context})"
211                )
212            }
213            SflowError::UnsupportedVersion { version } => {
214                write!(f, "Unsupported sFlow version: {version} (expected 5)")
215            }
216            SflowError::ParseError {
217                offset,
218                context,
219                kind,
220            } => {
221                write!(f, "Parse error at offset {offset}: {kind} ({context})")
222            }
223            SflowError::TooManySamples { count, max } => {
224                write!(f, "Too many samples: {count} exceeds maximum of {max}")
225            }
226        }
227    }
228}
229
230impl std::error::Error for SflowError {}