dbc_rs/error/
mod.rs

1use alloc::string::String;
2use core::fmt;
3use core::num::ParseIntError;
4
5pub mod lang;
6pub(crate) mod messages;
7
8/// Error type for DBC parsing and validation operations.
9///
10/// Errors are categorized by the component that generated them, making it
11/// easier to identify where validation or parsing failed.
12///
13/// # Examples
14///
15/// ```rust
16/// use dbc_rs::Error;
17///
18/// let error = Error::Signal("Signal name cannot be empty".to_string());
19/// println!("{}", error);
20/// ```
21#[derive(Debug, PartialEq)]
22pub enum Error {
23    /// General data validation or parsing error.
24    InvalidData(String),
25    /// Signal-specific validation or parsing error.
26    Signal(String),
27    /// Message-specific validation or parsing error.
28    Message(String),
29    /// DBC file-level validation or parsing error.
30    Dbc(String),
31    /// Version parsing error.
32    Version(String),
33    /// Node-related validation or parsing error.
34    Nodes(String),
35}
36
37impl fmt::Display for Error {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            Error::InvalidData(msg) => {
41                // Display the message with category prefix for better readability
42                write!(f, "{}", messages::format_invalid_data(msg))
43            }
44            Error::Signal(msg) => {
45                // Display the message with category prefix for better readability
46                write!(f, "{}", messages::format_signal_error(msg))
47            }
48            Error::Message(msg) => {
49                write!(f, "{}", messages::format_message_error(msg))
50            }
51            Error::Dbc(msg) => {
52                write!(f, "{}", messages::format_dbc_error(msg))
53            }
54            Error::Version(msg) => {
55                write!(f, "{}", messages::format_version_error(msg))
56            }
57            Error::Nodes(msg) => {
58                write!(f, "{}", messages::format_nodes_error(msg))
59            }
60        }
61    }
62}
63
64impl From<ParseIntError> for Error {
65    fn from(err: ParseIntError) -> Self {
66        Error::InvalidData(messages::parse_number_failed(err))
67    }
68}
69
70#[cfg(feature = "std")]
71impl std::error::Error for Error {
72    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
73        None
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::Error;
80    use crate::error::lang;
81    use alloc::string::ToString;
82
83    #[test]
84    fn test_from_parse_int_error() {
85        // Create a ParseIntError by trying to parse an invalid string
86        let parse_error = "invalid".parse::<u32>().unwrap_err();
87        let error: Error = parse_error.into();
88
89        match error {
90            Error::InvalidData(msg) => {
91                assert!(msg.contains(lang::FORMAT_PARSE_NUMBER_FAILED.split(':').next().unwrap()))
92            }
93            _ => panic!("Expected InvalidData error"),
94        }
95    }
96
97    #[test]
98    fn test_display_invalid_data() {
99        let error = Error::InvalidData("Test error message".to_string());
100        let display = error.to_string();
101        assert!(display.starts_with(lang::INVALID_DATA_CATEGORY));
102        assert!(display.contains("Test error message"));
103    }
104
105    #[test]
106    fn test_display_signal_error() {
107        let error = Error::Signal(lang::SIGNAL_NAME_EMPTY.to_string());
108        let display = error.to_string();
109        assert!(display.starts_with(lang::SIGNAL_ERROR_CATEGORY));
110        assert!(display.contains(lang::SIGNAL_NAME_EMPTY));
111    }
112
113    #[test]
114    fn test_display_formatting() {
115        // Test that Display properly formats complex error messages
116        let error = Error::InvalidData(
117            "Duplicate message ID: 256 (messages 'EngineData' and 'BrakeData')".to_string(),
118        );
119        let display = error.to_string();
120        assert!(display.starts_with(lang::INVALID_DATA_CATEGORY));
121        assert!(display.contains("256"));
122        assert!(display.contains("EngineData"));
123        assert!(display.contains("BrakeData"));
124    }
125
126    #[test]
127    fn test_display_parse_error() {
128        let parse_error = "not_a_number".parse::<u32>().unwrap_err();
129        let error: Error = parse_error.into();
130        let display = error.to_string();
131
132        assert!(display.starts_with(lang::INVALID_DATA_CATEGORY));
133        assert!(display.contains(lang::FORMAT_PARSE_NUMBER_FAILED.split(':').next().unwrap()));
134    }
135
136    #[cfg(feature = "std")]
137    #[test]
138    fn test_std_error_trait() {
139        use std::error::Error as StdError;
140
141        let error = Error::InvalidData("Test".to_string());
142        // Verify it implements std::error::Error
143        let _: &dyn StdError = &error;
144
145        // Verify source() returns None (no underlying error)
146        assert!(error.source().is_none());
147    }
148}