dbc_rs/error/
impls.rs

1use core::fmt;
2
3use super::Error;
4
5impl Error {
6    // ============================================================================
7    // Constructor helpers (for creating errors without line info)
8    // ============================================================================
9
10    /// Create an UnexpectedEof error without line info.
11    #[inline]
12    pub fn unexpected_eof() -> Self {
13        Error::UnexpectedEof { line: None }
14    }
15
16    /// Create an UnexpectedEof error with line info.
17    #[inline]
18    pub fn unexpected_eof_at(line: usize) -> Self {
19        Error::UnexpectedEof { line: Some(line) }
20    }
21
22    /// Create an Expected error without line info.
23    #[inline]
24    pub fn expected(msg: &'static str) -> Self {
25        Error::Expected { msg, line: None }
26    }
27
28    /// Create an Expected error with line info.
29    #[inline]
30    pub fn expected_at(msg: &'static str, line: usize) -> Self {
31        Error::Expected {
32            msg,
33            line: Some(line),
34        }
35    }
36
37    /// Create an InvalidChar error without line info.
38    #[inline]
39    pub fn invalid_char(char: char) -> Self {
40        Error::InvalidChar { char, line: None }
41    }
42
43    /// Create an InvalidChar error with line info.
44    #[inline]
45    pub fn invalid_char_at(char: char, line: usize) -> Self {
46        Error::InvalidChar {
47            char,
48            line: Some(line),
49        }
50    }
51
52    /// Create a MaxStrLength error without line info.
53    #[inline]
54    pub fn max_str_length(max: usize) -> Self {
55        Error::MaxStrLength { max, line: None }
56    }
57
58    /// Create a MaxStrLength error with line info.
59    #[inline]
60    pub fn max_str_length_at(max: usize, line: usize) -> Self {
61        Error::MaxStrLength {
62            max,
63            line: Some(line),
64        }
65    }
66
67    /// Create a Version error without line info.
68    #[inline]
69    pub fn version(msg: &'static str) -> Self {
70        Error::Version { msg, line: None }
71    }
72
73    /// Create a Version error with line info.
74    #[inline]
75    pub fn version_at(msg: &'static str, line: usize) -> Self {
76        Error::Version {
77            msg,
78            line: Some(line),
79        }
80    }
81
82    /// Create a Message error without line info.
83    #[inline]
84    pub fn message(msg: &'static str) -> Self {
85        Error::Message { msg, line: None }
86    }
87
88    /// Create a Message error with line info.
89    #[inline]
90    pub fn message_at(msg: &'static str, line: usize) -> Self {
91        Error::Message {
92            msg,
93            line: Some(line),
94        }
95    }
96
97    /// Create a Receivers error without line info.
98    #[inline]
99    pub fn receivers(msg: &'static str) -> Self {
100        Error::Receivers { msg, line: None }
101    }
102
103    /// Create a Receivers error with line info.
104    #[inline]
105    pub fn receivers_at(msg: &'static str, line: usize) -> Self {
106        Error::Receivers {
107            msg,
108            line: Some(line),
109        }
110    }
111
112    /// Create a Nodes error without line info.
113    #[inline]
114    pub fn nodes(msg: &'static str) -> Self {
115        Error::Nodes { msg, line: None }
116    }
117
118    /// Create a Nodes error with line info.
119    #[inline]
120    pub fn nodes_at(msg: &'static str, line: usize) -> Self {
121        Error::Nodes {
122            msg,
123            line: Some(line),
124        }
125    }
126
127    /// Create a Signal error without line info.
128    #[inline]
129    pub fn signal(msg: &'static str) -> Self {
130        Error::Signal { msg, line: None }
131    }
132
133    /// Create a Signal error with line info.
134    #[inline]
135    pub fn signal_at(msg: &'static str, line: usize) -> Self {
136        Error::Signal {
137            msg,
138            line: Some(line),
139        }
140    }
141
142    // ============================================================================
143    // Line number helpers
144    // ============================================================================
145
146    /// Get the line number associated with this error, if any.
147    #[inline]
148    pub fn line(&self) -> Option<usize> {
149        match self {
150            Error::UnexpectedEof { line } => *line,
151            Error::Expected { line, .. } => *line,
152            Error::InvalidChar { line, .. } => *line,
153            Error::MaxStrLength { line, .. } => *line,
154            Error::Version { line, .. } => *line,
155            Error::Message { line, .. } => *line,
156            Error::Receivers { line, .. } => *line,
157            Error::Nodes { line, .. } => *line,
158            Error::Signal { line, .. } => *line,
159            Error::Decoding(_) | Error::Validation(_) => None,
160        }
161    }
162
163    /// Add line number information to an error.
164    /// If the error already has line info, it is preserved.
165    #[inline]
166    pub fn with_line(self, line: usize) -> Self {
167        match self {
168            Error::UnexpectedEof { line: None } => Error::UnexpectedEof { line: Some(line) },
169            Error::Expected { msg, line: None } => Error::Expected {
170                msg,
171                line: Some(line),
172            },
173            Error::InvalidChar { char, line: None } => Error::InvalidChar {
174                char,
175                line: Some(line),
176            },
177            Error::MaxStrLength { max, line: None } => Error::MaxStrLength {
178                max,
179                line: Some(line),
180            },
181            Error::Version { msg, line: None } => Error::Version {
182                msg,
183                line: Some(line),
184            },
185            Error::Message { msg, line: None } => Error::Message {
186                msg,
187                line: Some(line),
188            },
189            Error::Receivers { msg, line: None } => Error::Receivers {
190                msg,
191                line: Some(line),
192            },
193            Error::Nodes { msg, line: None } => Error::Nodes {
194                msg,
195                line: Some(line),
196            },
197            Error::Signal { msg, line: None } => Error::Signal {
198                msg,
199                line: Some(line),
200            },
201            // Already has line info or doesn't support it - return unchanged
202            other => other,
203        }
204    }
205}
206
207impl fmt::Display for Error {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        match self {
210            Error::UnexpectedEof { line } => {
211                if let Some(line) = line {
212                    write!(f, "line {}: {}", line, Error::UNEXPECTED_EOF)
213                } else {
214                    write!(f, "{}", Error::UNEXPECTED_EOF)
215                }
216            }
217            Error::Expected { msg, line } => {
218                if let Some(line) = line {
219                    write!(f, "line {}: {}", line, msg)
220                } else {
221                    write!(f, "{}", msg)
222                }
223            }
224            Error::InvalidChar { char, line } => {
225                if let Some(line) = line {
226                    write!(
227                        f,
228                        "line {}: {}: {}",
229                        line,
230                        Error::INVALID_CHARACTER,
231                        char.escape_debug()
232                    )
233                } else {
234                    write!(f, "{}: {}", Error::INVALID_CHARACTER, char.escape_debug())
235                }
236            }
237            Error::MaxStrLength { max, line } => {
238                if let Some(line) = line {
239                    write!(
240                        f,
241                        "line {}: {}: {}",
242                        line,
243                        Error::STRING_LENGTH_EXCEEDS_MAX,
244                        max
245                    )
246                } else {
247                    write!(f, "{}: {}", Error::STRING_LENGTH_EXCEEDS_MAX, max)
248                }
249            }
250            Error::Version { msg, line } => {
251                if let Some(line) = line {
252                    write!(f, "line {}: {}: {}", line, Error::VERSION_ERROR_PREFIX, msg)
253                } else {
254                    write!(f, "{}: {}", Error::VERSION_ERROR_PREFIX, msg)
255                }
256            }
257            Error::Message { msg, line } => {
258                if let Some(line) = line {
259                    write!(f, "line {}: {}: {}", line, Error::MESSAGE_ERROR_PREFIX, msg)
260                } else {
261                    write!(f, "{}: {}", Error::MESSAGE_ERROR_PREFIX, msg)
262                }
263            }
264            Error::Receivers { msg, line } => {
265                if let Some(line) = line {
266                    write!(
267                        f,
268                        "line {}: {}: {}",
269                        line,
270                        Error::RECEIVERS_ERROR_PREFIX,
271                        msg
272                    )
273                } else {
274                    write!(f, "{}: {}", Error::RECEIVERS_ERROR_PREFIX, msg)
275                }
276            }
277            Error::Nodes { msg, line } => {
278                if let Some(line) = line {
279                    write!(f, "line {}: {}: {}", line, Error::NODES_ERROR_PREFIX, msg)
280                } else {
281                    write!(f, "{}: {}", Error::NODES_ERROR_PREFIX, msg)
282                }
283            }
284            Error::Signal { msg, line } => {
285                if let Some(line) = line {
286                    write!(f, "line {}: {}: {}", line, Error::SIGNAL_ERROR_PREFIX, msg)
287                } else {
288                    write!(f, "{}: {}", Error::SIGNAL_ERROR_PREFIX, msg)
289                }
290            }
291            Error::Decoding(msg) => {
292                write!(f, "{}: {}", Error::DECODING_ERROR_PREFIX, msg)
293            }
294            Error::Validation(msg) => {
295                write!(f, "{}: {}", Error::VALIDATION_ERROR_PREFIX, msg)
296            }
297        }
298    }
299}
300
301impl From<core::num::ParseIntError> for Error {
302    fn from(_err: core::num::ParseIntError) -> Self {
303        Error::expected(Error::PARSE_NUMBER_FAILED)
304    }
305}
306
307// std::error::Error is only available with std feature
308#[cfg(feature = "std")]
309impl std::error::Error for Error {
310    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
311        None
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    #![allow(clippy::float_cmp)]
318
319    // Tests that require std feature (for Display/ToString)
320    #[cfg(feature = "std")]
321    mod tests_with_std {
322        use crate::Error;
323
324        #[test]
325        fn test_from_parse_int_error() {
326            // Create a ParseIntError by trying to parse an invalid string
327            let parse_error = "invalid".parse::<u32>().unwrap_err();
328            let error: Error = parse_error.into();
329
330            match error {
331                Error::Expected { msg, line } => {
332                    assert_eq!(msg, Error::PARSE_NUMBER_FAILED);
333                    assert_eq!(line, None);
334                }
335                _ => panic!("Expected Error::Expected"),
336            }
337        }
338
339        #[test]
340        fn test_display_decoding_error() {
341            let error = Error::Decoding("Test error message");
342            let display = error.to_string();
343            assert!(display.starts_with(Error::DECODING_ERROR_PREFIX));
344            assert!(display.contains("Test error message"));
345        }
346
347        #[test]
348        fn test_display_signal_error() {
349            let error = Error::signal("Test signal error");
350            let display = error.to_string();
351            assert!(display.starts_with(Error::SIGNAL_ERROR_PREFIX));
352            assert!(display.contains("Test signal error"));
353        }
354
355        #[test]
356        fn test_display_formatting() {
357            // Test that Display properly formats complex error messages
358            let error = Error::Decoding(
359                "Duplicate message ID: 256 (messages 'EngineData' and 'BrakeData')",
360            );
361            let display = error.to_string();
362            assert!(display.starts_with(Error::DECODING_ERROR_PREFIX));
363            assert!(display.contains("256"));
364            assert!(display.contains("EngineData"));
365            assert!(display.contains("BrakeData"));
366        }
367
368        #[test]
369        fn test_display_from_parse_int_error() {
370            let int_error = "not_a_number".parse::<u32>().unwrap_err();
371            let error: Error = int_error.into();
372            let display = error.to_string();
373
374            assert!(display.contains(Error::PARSE_NUMBER_FAILED));
375        }
376
377        #[test]
378        fn test_error_with_line_number() {
379            let error = Error::expected_at("Expected identifier", 42);
380            let display = error.to_string();
381            assert!(display.contains("line 42"));
382            assert!(display.contains("Expected identifier"));
383        }
384
385        #[test]
386        fn test_error_without_line_number() {
387            let error = Error::expected("Expected identifier");
388            let display = error.to_string();
389            assert!(!display.contains("line"));
390            assert!(display.contains("Expected identifier"));
391        }
392
393        #[test]
394        fn test_with_line_adds_line_info() {
395            let error = Error::expected("Expected whitespace");
396            assert_eq!(error.line(), None);
397
398            let error_with_line = error.with_line(10);
399            assert_eq!(error_with_line.line(), Some(10));
400        }
401
402        #[test]
403        fn test_with_line_preserves_existing_line() {
404            let error = Error::expected_at("Expected whitespace", 5);
405            let error_with_line = error.with_line(10);
406            // Should preserve the original line (5), not overwrite with 10
407            assert_eq!(error_with_line.line(), Some(5));
408        }
409
410        #[test]
411        fn test_invalid_char_display() {
412            let error = Error::invalid_char_at('\t', 15);
413            let display = error.to_string();
414            assert!(display.contains("line 15"));
415            assert!(display.contains("\\t"));
416        }
417
418        #[test]
419        fn test_max_str_length_display() {
420            let error = Error::max_str_length_at(256, 20);
421            let display = error.to_string();
422            assert!(display.contains("line 20"));
423            assert!(display.contains("256"));
424        }
425    }
426
427    // Tests that require std feature (for std::error::Error trait)
428    // Only available when std is enabled
429    #[cfg(feature = "std")]
430    mod tests_std {
431        use super::super::Error;
432        use std::error::Error as StdError;
433
434        #[test]
435        fn test_std_error_trait() {
436            let error = Error::Decoding("Test");
437            // Verify it implements std::error::Error
438            let _: &dyn StdError = &error;
439
440            // Verify source() returns None (no underlying error)
441            assert!(error.source().is_none());
442        }
443    }
444}