dbc_rs/error/
mod.rs

1use core::{convert::From, fmt};
2
3// ParseIntError is used in From<ParseIntError> implementations
4#[cfg(any(feature = "alloc", feature = "kernel"))]
5use core::num::ParseIntError;
6
7// Type alias for String based on feature flags
8// Kernel takes priority when both are enabled
9#[cfg(feature = "kernel")]
10use crate::kernel::alloc::string::String as ErrorString;
11#[cfg(all(feature = "alloc", not(feature = "kernel")))]
12use alloc::string::String as ErrorString;
13
14// Helper function to convert &str to ErrorString
15#[cfg(feature = "kernel")]
16pub(crate) fn str_to_error_string(s: &str) -> ErrorString {
17    crate::kernel::alloc::string::String::from_str(s)
18}
19
20#[cfg(all(feature = "alloc", not(feature = "kernel")))]
21pub(crate) fn str_to_error_string(s: &str) -> ErrorString {
22    alloc::string::String::from(s)
23}
24
25pub mod lang;
26
27/// Error type for DBC parsing and validation operations.
28///
29/// This enum represents all possible errors that can occur when working with DBC files.
30/// Most variants require the `alloc` feature to be enabled.
31#[derive(Debug, PartialEq)]
32pub enum Error {
33    /// Invalid data error (e.g., parse failures, invalid formats).
34    #[cfg(any(feature = "alloc", feature = "kernel"))]
35    InvalidData(ErrorString),
36
37    /// Signal-related error (e.g., invalid signal definition).
38    #[cfg(any(feature = "alloc", feature = "kernel"))]
39    Signal(ErrorString),
40
41    /// Message-related error (e.g., invalid message definition).
42    #[cfg(any(feature = "alloc", feature = "kernel"))]
43    Message(ErrorString),
44
45    /// DBC file-level error (e.g., missing required sections).
46    #[cfg(any(feature = "alloc", feature = "kernel"))]
47    Dbc(ErrorString),
48
49    /// Version parsing error.
50    #[cfg(any(feature = "alloc", feature = "kernel"))]
51    Version(ErrorString),
52
53    /// Node-related error (e.g., duplicate node names).
54    #[cfg(any(feature = "alloc", feature = "kernel"))]
55    Nodes(ErrorString),
56
57    /// Low-level parse error (available in `no_std` builds).
58    ParseError(ParseError),
59}
60
61/// Low-level parsing error that can occur during DBC file parsing.
62///
63/// This error type is available in both `std` and `no_std` builds.
64#[derive(Debug, PartialEq, Clone, Copy)]
65pub enum ParseError {
66    /// Unexpected end of input encountered.
67    UnexpectedEof,
68
69    /// Expected a specific token or value.
70    Expected(&'static str),
71
72    /// Invalid character encountered.
73    InvalidChar(char),
74
75    /// String length exceeds the maximum allowed length.
76    MaxStrLength(u16),
77
78    /// Version-related parse error.
79    Version(&'static str),
80
81    /// Message-related parse error.
82    Message(&'static str),
83
84    /// Receivers-related parse error.
85    Receivers(&'static str),
86
87    /// Nodes-related parse error.
88    Nodes(&'static str),
89
90    /// Signal-related parse error.
91    Signal(&'static str),
92}
93
94impl fmt::Display for ParseError {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        match self {
97            ParseError::UnexpectedEof => write!(f, "Unexpected end of input"),
98            ParseError::Expected(msg) => write!(f, "Expected {}", msg),
99            ParseError::InvalidChar(c) => write!(f, "Invalid character: {}", c),
100            ParseError::MaxStrLength(max) => write!(f, "String length exceeds maximum: {}", max),
101            ParseError::Version(msg) => write!(f, "Version error: {}", msg),
102            ParseError::Message(msg) => write!(f, "Message error: {}", msg),
103            ParseError::Receivers(msg) => write!(f, "Receivers error: {}", msg),
104            ParseError::Nodes(msg) => write!(f, "Nodes error: {}", msg),
105            ParseError::Signal(msg) => write!(f, "Signal error: {}", msg),
106        }
107    }
108}
109
110/// Result type alias for operations that can return an `Error`.
111pub type Result<T> = core::result::Result<T, Error>;
112
113/// Result type alias for low-level parsing operations that can return a `ParseError`.
114pub type ParseResult<T> = core::result::Result<T, ParseError>;
115
116// Helper functions for creating Error variants (simplifies error creation)
117#[cfg(any(feature = "alloc", feature = "kernel"))]
118impl Error {
119    pub(crate) fn signal(msg: &'static str) -> Self {
120        Error::Signal(str_to_error_string(msg))
121    }
122
123    pub(crate) fn message(msg: &'static str) -> Self {
124        Error::Message(str_to_error_string(msg))
125    }
126
127    pub(crate) fn dbc(msg: &'static str) -> Self {
128        Error::Dbc(str_to_error_string(msg))
129    }
130
131    pub(crate) fn version(msg: &'static str) -> Self {
132        Error::Version(str_to_error_string(msg))
133    }
134
135    pub(crate) fn nodes(msg: &'static str) -> Self {
136        Error::Nodes(str_to_error_string(msg))
137    }
138}
139
140// Unified Display implementation for alloc and kernel
141#[cfg(any(feature = "alloc", feature = "kernel"))]
142impl fmt::Display for Error {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        match self {
145            Error::InvalidData(msg) => write!(f, "{}: {}", lang::INVALID_DATA_CATEGORY, msg),
146            Error::Signal(msg) => write!(f, "{}: {}", lang::SIGNAL_ERROR_CATEGORY, msg),
147            Error::Message(msg) => write!(f, "{}: {}", lang::MESSAGE_ERROR_CATEGORY, msg),
148            Error::Dbc(msg) => write!(f, "{}: {}", lang::DBC_ERROR_CATEGORY, msg),
149            Error::Version(msg) => write!(f, "{}: {}", lang::VERSION_ERROR_CATEGORY, msg),
150            Error::Nodes(msg) => write!(f, "{}: {}", lang::NODES_ERROR_CATEGORY, msg),
151            Error::ParseError(msg) => write!(f, "Parse Error: {}", msg),
152        }
153    }
154}
155
156// Unified From<ParseIntError> implementation for alloc and kernel
157#[cfg(any(feature = "alloc", feature = "kernel"))]
158impl From<ParseIntError> for Error {
159    fn from(_err: ParseIntError) -> Self {
160        Error::InvalidData(str_to_error_string(lang::PARSE_NUMBER_FAILED))
161    }
162}
163
164#[cfg(not(any(feature = "alloc", feature = "kernel")))]
165impl From<core::num::ParseIntError> for Error {
166    fn from(_err: core::num::ParseIntError) -> Self {
167        // In no_std, we can only return ParseError
168        // ParseIntError conversion is not fully supported in no_std
169        Error::ParseError(ParseError::Expected("Invalid number format"))
170    }
171}
172
173impl From<ParseError> for Error {
174    fn from(err: ParseError) -> Self {
175        Error::ParseError(err)
176    }
177}
178
179// std::error::Error is only available with std feature (which requires alloc)
180// Display is already implemented for alloc feature, so this should work
181#[cfg(all(feature = "std", feature = "alloc", not(feature = "kernel")))]
182impl std::error::Error for Error {
183    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
184        None
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    #![allow(clippy::float_cmp)]
191
192    // Tests that require alloc or kernel feature (for Display/ToString)
193    #[cfg(any(feature = "alloc", feature = "kernel"))]
194    mod tests_with_alloc {
195        use crate::error::{Error, lang};
196
197        #[test]
198        fn test_from_parse_int_error() {
199            // Create a ParseIntError by trying to parse an invalid string
200            let parse_error = "invalid".parse::<u32>().unwrap_err();
201            let error: Error = parse_error.into();
202
203            match error {
204                Error::InvalidData(msg) => {
205                    assert!(msg.contains(lang::PARSE_NUMBER_FAILED));
206                }
207                _ => panic!("Expected InvalidData error"),
208            }
209        }
210
211        #[test]
212        fn test_display_invalid_data() {
213            use crate::compat::{display_to_string, str_to_string};
214            let error = Error::InvalidData(str_to_string("Test error message"));
215            let display = display_to_string(error);
216            assert!(display.starts_with(lang::INVALID_DATA_CATEGORY));
217            assert!(display.contains("Test error message"));
218        }
219
220        #[test]
221        fn test_display_signal_error() {
222            use crate::compat::{display_to_string, str_to_string};
223            let error = Error::Signal(str_to_string("Test signal error"));
224            let display = display_to_string(error);
225            assert!(display.starts_with(lang::SIGNAL_ERROR_CATEGORY));
226            assert!(display.contains("Test signal error"));
227        }
228
229        #[test]
230        fn test_display_formatting() {
231            use crate::compat::{display_to_string, str_to_string};
232            // Test that Display properly formats complex error messages
233            let error = Error::InvalidData(str_to_string(
234                "Duplicate message ID: 256 (messages 'EngineData' and 'BrakeData')",
235            ));
236            let display = display_to_string(error);
237            assert!(display.starts_with(lang::INVALID_DATA_CATEGORY));
238            assert!(display.contains("256"));
239            assert!(display.contains("EngineData"));
240            assert!(display.contains("BrakeData"));
241        }
242
243        #[test]
244        fn test_display_parse_error() {
245            use crate::compat::display_to_string;
246            let parse_error = "not_a_number".parse::<u32>().unwrap_err();
247            let error: Error = parse_error.into();
248            let display = display_to_string(error);
249
250            assert!(display.starts_with(lang::INVALID_DATA_CATEGORY));
251            assert!(display.contains(lang::PARSE_NUMBER_FAILED));
252        }
253    }
254
255    // Tests that require std feature (for std::error::Error trait)
256    // Only available when std and alloc are enabled, but kernel is not
257    #[cfg(all(feature = "std", feature = "alloc", not(feature = "kernel")))]
258    mod tests_std {
259        use crate::error::Error;
260        use std::error::Error as StdError;
261
262        #[test]
263        fn test_std_error_trait() {
264            use crate::compat::str_to_string;
265            let error = Error::InvalidData(str_to_string("Test"));
266            // Verify it implements std::error::Error
267            let _: &dyn StdError = &error;
268
269            // Verify source() returns None (no underlying error)
270            assert!(error.source().is_none());
271        }
272    }
273}