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::Encoding(_) | 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::Encoding(msg) => {
295                write!(f, "{}: {}", Error::ENCODING_ERROR_PREFIX, msg)
296            }
297            Error::Validation(msg) => {
298                write!(f, "{}: {}", Error::VALIDATION_ERROR_PREFIX, msg)
299            }
300        }
301    }
302}
303
304impl From<core::num::ParseIntError> for Error {
305    fn from(_err: core::num::ParseIntError) -> Self {
306        Error::expected(Error::PARSE_NUMBER_FAILED)
307    }
308}
309
310// std::error::Error is only available with std feature
311#[cfg(feature = "std")]
312impl std::error::Error for Error {
313    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
314        None
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    #![allow(clippy::float_cmp)]
321
322    // Tests that require std feature (for Display/ToString)
323    #[cfg(feature = "std")]
324    mod tests_with_std {
325        use crate::Error;
326
327        #[test]
328        fn test_from_parse_int_error() {
329            // Create a ParseIntError by trying to parse an invalid string
330            let parse_error = "invalid".parse::<u32>().unwrap_err();
331            let error: Error = parse_error.into();
332
333            match error {
334                Error::Expected { msg, line } => {
335                    assert_eq!(msg, Error::PARSE_NUMBER_FAILED);
336                    assert_eq!(line, None);
337                }
338                _ => panic!("Expected Error::Expected"),
339            }
340        }
341
342        #[test]
343        fn test_display_decoding_error() {
344            let error = Error::Decoding("Test error message");
345            let display = error.to_string();
346            assert!(display.starts_with(Error::DECODING_ERROR_PREFIX));
347            assert!(display.contains("Test error message"));
348        }
349
350        #[test]
351        fn test_display_signal_error() {
352            let error = Error::signal("Test signal error");
353            let display = error.to_string();
354            assert!(display.starts_with(Error::SIGNAL_ERROR_PREFIX));
355            assert!(display.contains("Test signal error"));
356        }
357
358        #[test]
359        fn test_display_formatting() {
360            // Test that Display properly formats complex error messages
361            let error = Error::Decoding(
362                "Duplicate message ID: 256 (messages 'EngineData' and 'BrakeData')",
363            );
364            let display = error.to_string();
365            assert!(display.starts_with(Error::DECODING_ERROR_PREFIX));
366            assert!(display.contains("256"));
367            assert!(display.contains("EngineData"));
368            assert!(display.contains("BrakeData"));
369        }
370
371        #[test]
372        fn test_display_from_parse_int_error() {
373            let int_error = "not_a_number".parse::<u32>().unwrap_err();
374            let error: Error = int_error.into();
375            let display = error.to_string();
376
377            assert!(display.contains(Error::PARSE_NUMBER_FAILED));
378        }
379
380        #[test]
381        fn test_error_with_line_number() {
382            let error = Error::expected_at("Expected identifier", 42);
383            let display = error.to_string();
384            assert!(display.contains("line 42"));
385            assert!(display.contains("Expected identifier"));
386        }
387
388        #[test]
389        fn test_error_without_line_number() {
390            let error = Error::expected("Expected identifier");
391            let display = error.to_string();
392            assert!(!display.contains("line"));
393            assert!(display.contains("Expected identifier"));
394        }
395
396        #[test]
397        fn test_with_line_adds_line_info() {
398            let error = Error::expected("Expected whitespace");
399            assert_eq!(error.line(), None);
400
401            let error_with_line = error.with_line(10);
402            assert_eq!(error_with_line.line(), Some(10));
403        }
404
405        #[test]
406        fn test_with_line_preserves_existing_line() {
407            let error = Error::expected_at("Expected whitespace", 5);
408            let error_with_line = error.with_line(10);
409            // Should preserve the original line (5), not overwrite with 10
410            assert_eq!(error_with_line.line(), Some(5));
411        }
412
413        #[test]
414        fn test_invalid_char_display() {
415            let error = Error::invalid_char_at('\t', 15);
416            let display = error.to_string();
417            assert!(display.contains("line 15"));
418            assert!(display.contains("\\t"));
419        }
420
421        #[test]
422        fn test_max_str_length_display() {
423            let error = Error::max_str_length_at(256, 20);
424            let display = error.to_string();
425            assert!(display.contains("line 20"));
426            assert!(display.contains("256"));
427        }
428    }
429
430    // Tests that require std feature (for std::error::Error trait)
431    // Only available when std is enabled
432    #[cfg(feature = "std")]
433    mod tests_std {
434        use super::super::Error;
435        use std::error::Error as StdError;
436
437        #[test]
438        fn test_std_error_trait() {
439            let error = Error::Decoding("Test");
440            // Verify it implements std::error::Error
441            let _: &dyn StdError = &error;
442
443            // Verify source() returns None (no underlying error)
444            assert!(error.source().is_none());
445        }
446    }
447}