1#[cfg(not(feature = "std"))]
29extern crate alloc;
30#[cfg(not(feature = "std"))]
31use alloc::format;
32mod category;
33mod core;
34pub mod encoding;
35mod format;
36pub mod resource;
37
38pub use category::ErrorCategory;
40pub use core::{CoreError, Result};
41
42pub use encoding::{
44 utf8_error, validate_ass_text_content, validate_bom_handling, validate_utf8_detailed,
45};
46pub use format::{invalid_color, invalid_numeric, invalid_time, validate_color_format};
47pub use resource::{
48 check_memory_limit, feature_not_supported, out_of_memory, resource_limit_exceeded,
49};
50
51impl CoreError {
52 pub fn invalid_color<T: ::core::fmt::Display>(format: T) -> Self {
54 format::invalid_color(format)
55 }
56
57 pub fn invalid_numeric<T: ::core::fmt::Display>(value: T, reason: &str) -> Self {
59 format::invalid_numeric(value, reason)
60 }
61
62 pub fn invalid_time<T: ::core::fmt::Display>(time: T, reason: &str) -> Self {
64 format::invalid_time(time, reason)
65 }
66
67 #[must_use]
69 pub const fn utf8_error(position: usize, message: alloc::string::String) -> Self {
70 encoding::utf8_error(position, message)
71 }
72
73 #[must_use]
75 pub fn feature_not_supported(feature: &str, required_feature: &str) -> Self {
76 resource::feature_not_supported(feature, required_feature)
77 }
78
79 #[must_use]
81 pub fn resource_limit_exceeded(resource: &str, current: usize, limit: usize) -> Self {
82 resource::resource_limit_exceeded(resource, current, limit)
83 }
84}
85
86impl From<crate::parser::ParseError> for CoreError {
88 fn from(err: crate::parser::ParseError) -> Self {
89 Self::Parse(err)
90 }
91}
92
93#[cfg(feature = "std")]
95impl From<std::io::Error> for CoreError {
96 fn from(err: std::io::Error) -> Self {
97 Self::Io(format!("{err}"))
98 }
99}
100
101impl From<::core::str::Utf8Error> for CoreError {
103 fn from(err: ::core::str::Utf8Error) -> Self {
104 Self::Utf8Error {
105 position: 0, message: format!("{err}"),
107 }
108 }
109}
110
111impl From<::core::num::ParseIntError> for CoreError {
113 fn from(err: ::core::num::ParseIntError) -> Self {
114 Self::InvalidNumeric(format!("Integer parse error: {err}"))
115 }
116}
117
118impl From<::core::num::ParseFloatError> for CoreError {
120 fn from(err: ::core::num::ParseFloatError) -> Self {
121 Self::InvalidNumeric(format!("Float parse error: {err}"))
122 }
123}
124
125#[cfg(test)]
126mod tests;
127
128#[cfg(test)]
129mod inline_tests {
130 use super::*;
131 #[cfg(not(feature = "std"))]
132 use alloc::{format, string::ToString, vec};
133
134 #[test]
135 fn error_creation_methods() {
136 let parse_err = CoreError::parse("test message");
137 assert!(matches!(parse_err, CoreError::Parse(_)));
138
139 let color_err = CoreError::invalid_color("invalid");
140 assert!(matches!(color_err, CoreError::InvalidColor(_)));
141
142 let time_err = CoreError::invalid_time("invalid", "wrong format");
143 assert!(matches!(time_err, CoreError::InvalidTime(_)));
144 }
145
146 #[test]
147 fn error_display() {
148 let error = CoreError::invalid_color("test");
149 let display_str = format!("{error}");
150 assert!(display_str.contains("Invalid color format"));
151 assert!(display_str.contains("test"));
152 }
153
154 #[test]
155 fn error_conversion() {
156 let parse_int_err: ::core::num::ParseIntError = "abc".parse::<i32>().unwrap_err();
157 let core_err: CoreError = parse_int_err.into();
158 assert!(matches!(core_err, CoreError::InvalidNumeric(_)));
159
160 let parse_float_err: ::core::num::ParseFloatError = "xyz".parse::<f32>().unwrap_err();
161 let core_err: CoreError = parse_float_err.into();
162 assert!(matches!(core_err, CoreError::InvalidNumeric(_)));
163 }
164
165 #[test]
166 fn error_properties() {
167 let error = CoreError::invalid_color("test");
168 assert_eq!(error.category(), ErrorCategory::Format);
169 assert!(error.suggestion().is_some());
170 assert!(error.is_recoverable());
171 assert!(!error.is_internal_bug());
172 }
173
174 #[test]
175 fn result_type_alias() {
176 fn test_function() -> i32 {
177 42
178 }
179
180 assert_eq!(test_function(), 42);
181 }
182
183 #[test]
184 fn core_error_invalid_color_convenience() {
185 let color_err = CoreError::invalid_color("red");
186 assert!(matches!(color_err, CoreError::InvalidColor(_)));
187 if let CoreError::InvalidColor(msg) = color_err {
188 assert!(msg.contains("red"));
189 }
190 }
191
192 #[test]
193 fn core_error_invalid_numeric_convenience() {
194 let numeric_err = CoreError::invalid_numeric("abc", "not a number");
195 assert!(matches!(numeric_err, CoreError::InvalidNumeric(_)));
196 if let CoreError::InvalidNumeric(msg) = numeric_err {
197 assert!(msg.contains("abc"));
198 assert!(msg.contains("not a number"));
199 }
200 }
201
202 #[test]
203 fn core_error_invalid_time_convenience() {
204 let time_err = CoreError::invalid_time("25:00:00", "hours out of range");
205 assert!(matches!(time_err, CoreError::InvalidTime(_)));
206 if let CoreError::InvalidTime(msg) = time_err {
207 assert!(msg.contains("25:00:00"));
208 assert!(msg.contains("hours out of range"));
209 }
210 }
211
212 #[test]
213 fn core_error_utf8_error_convenience() {
214 let utf8_err = CoreError::utf8_error(42, "invalid sequence".to_string());
215 assert!(matches!(utf8_err, CoreError::Utf8Error { .. }));
216 if let CoreError::Utf8Error { position, message } = utf8_err {
217 assert_eq!(position, 42);
218 assert_eq!(message, "invalid sequence");
219 }
220 }
221
222 #[test]
223 fn core_error_feature_not_supported_convenience() {
224 let feature_err = CoreError::feature_not_supported("simd", "cpu-features");
225 assert!(matches!(feature_err, CoreError::FeatureNotSupported { .. }));
226 if let CoreError::FeatureNotSupported {
227 feature,
228 required_feature,
229 } = feature_err
230 {
231 assert_eq!(feature, "simd");
232 assert_eq!(required_feature, "cpu-features");
233 }
234 }
235
236 #[test]
237 fn core_error_resource_limit_exceeded_convenience() {
238 let resource_err = CoreError::resource_limit_exceeded("memory", 1024, 512);
239 assert!(matches!(
240 resource_err,
241 CoreError::ResourceLimitExceeded { .. }
242 ));
243 if let CoreError::ResourceLimitExceeded {
244 resource,
245 current,
246 limit,
247 } = resource_err
248 {
249 assert_eq!(resource, "memory");
250 assert_eq!(current, 1024);
251 assert_eq!(limit, 512);
252 }
253 }
254
255 #[test]
256 fn utf8_error_conversion() {
257 let invalid_bytes = vec![0xFF, 0xFE];
258 let utf8_err = ::core::str::from_utf8(&invalid_bytes).unwrap_err();
259 let core_err: CoreError = utf8_err.into();
260 assert!(matches!(core_err, CoreError::Utf8Error { .. }));
261 if let CoreError::Utf8Error { position, message } = core_err {
262 assert_eq!(position, 0); assert!(message.contains("invalid utf-8"));
264 }
265 }
266
267 #[cfg(feature = "std")]
268 #[test]
269 fn io_error_conversion() {
270 use std::io::{Error, ErrorKind};
271
272 let io_err = Error::new(ErrorKind::NotFound, "file not found");
273 let core_err: CoreError = io_err.into();
274 assert!(matches!(core_err, CoreError::Io(_)));
275 if let CoreError::Io(msg) = core_err {
276 assert!(msg.contains("file not found"));
277 }
278 }
279
280 #[test]
281 fn parse_error_conversion() {
282 let parse_err = crate::parser::ParseError::IoError {
283 message: "io failure".to_string(),
284 };
285 let core_err: CoreError = parse_err.into();
286 assert!(matches!(core_err, CoreError::Parse(_)));
287 }
288
289 #[test]
290 fn numeric_conversion_edge_cases() {
291 let int_overflow: ::core::num::ParseIntError =
293 "999999999999999999999999999".parse::<i32>().unwrap_err();
294 let core_err: CoreError = int_overflow.into();
295 assert!(matches!(core_err, CoreError::InvalidNumeric(_)));
296 if let CoreError::InvalidNumeric(msg) = core_err {
297 assert!(msg.contains("Integer parse error"));
298 }
299
300 let float_err: ::core::num::ParseFloatError = "not_a_float".parse::<f64>().unwrap_err();
301 let core_err: CoreError = float_err.into();
302 assert!(matches!(core_err, CoreError::InvalidNumeric(_)));
303 if let CoreError::InvalidNumeric(msg) = core_err {
304 assert!(msg.contains("Float parse error"));
305 }
306 }
307
308 #[test]
309 fn module_re_exports() {
310 let _color_err = invalid_color("test");
312 let _numeric_err = invalid_numeric("test", "reason");
313 let _time_err = invalid_time("test", "reason");
314 let _utf8_err = utf8_error(0, "message".to_string());
315 let _feature_err = feature_not_supported("feature", "required");
316 let _resource_err = resource_limit_exceeded("resource", 100, 50);
317 let _memory_err = out_of_memory("test");
318 let _limit_err = check_memory_limit(1000, 500, 800);
319
320 let _validated = validate_utf8_detailed(b"test");
322 let _color_validated = validate_color_format("&H000000");
323 let _ass_validated = validate_ass_text_content("test");
324 let _bom_validated = validate_bom_handling(b"test");
325 }
326
327 #[test]
328 fn error_display_consistency() {
329 let errors = vec![
331 CoreError::Tokenization("test".to_string()),
332 CoreError::Analysis("test".to_string()),
333 CoreError::Plugin("test".to_string()),
334 CoreError::InvalidColor("test".to_string()),
335 CoreError::InvalidNumeric("test".to_string()),
336 CoreError::InvalidTime("test".to_string()),
337 CoreError::Io("test".to_string()),
338 CoreError::OutOfMemory("test".to_string()),
339 CoreError::Config("test".to_string()),
340 CoreError::Validation("test".to_string()),
341 CoreError::SecurityViolation("test".to_string()),
342 CoreError::Internal("test".to_string()),
343 ];
344
345 for error in errors {
346 let display_str = format!("{error}");
347 assert!(!display_str.is_empty());
348 assert!(display_str.contains("test"));
349 }
350
351 let utf8_err = CoreError::Utf8Error {
353 position: 42,
354 message: "test".to_string(),
355 };
356 let display_str = format!("{utf8_err}");
357 assert!(display_str.contains("42"));
358 assert!(display_str.contains("test"));
359
360 let feature_err = CoreError::FeatureNotSupported {
361 feature: "feature1".to_string(),
362 required_feature: "feature2".to_string(),
363 };
364 let display_str = format!("{feature_err}");
365 assert!(display_str.contains("feature1"));
366 assert!(display_str.contains("feature2"));
367
368 let version_err = CoreError::VersionIncompatible {
369 message: "version mismatch".to_string(),
370 };
371 let display_str = format!("{version_err}");
372 assert!(display_str.contains("version mismatch"));
373
374 let resource_err = CoreError::ResourceLimitExceeded {
375 resource: "memory".to_string(),
376 current: 100,
377 limit: 50,
378 };
379 let display_str = format!("{resource_err}");
380 assert!(display_str.contains("memory"));
381 assert!(display_str.contains("100"));
382 assert!(display_str.contains("50"));
383 }
384}