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