1#[derive(Debug, thiserror::Error)]
14#[non_exhaustive]
15pub enum LibmagicError {
16 #[error("Parse error: {0}")]
18 ParseError(#[from] ParseError),
19
20 #[error("Evaluation error: {0}")]
22 EvaluationError(#[from] EvaluationError),
23
24 #[error("I/O error: {0}")]
26 IoError(#[from] std::io::Error),
27
28 #[error("Evaluation timeout exceeded after {timeout_ms}ms")]
30 Timeout {
31 timeout_ms: u64,
33 },
34
35 #[error("Configuration error: {reason}")]
48 ConfigError {
49 reason: String,
51 },
52
53 #[error("File error: {0}")]
68 FileError(String),
69}
70
71#[derive(Debug, thiserror::Error)]
73#[non_exhaustive]
74pub enum ParseError {
75 #[error("Invalid syntax at line {line}: {message}")]
77 InvalidSyntax {
78 line: usize,
80 message: String,
82 },
83
84 #[error("Unsupported feature at line {line}: {feature}")]
86 UnsupportedFeature {
87 line: usize,
89 feature: String,
91 },
92
93 #[error("Invalid offset specification at line {line}: {offset}")]
95 InvalidOffset {
96 line: usize,
98 offset: String,
100 },
101
102 #[error("Invalid type specification at line {line}: {type_spec}")]
104 InvalidType {
105 line: usize,
107 type_spec: String,
109 },
110
111 #[error("Invalid operator at line {line}: {operator}")]
113 InvalidOperator {
114 line: usize,
116 operator: String,
118 },
119
120 #[error("Invalid value at line {line}: {value}")]
122 InvalidValue {
123 line: usize,
125 value: String,
127 },
128
129 #[error("Unsupported format at line {line}: {format_type}\n{message}")]
131 UnsupportedFormat {
132 line: usize,
134 format_type: String,
136 message: String,
138 },
139
140 #[error("I/O error: {0}")]
142 IoError(#[from] std::io::Error),
143}
144
145#[derive(Debug, thiserror::Error)]
147#[non_exhaustive]
148pub enum EvaluationError {
149 #[error("Buffer overrun at offset {offset}")]
151 BufferOverrun {
152 offset: usize,
154 },
155
156 #[error("Invalid offset: {offset}")]
158 InvalidOffset {
159 offset: i64,
161 },
162
163 #[error("Unsupported type: {type_name}")]
165 UnsupportedType {
166 type_name: String,
168 },
169
170 #[error("Recursion limit exceeded (depth: {depth})")]
172 RecursionLimitExceeded {
173 depth: u32,
175 },
176
177 #[error("String length limit exceeded: {length} > {max_length}")]
179 StringLengthExceeded {
180 length: usize,
182 max_length: usize,
184 },
185
186 #[error("Invalid string encoding at offset {offset}")]
188 InvalidStringEncoding {
189 offset: usize,
191 },
192
193 #[error("Evaluation timeout exceeded after {timeout_ms}ms")]
195 Timeout {
196 timeout_ms: u64,
198 },
199
200 #[error("Type reading error: {0}")]
202 TypeReadError(#[from] crate::evaluator::types::TypeReadError),
203
204 #[error("invalid value transform: {reason}")]
224 InvalidValueTransform {
225 reason: String,
228 },
229
230 #[error("use directive references unknown name: {name}")]
249 UnknownName {
250 name: String,
252 },
253
254 #[error("Internal error: {message}")]
256 InternalError {
257 message: String,
259 },
260
261 #[error("indirect directive evaluated without a rule environment")]
284 IndirectWithoutEnvironment,
285}
286
287impl ParseError {
288 #[must_use]
290 pub fn invalid_syntax(line: usize, message: impl Into<String>) -> Self {
291 Self::InvalidSyntax {
292 line,
293 message: message.into(),
294 }
295 }
296
297 #[must_use]
299 pub fn unsupported_feature(line: usize, feature: impl Into<String>) -> Self {
300 Self::UnsupportedFeature {
301 line,
302 feature: feature.into(),
303 }
304 }
305
306 #[must_use]
308 pub fn invalid_offset(line: usize, offset: impl Into<String>) -> Self {
309 Self::InvalidOffset {
310 line,
311 offset: offset.into(),
312 }
313 }
314
315 #[must_use]
317 pub fn invalid_type(line: usize, type_spec: impl Into<String>) -> Self {
318 Self::InvalidType {
319 line,
320 type_spec: type_spec.into(),
321 }
322 }
323
324 #[must_use]
326 pub fn invalid_operator(line: usize, operator: impl Into<String>) -> Self {
327 Self::InvalidOperator {
328 line,
329 operator: operator.into(),
330 }
331 }
332
333 #[must_use]
335 pub fn invalid_value(line: usize, value: impl Into<String>) -> Self {
336 Self::InvalidValue {
337 line,
338 value: value.into(),
339 }
340 }
341
342 #[must_use]
344 pub fn unsupported_format(
345 line: usize,
346 format_type: impl Into<String>,
347 message: impl Into<String>,
348 ) -> Self {
349 Self::UnsupportedFormat {
350 line,
351 format_type: format_type.into(),
352 message: message.into(),
353 }
354 }
355}
356
357impl EvaluationError {
358 #[must_use]
360 pub fn buffer_overrun(offset: usize) -> Self {
361 Self::BufferOverrun { offset }
362 }
363
364 #[must_use]
366 pub fn invalid_offset(offset: i64) -> Self {
367 Self::InvalidOffset { offset }
368 }
369
370 #[must_use]
372 pub fn unsupported_type(type_name: impl Into<String>) -> Self {
373 Self::UnsupportedType {
374 type_name: type_name.into(),
375 }
376 }
377
378 #[must_use]
380 pub fn recursion_limit_exceeded(depth: u32) -> Self {
381 Self::RecursionLimitExceeded { depth }
382 }
383
384 #[must_use]
386 pub fn string_length_exceeded(length: usize, max_length: usize) -> Self {
387 Self::StringLengthExceeded { length, max_length }
388 }
389
390 #[must_use]
392 pub fn invalid_string_encoding(offset: usize) -> Self {
393 Self::InvalidStringEncoding { offset }
394 }
395
396 #[must_use]
398 pub fn timeout(timeout_ms: u64) -> Self {
399 Self::Timeout { timeout_ms }
400 }
401
402 #[must_use]
404 pub fn internal_error(message: impl Into<String>) -> Self {
405 Self::InternalError {
406 message: message.into(),
407 }
408 }
409
410 #[must_use]
412 pub const fn indirect_without_environment() -> Self {
413 Self::IndirectWithoutEnvironment
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420 use std::io;
421
422 #[test]
423 fn test_libmagic_error_from_parse_error() {
424 let parse_error = ParseError::invalid_syntax(10, "unexpected token");
425 let libmagic_error = LibmagicError::from(parse_error);
426
427 match libmagic_error {
428 LibmagicError::ParseError(_) => (),
429 _ => panic!("Expected ParseError variant"),
430 }
431 }
432
433 #[test]
434 fn test_libmagic_error_from_evaluation_error() {
435 let eval_error = EvaluationError::buffer_overrun(100);
436 let libmagic_error = LibmagicError::from(eval_error);
437
438 match libmagic_error {
439 LibmagicError::EvaluationError(_) => (),
440 _ => panic!("Expected EvaluationError variant"),
441 }
442 }
443
444 #[test]
445 fn test_libmagic_error_from_io_error() {
446 let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found");
447 let libmagic_error = LibmagicError::from(io_error);
448
449 match libmagic_error {
450 LibmagicError::IoError(_) => (),
451 _ => panic!("Expected IoError variant"),
452 }
453 }
454
455 #[test]
456 fn test_parse_error_display() {
457 let error = ParseError::invalid_syntax(5, "missing operator");
458 let display = format!("{error}");
459 assert_eq!(display, "Invalid syntax at line 5: missing operator");
460 }
461
462 #[test]
463 fn test_parse_error_unsupported_feature() {
464 let error = ParseError::unsupported_feature(12, "regex patterns");
465 let display = format!("{error}");
466 assert_eq!(display, "Unsupported feature at line 12: regex patterns");
467 }
468
469 #[test]
470 fn test_parse_error_invalid_offset() {
471 let error = ParseError::invalid_offset(8, "invalid_offset_spec");
472 let display = format!("{error}");
473 assert_eq!(
474 display,
475 "Invalid offset specification at line 8: invalid_offset_spec"
476 );
477 }
478
479 #[test]
480 fn test_parse_error_invalid_type() {
481 let error = ParseError::invalid_type(15, "unknown_type");
482 let display = format!("{error}");
483 assert_eq!(
484 display,
485 "Invalid type specification at line 15: unknown_type"
486 );
487 }
488
489 #[test]
490 fn test_parse_error_invalid_operator() {
491 let error = ParseError::invalid_operator(20, "??");
492 let display = format!("{error}");
493 assert_eq!(display, "Invalid operator at line 20: ??");
494 }
495
496 #[test]
497 fn test_parse_error_invalid_value() {
498 let error = ParseError::invalid_value(25, "malformed_hex");
499 let display = format!("{error}");
500 assert_eq!(display, "Invalid value at line 25: malformed_hex");
501 }
502
503 #[test]
504 fn test_evaluation_error_buffer_overrun() {
505 let error = EvaluationError::buffer_overrun(1024);
506 let display = format!("{error}");
507 assert_eq!(display, "Buffer overrun at offset 1024");
508 }
509
510 #[test]
511 fn test_evaluation_error_invalid_offset() {
512 let error = EvaluationError::invalid_offset(-50);
513 let display = format!("{error}");
514 assert_eq!(display, "Invalid offset: -50");
515 }
516
517 #[test]
518 fn test_evaluation_error_unsupported_type() {
519 let error = EvaluationError::unsupported_type("complex_type");
520 let display = format!("{error}");
521 assert_eq!(display, "Unsupported type: complex_type");
522 }
523
524 #[test]
525 fn test_evaluation_error_recursion_limit() {
526 let error = EvaluationError::recursion_limit_exceeded(100);
527 let display = format!("{error}");
528 assert_eq!(display, "Recursion limit exceeded (depth: 100)");
529 }
530
531 #[test]
532 fn test_evaluation_error_string_length_exceeded() {
533 let error = EvaluationError::string_length_exceeded(2048, 1024);
534 let display = format!("{error}");
535 assert_eq!(display, "String length limit exceeded: 2048 > 1024");
536 }
537
538 #[test]
539 fn test_evaluation_error_invalid_string_encoding() {
540 let error = EvaluationError::invalid_string_encoding(512);
541 let display = format!("{error}");
542 assert_eq!(display, "Invalid string encoding at offset 512");
543 }
544
545 #[test]
546 fn test_evaluation_error_internal_error() {
547 let error = EvaluationError::internal_error("recursion depth underflow");
548 let display = format!("{error}");
549 assert_eq!(display, "Internal error: recursion depth underflow");
550 }
551
552 #[test]
553 fn test_libmagic_error_display_parse() {
554 let parse_error = ParseError::invalid_syntax(10, "unexpected token");
555 let libmagic_error = LibmagicError::from(parse_error);
556 let display = format!("{libmagic_error}");
557 assert_eq!(
558 display,
559 "Parse error: Invalid syntax at line 10: unexpected token"
560 );
561 }
562
563 #[test]
564 fn test_libmagic_error_display_evaluation() {
565 let eval_error = EvaluationError::buffer_overrun(100);
566 let libmagic_error = LibmagicError::from(eval_error);
567 let display = format!("{libmagic_error}");
568 assert_eq!(display, "Evaluation error: Buffer overrun at offset 100");
569 }
570
571 #[test]
572 fn test_libmagic_error_display_io() {
573 let io_error = io::Error::new(io::ErrorKind::PermissionDenied, "access denied");
574 let libmagic_error = LibmagicError::from(io_error);
575 let display = format!("{libmagic_error}");
576 assert!(display.starts_with("I/O error:"));
577 assert!(display.contains("access denied"));
578 }
579
580 #[test]
581 fn test_error_debug_formatting() {
582 let error = LibmagicError::ParseError(ParseError::invalid_syntax(5, "test"));
583 let debug = format!("{error:?}");
584 assert!(debug.contains("ParseError"));
585 assert!(debug.contains("InvalidSyntax"));
586 }
587
588 #[test]
589 fn test_parse_error_constructors() {
590 let error1 = ParseError::invalid_syntax(1, "test");
591 let error2 = ParseError::unsupported_feature(2, "feature");
592 let error3 = ParseError::invalid_offset(3, "offset");
593 let error4 = ParseError::invalid_type(4, "type");
594 let error5 = ParseError::invalid_operator(5, "op");
595 let error6 = ParseError::invalid_value(6, "value");
596
597 assert!(matches!(error1, ParseError::InvalidSyntax { .. }));
599 assert!(matches!(error2, ParseError::UnsupportedFeature { .. }));
600 assert!(matches!(error3, ParseError::InvalidOffset { .. }));
601 assert!(matches!(error4, ParseError::InvalidType { .. }));
602 assert!(matches!(error5, ParseError::InvalidOperator { .. }));
603 assert!(matches!(error6, ParseError::InvalidValue { .. }));
604 }
605
606 #[test]
607 fn test_evaluation_error_constructors() {
608 let error1 = EvaluationError::buffer_overrun(100);
609 let error2 = EvaluationError::invalid_offset(-1);
610 let error3 = EvaluationError::unsupported_type("test");
611 let error4 = EvaluationError::recursion_limit_exceeded(50);
612 let error5 = EvaluationError::string_length_exceeded(100, 50);
613 let error6 = EvaluationError::invalid_string_encoding(200);
614
615 assert!(matches!(error1, EvaluationError::BufferOverrun { .. }));
617 assert!(matches!(error2, EvaluationError::InvalidOffset { .. }));
618 assert!(matches!(error3, EvaluationError::UnsupportedType { .. }));
619 assert!(matches!(
620 error4,
621 EvaluationError::RecursionLimitExceeded { .. }
622 ));
623 assert!(matches!(
624 error5,
625 EvaluationError::StringLengthExceeded { .. }
626 ));
627 assert!(matches!(
628 error6,
629 EvaluationError::InvalidStringEncoding { .. }
630 ));
631 }
632
633 #[test]
634 fn test_parse_error_unsupported_format() {
635 let error = ParseError::unsupported_format(
636 0,
637 "binary .mgc",
638 "Binary files not supported. Use --use-builtin option.",
639 );
640 let display = format!("{error}");
641 assert!(display.contains("Unsupported format"));
642 assert!(display.contains("binary .mgc"));
643 assert!(display.contains("--use-builtin"));
644 }
645
646 #[test]
647 fn test_parse_error_unsupported_format_constructor() {
648 let error = ParseError::unsupported_format(5, "test_format", "test message");
649
650 match error {
651 ParseError::UnsupportedFormat {
652 line,
653 format_type,
654 message,
655 } => {
656 assert_eq!(line, 5);
657 assert_eq!(format_type, "test_format");
658 assert_eq!(message, "test message");
659 }
660 _ => panic!("Expected UnsupportedFormat variant"),
661 }
662 }
663}