Skip to main content

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            #[cfg(feature = "std")]
161            Error::Io(_) => None,
162        }
163    }
164
165    /// Add line number information to an error.
166    /// If the error already has line info, it is preserved.
167    #[inline]
168    pub fn with_line(self, line: usize) -> Self {
169        match self {
170            Error::UnexpectedEof { line: None } => Error::UnexpectedEof { line: Some(line) },
171            Error::Expected { msg, line: None } => Error::Expected {
172                msg,
173                line: Some(line),
174            },
175            Error::InvalidChar { char, line: None } => Error::InvalidChar {
176                char,
177                line: Some(line),
178            },
179            Error::MaxStrLength { max, line: None } => Error::MaxStrLength {
180                max,
181                line: Some(line),
182            },
183            Error::Version { msg, line: None } => Error::Version {
184                msg,
185                line: Some(line),
186            },
187            Error::Message { msg, line: None } => Error::Message {
188                msg,
189                line: Some(line),
190            },
191            Error::Receivers { msg, line: None } => Error::Receivers {
192                msg,
193                line: Some(line),
194            },
195            Error::Nodes { msg, line: None } => Error::Nodes {
196                msg,
197                line: Some(line),
198            },
199            Error::Signal { msg, line: None } => Error::Signal {
200                msg,
201                line: Some(line),
202            },
203            // Already has line info or doesn't support it - return unchanged
204            other => other,
205        }
206    }
207}
208
209impl fmt::Display for Error {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        match self {
212            Error::UnexpectedEof { line } => {
213                if let Some(line) = line {
214                    write!(f, "line {}: {}", line, Error::UNEXPECTED_EOF)
215                } else {
216                    write!(f, "{}", Error::UNEXPECTED_EOF)
217                }
218            }
219            Error::Expected { msg, line } => {
220                if let Some(line) = line {
221                    write!(f, "line {}: {}", line, msg)
222                } else {
223                    write!(f, "{}", msg)
224                }
225            }
226            Error::InvalidChar { char, line } => {
227                if let Some(line) = line {
228                    write!(
229                        f,
230                        "line {}: {}: {}",
231                        line,
232                        Error::INVALID_CHARACTER,
233                        char.escape_debug()
234                    )
235                } else {
236                    write!(f, "{}: {}", Error::INVALID_CHARACTER, char.escape_debug())
237                }
238            }
239            Error::MaxStrLength { max, line } => {
240                if let Some(line) = line {
241                    write!(
242                        f,
243                        "line {}: {}: {}",
244                        line,
245                        Error::STRING_LENGTH_EXCEEDS_MAX,
246                        max
247                    )
248                } else {
249                    write!(f, "{}: {}", Error::STRING_LENGTH_EXCEEDS_MAX, max)
250                }
251            }
252            Error::Version { msg, line } => {
253                if let Some(line) = line {
254                    write!(f, "line {}: {}: {}", line, Error::VERSION_ERROR_PREFIX, msg)
255                } else {
256                    write!(f, "{}: {}", Error::VERSION_ERROR_PREFIX, msg)
257                }
258            }
259            Error::Message { msg, line } => {
260                if let Some(line) = line {
261                    write!(f, "line {}: {}: {}", line, Error::MESSAGE_ERROR_PREFIX, msg)
262                } else {
263                    write!(f, "{}: {}", Error::MESSAGE_ERROR_PREFIX, msg)
264                }
265            }
266            Error::Receivers { msg, line } => {
267                if let Some(line) = line {
268                    write!(
269                        f,
270                        "line {}: {}: {}",
271                        line,
272                        Error::RECEIVERS_ERROR_PREFIX,
273                        msg
274                    )
275                } else {
276                    write!(f, "{}: {}", Error::RECEIVERS_ERROR_PREFIX, msg)
277                }
278            }
279            Error::Nodes { msg, line } => {
280                if let Some(line) = line {
281                    write!(f, "line {}: {}: {}", line, Error::NODES_ERROR_PREFIX, msg)
282                } else {
283                    write!(f, "{}: {}", Error::NODES_ERROR_PREFIX, msg)
284                }
285            }
286            Error::Signal { msg, line } => {
287                if let Some(line) = line {
288                    write!(f, "line {}: {}: {}", line, Error::SIGNAL_ERROR_PREFIX, msg)
289                } else {
290                    write!(f, "{}: {}", Error::SIGNAL_ERROR_PREFIX, msg)
291                }
292            }
293            Error::Decoding(msg) => {
294                write!(f, "{}: {}", Error::DECODING_ERROR_PREFIX, msg)
295            }
296            Error::Encoding(msg) => {
297                write!(f, "{}: {}", Error::ENCODING_ERROR_PREFIX, msg)
298            }
299            Error::Validation(msg) => {
300                write!(f, "{}: {}", Error::VALIDATION_ERROR_PREFIX, msg)
301            }
302            #[cfg(feature = "std")]
303            Error::Io(msg) => {
304                write!(f, "I/O error: {}", msg)
305            }
306        }
307    }
308}
309
310#[cfg(feature = "std")]
311impl From<std::io::Error> for Error {
312    fn from(err: std::io::Error) -> Self {
313        Error::Io(err.to_string())
314    }
315}
316
317impl From<core::num::ParseIntError> for Error {
318    fn from(_err: core::num::ParseIntError) -> Self {
319        Error::expected(Error::PARSE_NUMBER_FAILED)
320    }
321}
322
323// std::error::Error is only available with std feature
324#[cfg(feature = "std")]
325impl std::error::Error for Error {}
326
327impl From<sigma_bounded::Error> for Error {
328    #[inline]
329    fn from(e: sigma_bounded::Error) -> Self {
330        match e {
331            sigma_bounded::Error::CapacityExceeded => {
332                Self::Validation(Self::MAX_NAME_SIZE_EXCEEDED)
333            }
334            sigma_bounded::Error::InvalidUtf8 => Self::expected(Self::INVALID_UTF8),
335            _ => Self::Validation(Self::MAX_NAME_SIZE_EXCEEDED),
336        }
337    }
338}
339
340#[cfg(test)]
341mod tests {
342    #![allow(clippy::float_cmp)]
343
344    // Tests that require std feature (for Display/ToString)
345    #[cfg(feature = "std")]
346    mod tests_with_std {
347        use crate::Error;
348
349        #[test]
350        fn test_from_parse_int_error() {
351            // Create a ParseIntError by trying to parse an invalid string
352            let parse_error = "invalid".parse::<u32>().unwrap_err();
353            let error: Error = parse_error.into();
354
355            match error {
356                Error::Expected { msg, line } => {
357                    assert_eq!(msg, Error::PARSE_NUMBER_FAILED);
358                    assert_eq!(line, None);
359                }
360                _ => panic!("Expected Error::Expected"),
361            }
362        }
363
364        #[test]
365        fn test_display_decoding_error() {
366            let error = Error::Decoding("Test error message");
367            let display = error.to_string();
368            assert!(display.starts_with(Error::DECODING_ERROR_PREFIX));
369            assert!(display.contains("Test error message"));
370        }
371
372        #[test]
373        fn test_display_signal_error() {
374            let error = Error::signal("Test signal error");
375            let display = error.to_string();
376            assert!(display.starts_with(Error::SIGNAL_ERROR_PREFIX));
377            assert!(display.contains("Test signal error"));
378        }
379
380        #[test]
381        fn test_display_formatting() {
382            // Test that Display properly formats complex error messages
383            let error = Error::Decoding(
384                "Duplicate message ID: 256 (messages 'EngineData' and 'BrakeData')",
385            );
386            let display = error.to_string();
387            assert!(display.starts_with(Error::DECODING_ERROR_PREFIX));
388            assert!(display.contains("256"));
389            assert!(display.contains("EngineData"));
390            assert!(display.contains("BrakeData"));
391        }
392
393        #[test]
394        fn test_display_from_parse_int_error() {
395            let int_error = "not_a_number".parse::<u32>().unwrap_err();
396            let error: Error = int_error.into();
397            let display = error.to_string();
398
399            assert!(display.contains(Error::PARSE_NUMBER_FAILED));
400        }
401
402        #[test]
403        fn test_error_with_line_number() {
404            let error = Error::expected_at("Expected identifier", 42);
405            let display = error.to_string();
406            assert!(display.contains("line 42"));
407            assert!(display.contains("Expected identifier"));
408        }
409
410        #[test]
411        fn test_error_without_line_number() {
412            let error = Error::expected("Expected identifier");
413            let display = error.to_string();
414            assert!(!display.contains("line"));
415            assert!(display.contains("Expected identifier"));
416        }
417
418        #[test]
419        fn test_with_line_adds_line_info() {
420            let error = Error::expected("Expected whitespace");
421            assert_eq!(error.line(), None);
422
423            let error_with_line = error.with_line(10);
424            assert_eq!(error_with_line.line(), Some(10));
425        }
426
427        #[test]
428        fn test_with_line_preserves_existing_line() {
429            let error = Error::expected_at("Expected whitespace", 5);
430            let error_with_line = error.with_line(10);
431            // Should preserve the original line (5), not overwrite with 10
432            assert_eq!(error_with_line.line(), Some(5));
433        }
434
435        #[test]
436        fn test_invalid_char_display() {
437            let error = Error::invalid_char_at('\t', 15);
438            let display = error.to_string();
439            assert!(display.contains("line 15"));
440            assert!(display.contains("\\t"));
441        }
442
443        #[test]
444        fn test_max_str_length_display() {
445            let error = Error::max_str_length_at(256, 20);
446            let display = error.to_string();
447            assert!(display.contains("line 20"));
448            assert!(display.contains("256"));
449        }
450    }
451
452    // Tests that require std feature (for std::error::Error trait)
453    // Only available when std is enabled
454    #[cfg(feature = "std")]
455    mod tests_std {
456        use super::super::Error;
457        use std::error::Error as StdError;
458
459        #[test]
460        fn test_std_error_trait() {
461            let error = Error::Decoding("Test");
462            // Verify it implements std::error::Error
463            let _: &dyn StdError = &error;
464
465            // Verify source() returns None (no underlying error)
466            assert!(error.source().is_none());
467        }
468    }
469}