1use core::{convert::From, fmt, num::ParseIntError};
2
3pub mod lang;
4pub(crate) mod messages;
5
6#[derive(Debug, PartialEq)]
11pub enum Error {
12 #[cfg(feature = "alloc")]
14 InvalidData(String),
15
16 #[cfg(feature = "alloc")]
18 Signal(String),
19
20 #[cfg(feature = "alloc")]
22 Message(String),
23
24 #[cfg(feature = "alloc")]
26 Dbc(String),
27
28 #[cfg(feature = "alloc")]
30 Version(String),
31
32 #[cfg(feature = "alloc")]
34 Nodes(String),
35
36 ParseError(ParseError),
38}
39
40#[derive(Debug, PartialEq, Clone, Copy)]
44pub enum ParseError {
45 UnexpectedEof,
47
48 Expected(&'static str),
50
51 InvalidChar(char),
53
54 MaxStrLength(u16),
56
57 Version(&'static str),
59}
60
61impl fmt::Display for ParseError {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 ParseError::UnexpectedEof => write!(f, "Unexpected end of input"),
65 ParseError::Expected(msg) => write!(f, "Expected {}", msg),
66 ParseError::InvalidChar(c) => write!(f, "Invalid character: {}", c),
67 ParseError::MaxStrLength(max) => write!(f, "String length exceeds maximum: {}", max),
68 ParseError::Version(msg) => write!(f, "Version error: {}", msg),
69 }
70 }
71}
72
73pub type Result<T> = core::result::Result<T, Error>;
75
76pub type ParseResult<T> = core::result::Result<T, ParseError>;
78
79#[cfg(feature = "alloc")]
80impl fmt::Display for Error {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match self {
83 Error::InvalidData(msg) => {
84 write!(f, "{}", messages::format_invalid_data(msg))
86 }
87 Error::Signal(msg) => {
88 write!(f, "{}", messages::format_signal_error(msg))
90 }
91 Error::Message(msg) => {
92 write!(f, "{}", messages::format_message_error(msg))
93 }
94 Error::Dbc(msg) => {
95 write!(f, "{}", messages::format_dbc_error(msg))
96 }
97 Error::Version(msg) => {
98 write!(f, "{}", messages::format_version_error(msg))
99 }
100 Error::Nodes(msg) => {
101 write!(f, "{}", messages::format_nodes_error(msg))
102 }
103 Error::ParseError(msg) => {
104 write!(f, "Parse Error: {}", msg)
105 }
106 }
107 }
108}
109
110#[cfg(feature = "alloc")]
111impl From<ParseIntError> for Error {
112 fn from(err: ParseIntError) -> Self {
113 Error::InvalidData(messages::parse_number_failed(err))
114 }
115}
116
117#[cfg(not(feature = "alloc"))]
118impl From<ParseIntError> for Error {
119 fn from(_err: ParseIntError) -> Self {
120 Error::ParseError(ParseError::Expected("Invalid number format"))
123 }
124}
125
126impl From<ParseError> for Error {
127 fn from(err: ParseError) -> Self {
128 Error::ParseError(err)
129 }
130}
131
132#[cfg(feature = "std")]
133impl std::error::Error for Error {
134 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
135 None
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 #![allow(clippy::float_cmp)]
142 use super::Error;
143 use crate::error::lang;
144 use alloc::string::ToString;
145
146 #[test]
147 fn test_from_parse_int_error() {
148 let parse_error = "invalid".parse::<u32>().unwrap_err();
150 let error: Error = parse_error.into();
151
152 match error {
153 Error::InvalidData(msg) => {
154 assert!(msg.contains(lang::FORMAT_PARSE_NUMBER_FAILED.split(':').next().unwrap()));
155 }
156 _ => panic!("Expected InvalidData error"),
157 }
158 }
159
160 #[test]
161 fn test_display_invalid_data() {
162 let error = Error::InvalidData("Test error message".to_string());
163 let display = error.to_string();
164 assert!(display.starts_with(lang::INVALID_DATA_CATEGORY));
165 assert!(display.contains("Test error message"));
166 }
167
168 #[test]
169 fn test_display_signal_error() {
170 let error = Error::Signal(lang::SIGNAL_NAME_EMPTY.to_string());
171 let display = error.to_string();
172 assert!(display.starts_with(lang::SIGNAL_ERROR_CATEGORY));
173 assert!(display.contains(lang::SIGNAL_NAME_EMPTY));
174 }
175
176 #[test]
177 fn test_display_formatting() {
178 let error = Error::InvalidData(
180 "Duplicate message ID: 256 (messages 'EngineData' and 'BrakeData')".to_string(),
181 );
182 let display = error.to_string();
183 assert!(display.starts_with(lang::INVALID_DATA_CATEGORY));
184 assert!(display.contains("256"));
185 assert!(display.contains("EngineData"));
186 assert!(display.contains("BrakeData"));
187 }
188
189 #[test]
190 fn test_display_parse_error() {
191 let parse_error = "not_a_number".parse::<u32>().unwrap_err();
192 let error: Error = parse_error.into();
193 let display = error.to_string();
194
195 assert!(display.starts_with(lang::INVALID_DATA_CATEGORY));
196 assert!(display.contains(lang::FORMAT_PARSE_NUMBER_FAILED.split(':').next().unwrap()));
197 }
198
199 #[cfg(feature = "std")]
200 #[test]
201 fn test_std_error_trait() {
202 use std::error::Error as StdError;
203
204 let error = Error::InvalidData("Test".to_string());
205 let _: &dyn StdError = &error;
207
208 assert!(error.source().is_none());
210 }
211}