Skip to main content

libmagic_rs/evaluator/
mod.rs

1// Copyright (c) 2025-2026 the libmagic-rs contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Rule evaluation engine
5//!
6//! This module contains the core evaluation logic for executing magic rules
7//! against file buffers to identify file types.
8
9use crate::parser::ast::MagicRule;
10use crate::{EvaluationConfig, LibmagicError};
11use serde::{Deserialize, Serialize};
12
13#[cfg(test)]
14use crate::parser::ast::{Endianness, OffsetSpec, Operator, TypeKind, Value};
15
16pub mod offset;
17pub mod operators;
18pub mod strength;
19pub mod types;
20
21/// Context for maintaining evaluation state during rule processing
22///
23/// The `EvaluationContext` tracks the current state of rule evaluation,
24/// including the current offset position, recursion depth for nested rules,
25/// and configuration settings that control evaluation behavior.
26///
27/// # Examples
28///
29/// ```rust
30/// use libmagic_rs::evaluator::EvaluationContext;
31/// use libmagic_rs::EvaluationConfig;
32///
33/// let config = EvaluationConfig::default();
34/// let context = EvaluationContext::new(config);
35///
36/// assert_eq!(context.current_offset(), 0);
37/// assert_eq!(context.recursion_depth(), 0);
38/// ```
39#[derive(Debug, Clone)]
40pub struct EvaluationContext {
41    /// Current offset position in the file buffer
42    current_offset: usize,
43    /// Current recursion depth for nested rule evaluation
44    recursion_depth: u32,
45    /// Configuration settings for evaluation behavior
46    config: EvaluationConfig,
47}
48
49impl EvaluationContext {
50    /// Create a new evaluation context with the given configuration
51    ///
52    /// # Arguments
53    ///
54    /// * `config` - Configuration settings for evaluation behavior
55    ///
56    /// # Examples
57    ///
58    /// ```rust
59    /// use libmagic_rs::evaluator::EvaluationContext;
60    /// use libmagic_rs::EvaluationConfig;
61    ///
62    /// let config = EvaluationConfig::default();
63    /// let context = EvaluationContext::new(config);
64    /// ```
65    #[must_use]
66    pub const fn new(config: EvaluationConfig) -> Self {
67        Self {
68            current_offset: 0,
69            recursion_depth: 0,
70            config,
71        }
72    }
73
74    /// Get the current offset position
75    ///
76    /// # Returns
77    ///
78    /// The current offset position in the file buffer
79    #[must_use]
80    pub const fn current_offset(&self) -> usize {
81        self.current_offset
82    }
83
84    /// Set the current offset position
85    ///
86    /// # Arguments
87    ///
88    /// * `offset` - The new offset position
89    pub fn set_current_offset(&mut self, offset: usize) {
90        self.current_offset = offset;
91    }
92
93    /// Get the current recursion depth
94    ///
95    /// # Returns
96    ///
97    /// The current recursion depth for nested rule evaluation
98    #[must_use]
99    pub const fn recursion_depth(&self) -> u32 {
100        self.recursion_depth
101    }
102
103    /// Increment the recursion depth
104    ///
105    /// # Returns
106    ///
107    /// `Ok(())` if the recursion depth is within limits, or `Err(LibmagicError)`
108    /// if the maximum recursion depth would be exceeded
109    ///
110    /// # Errors
111    ///
112    /// Returns `LibmagicError::EvaluationError` if incrementing would exceed
113    /// the maximum recursion depth configured in the evaluation config.
114    pub fn increment_recursion_depth(&mut self) -> Result<(), LibmagicError> {
115        if self.recursion_depth >= self.config.max_recursion_depth {
116            return Err(LibmagicError::EvaluationError(
117                crate::error::EvaluationError::recursion_limit_exceeded(self.recursion_depth),
118            ));
119        }
120        self.recursion_depth += 1;
121        Ok(())
122    }
123
124    /// Decrement the recursion depth
125    ///
126    /// # Errors
127    ///
128    /// Returns an error if the recursion depth is already 0, as this indicates
129    /// a programming error in the evaluation logic (mismatched increment/decrement calls).
130    pub fn decrement_recursion_depth(&mut self) -> Result<(), LibmagicError> {
131        if self.recursion_depth == 0 {
132            return Err(LibmagicError::EvaluationError(
133                crate::error::EvaluationError::internal_error(
134                    "Attempted to decrement recursion depth below 0",
135                ),
136            ));
137        }
138        self.recursion_depth -= 1;
139        Ok(())
140    }
141
142    /// Get a reference to the evaluation configuration
143    ///
144    /// # Returns
145    ///
146    /// A reference to the `EvaluationConfig` used by this context
147    #[must_use]
148    pub const fn config(&self) -> &EvaluationConfig {
149        &self.config
150    }
151
152    /// Check if evaluation should stop at the first match
153    ///
154    /// # Returns
155    ///
156    /// `true` if evaluation should stop at the first match, `false` otherwise
157    #[must_use]
158    pub const fn should_stop_at_first_match(&self) -> bool {
159        self.config.stop_at_first_match
160    }
161
162    /// Get the maximum string length allowed
163    ///
164    /// # Returns
165    ///
166    /// The maximum string length that should be read during evaluation
167    #[must_use]
168    pub const fn max_string_length(&self) -> usize {
169        self.config.max_string_length
170    }
171
172    /// Check if MIME type mapping is enabled
173    ///
174    /// # Returns
175    ///
176    /// `true` if MIME type mapping should be performed, `false` otherwise
177    #[must_use]
178    pub const fn enable_mime_types(&self) -> bool {
179        self.config.enable_mime_types
180    }
181
182    /// Get the evaluation timeout in milliseconds
183    ///
184    /// # Returns
185    ///
186    /// The timeout duration in milliseconds, or `None` if no timeout is set
187    #[must_use]
188    pub const fn timeout_ms(&self) -> Option<u64> {
189        self.config.timeout_ms
190    }
191
192    /// Reset the context to initial state while preserving configuration
193    ///
194    /// This resets the current offset and recursion depth to 0, but keeps
195    /// the same configuration settings.
196    pub fn reset(&mut self) {
197        self.current_offset = 0;
198        self.recursion_depth = 0;
199    }
200}
201
202/// Result of evaluating a magic rule
203///
204/// Contains information about a successful rule match, including the rule
205/// that matched and its associated message.
206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
207pub struct RuleMatch {
208    /// The message associated with the matching rule
209    pub message: String,
210    /// The offset where the match occurred
211    pub offset: usize,
212    /// The rule level (depth in hierarchy)
213    pub level: u32,
214    /// The matched value
215    pub value: crate::parser::ast::Value,
216    /// Confidence score (0.0 to 1.0)
217    ///
218    /// Calculated based on match depth in the rule hierarchy.
219    /// Deeper matches indicate more specific file type identification
220    /// and thus higher confidence.
221    pub confidence: f64,
222}
223
224impl RuleMatch {
225    /// Calculate confidence score based on rule depth
226    ///
227    /// Formula: min(1.0, 0.3 + (level * 0.2))
228    /// - Level 0 (root): 0.3
229    /// - Level 1: 0.5
230    /// - Level 2: 0.7
231    /// - Level 3: 0.9
232    /// - Level 4+: 1.0 (capped)
233    ///
234    /// # Examples
235    ///
236    /// ```
237    /// use libmagic_rs::evaluator::RuleMatch;
238    ///
239    /// assert!((RuleMatch::calculate_confidence(0) - 0.3).abs() < 0.001);
240    /// assert!((RuleMatch::calculate_confidence(3) - 0.9).abs() < 0.001);
241    /// assert!((RuleMatch::calculate_confidence(10) - 1.0).abs() < 0.001);
242    /// ```
243    #[must_use]
244    pub fn calculate_confidence(level: u32) -> f64 {
245        (0.3 + (f64::from(level) * 0.2)).min(1.0)
246    }
247}
248
249/// Evaluate a single magic rule against a file buffer
250///
251/// This function performs the core rule evaluation by:
252/// 1. Resolving the rule's offset specification to an absolute position
253/// 2. Reading and interpreting bytes at that position according to the rule's type
254/// 3. Applying the rule's operator to compare the read value with the expected value
255///
256/// # Arguments
257///
258/// * `rule` - The magic rule to evaluate
259/// * `buffer` - The file buffer to evaluate against
260///
261/// # Returns
262///
263/// Returns `Ok(Some((offset, value)))` if the rule matches (with the resolved offset and
264/// read value), `Ok(None)` if it doesn't match, or `Err(LibmagicError)` if evaluation
265/// fails due to buffer access issues or other errors.
266///
267/// # Examples
268///
269/// ```rust
270/// use libmagic_rs::evaluator::evaluate_single_rule;
271/// use libmagic_rs::parser::ast::{MagicRule, OffsetSpec, TypeKind, Operator, Value};
272///
273/// // Create a rule to check for ELF magic bytes at offset 0
274/// let rule = MagicRule {
275///     offset: OffsetSpec::Absolute(0),
276///     typ: TypeKind::Byte { signed: true },
277///     op: Operator::Equal,
278///     value: Value::Uint(0x7f),
279///     message: "ELF magic".to_string(),
280///     children: vec![],
281///     level: 0,
282///     strength_modifier: None,
283/// };
284///
285/// let elf_buffer = &[0x7f, 0x45, 0x4c, 0x46]; // ELF magic bytes
286/// let result = evaluate_single_rule(&rule, elf_buffer).unwrap();
287/// assert!(result.is_some()); // Should match
288///
289/// let non_elf_buffer = &[0x50, 0x4b, 0x03, 0x04]; // ZIP magic bytes
290/// let result = evaluate_single_rule(&rule, non_elf_buffer).unwrap();
291/// assert!(result.is_none()); // Should not match
292/// ```
293///
294/// # Errors
295///
296/// * `LibmagicError::EvaluationError` - If offset resolution fails, buffer access is out of bounds,
297///   or type interpretation fails
298pub fn evaluate_single_rule(
299    rule: &MagicRule,
300    buffer: &[u8],
301) -> Result<Option<(usize, crate::parser::ast::Value)>, LibmagicError> {
302    // Step 1: Resolve the offset specification to an absolute position
303    let absolute_offset = offset::resolve_offset(&rule.offset, buffer)?;
304
305    // Step 2: Read and interpret bytes at the resolved offset according to the rule's type
306    let read_value = types::read_typed_value(buffer, absolute_offset, &rule.typ)
307        .map_err(|e| LibmagicError::EvaluationError(e.into()))?;
308
309    // Step 3: Coerce the rule's expected value to match the type's signedness/width
310    let expected_value = types::coerce_value_to_type(&rule.value, &rule.typ);
311
312    // Step 4: Apply the operator to compare the read value with the expected value
313    Ok(
314        operators::apply_operator(&rule.op, &read_value, &expected_value)
315            .then_some((absolute_offset, read_value)),
316    )
317}
318
319/// Evaluate a list of magic rules against a file buffer with hierarchical processing
320///
321/// This function implements the core hierarchical rule evaluation algorithm with graceful
322/// error handling:
323/// 1. Evaluates each top-level rule in sequence
324/// 2. If a parent rule matches, evaluates its child rules for refinement
325/// 3. Collects all matches or stops at first match based on configuration
326/// 4. Maintains evaluation context for recursion limits and state
327/// 5. Implements graceful degradation by skipping problematic rules and continuing evaluation
328///
329/// The hierarchical evaluation follows these principles:
330/// - Parent rules must match before children are evaluated
331/// - Child rules provide refinement and additional detail
332/// - Evaluation can stop at first match or continue for all matches
333/// - Recursion depth is limited to prevent infinite loops
334/// - Problematic rules are skipped to allow evaluation to continue
335///
336/// # Arguments
337///
338/// * `rules` - The list of magic rules to evaluate
339/// * `buffer` - The file buffer to evaluate against
340/// * `context` - Mutable evaluation context for state management
341///
342/// # Returns
343///
344/// Returns `Ok(Vec<RuleMatch>)` containing all matches found. Errors in individual rules
345/// are logged and skipped to allow evaluation to continue. Only returns `Err(LibmagicError)`
346/// for critical failures like timeout or recursion limit exceeded.
347///
348/// # Examples
349///
350/// ```rust
351/// use libmagic_rs::evaluator::{evaluate_rules, EvaluationContext, RuleMatch};
352/// use libmagic_rs::parser::ast::{MagicRule, OffsetSpec, TypeKind, Operator, Value};
353/// use libmagic_rs::EvaluationConfig;
354///
355/// // Create a hierarchical rule set for ELF files
356/// let parent_rule = MagicRule {
357///     offset: OffsetSpec::Absolute(0),
358///     typ: TypeKind::Byte { signed: true },
359///     op: Operator::Equal,
360///     value: Value::Uint(0x7f),
361///     message: "ELF".to_string(),
362///     children: vec![
363///         MagicRule {
364///             offset: OffsetSpec::Absolute(4),
365///             typ: TypeKind::Byte { signed: true },
366///             op: Operator::Equal,
367///             value: Value::Uint(2),
368///             message: "64-bit".to_string(),
369///             children: vec![],
370///             level: 1,
371///             strength_modifier: None,
372///         }
373///     ],
374///     level: 0,
375///     strength_modifier: None,
376/// };
377///
378/// let rules = vec![parent_rule];
379/// let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; // ELF64 header
380/// let config = EvaluationConfig::default();
381/// let mut context = EvaluationContext::new(config);
382///
383/// let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
384/// assert_eq!(matches.len(), 2); // Parent and child should both match
385/// ```
386///
387/// # Errors
388///
389/// * `LibmagicError::Timeout` - If evaluation exceeds configured timeout
390/// * `LibmagicError::EvaluationError` - Only for critical failures like recursion limit exceeded
391///
392/// Individual rule evaluation errors are handled gracefully and do not stop the overall evaluation.
393pub fn evaluate_rules(
394    rules: &[MagicRule],
395    buffer: &[u8],
396    context: &mut EvaluationContext,
397) -> Result<Vec<RuleMatch>, LibmagicError> {
398    let mut matches = Vec::with_capacity(8);
399    let start_time = std::time::Instant::now();
400    let mut rule_count = 0u32;
401
402    for rule in rules {
403        // Check timeout periodically (every 16 rules) to reduce syscall overhead
404        rule_count = rule_count.wrapping_add(1);
405        if rule_count.trailing_zeros() >= 4 {
406            if let Some(timeout_ms) = context.timeout_ms() {
407                if start_time.elapsed().as_millis() > u128::from(timeout_ms) {
408                    return Err(LibmagicError::Timeout { timeout_ms });
409                }
410            }
411        }
412
413        // Evaluate the current rule with graceful error handling
414        let match_data = match evaluate_single_rule(rule, buffer) {
415            Ok(data) => data,
416            Err(
417                LibmagicError::EvaluationError(
418                    crate::error::EvaluationError::BufferOverrun { .. }
419                    | crate::error::EvaluationError::InvalidOffset { .. }
420                    | crate::error::EvaluationError::TypeReadError(_),
421                )
422                | LibmagicError::IoError(_),
423            ) => {
424                // Expected evaluation errors for individual rules -- skip gracefully
425                continue;
426            }
427            Err(e) => {
428                // Unexpected errors (InternalError, UnsupportedType, etc.) should propagate
429                return Err(e);
430            }
431        };
432
433        if let Some((absolute_offset, read_value)) = match_data {
434            let match_result = RuleMatch {
435                message: rule.message.clone(),
436                offset: absolute_offset,
437                level: rule.level,
438                value: read_value,
439                confidence: RuleMatch::calculate_confidence(rule.level),
440            };
441            matches.push(match_result);
442
443            // If this rule has children, evaluate them recursively
444            if !rule.children.is_empty() {
445                // Check recursion depth limit - this is a critical error that should stop evaluation
446                context.increment_recursion_depth()?;
447
448                // Recursively evaluate child rules with graceful error handling
449                match evaluate_rules(&rule.children, buffer, context) {
450                    Ok(child_matches) => {
451                        matches.extend(child_matches);
452                    }
453                    Err(LibmagicError::Timeout { .. }) => {
454                        // Timeout is critical, propagate it up
455                        context.decrement_recursion_depth()?;
456                        return Err(LibmagicError::Timeout {
457                            timeout_ms: context.timeout_ms().unwrap_or(0),
458                        });
459                    }
460                    Err(LibmagicError::EvaluationError(
461                        crate::error::EvaluationError::RecursionLimitExceeded { .. },
462                    )) => {
463                        // Recursion limit is critical, propagate it up
464                        context.decrement_recursion_depth()?;
465                        return Err(LibmagicError::EvaluationError(
466                            crate::error::EvaluationError::RecursionLimitExceeded {
467                                depth: context.recursion_depth(),
468                            },
469                        ));
470                    }
471                    Err(
472                        LibmagicError::EvaluationError(
473                            crate::error::EvaluationError::BufferOverrun { .. }
474                            | crate::error::EvaluationError::InvalidOffset { .. }
475                            | crate::error::EvaluationError::TypeReadError(_),
476                        )
477                        | LibmagicError::IoError(_),
478                    ) => {
479                        // Expected child evaluation errors -- skip gracefully
480                    }
481                    Err(e) => {
482                        // Unexpected errors in children should propagate
483                        context.decrement_recursion_depth()?;
484                        return Err(e);
485                    }
486                }
487
488                // Restore recursion depth
489                context.decrement_recursion_depth()?;
490            }
491
492            // Stop at first match if configured to do so
493            if context.should_stop_at_first_match() {
494                break;
495            }
496        }
497    }
498
499    Ok(matches)
500}
501
502/// Evaluate magic rules with a fresh context
503///
504/// This is a convenience function that creates a new evaluation context
505/// and evaluates the rules. Useful for simple evaluation scenarios.
506///
507/// # Arguments
508///
509/// * `rules` - The list of magic rules to evaluate
510/// * `buffer` - The file buffer to evaluate against
511/// * `config` - Configuration for evaluation behavior
512///
513/// # Returns
514///
515/// Returns `Ok(Vec<RuleMatch>)` containing all matches found, or `Err(LibmagicError)`
516/// if evaluation fails.
517///
518/// # Examples
519///
520/// ```rust
521/// use libmagic_rs::evaluator::{evaluate_rules_with_config, RuleMatch};
522/// use libmagic_rs::parser::ast::{MagicRule, OffsetSpec, TypeKind, Operator, Value};
523/// use libmagic_rs::EvaluationConfig;
524///
525/// let rule = MagicRule {
526///     offset: OffsetSpec::Absolute(0),
527///     typ: TypeKind::Byte { signed: true },
528///     op: Operator::Equal,
529///     value: Value::Uint(0x7f),
530///     message: "ELF magic".to_string(),
531///     children: vec![],
532///     level: 0,
533///     strength_modifier: None,
534/// };
535///
536/// let rules = vec![rule];
537/// let buffer = &[0x7f, 0x45, 0x4c, 0x46];
538/// let config = EvaluationConfig::default();
539///
540/// let matches = evaluate_rules_with_config(&rules, buffer, &config).unwrap();
541/// assert_eq!(matches.len(), 1);
542/// assert_eq!(matches[0].message, "ELF magic");
543/// ```
544///
545/// # Errors
546///
547/// * `LibmagicError::EvaluationError` - If rule evaluation fails
548/// * `LibmagicError::Timeout` - If evaluation exceeds configured timeout
549pub fn evaluate_rules_with_config(
550    rules: &[MagicRule],
551    buffer: &[u8],
552    config: &EvaluationConfig,
553) -> Result<Vec<RuleMatch>, LibmagicError> {
554    let mut context = EvaluationContext::new(config.clone());
555    evaluate_rules(rules, buffer, &mut context)
556}
557
558#[cfg(test)]
559mod tests {
560    use super::*;
561    use crate::parser::ast::{Endianness, OffsetSpec, Operator, TypeKind, Value};
562
563    #[test]
564    fn test_evaluate_single_rule_byte_equal_match() {
565        let rule = MagicRule {
566            offset: OffsetSpec::Absolute(0),
567            typ: TypeKind::Byte { signed: true },
568            op: Operator::Equal,
569            value: Value::Uint(0x7f),
570            message: "ELF magic".to_string(),
571            children: vec![],
572            level: 0,
573            strength_modifier: None,
574        };
575
576        let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // ELF magic bytes
577        let result = evaluate_single_rule(&rule, buffer).unwrap();
578        assert!(result.is_some());
579    }
580
581    #[test]
582    fn test_evaluate_single_rule_byte_equal_no_match() {
583        let rule = MagicRule {
584            offset: OffsetSpec::Absolute(0),
585            typ: TypeKind::Byte { signed: true },
586            op: Operator::Equal,
587            value: Value::Uint(0x7f),
588            message: "ELF magic".to_string(),
589            children: vec![],
590            level: 0,
591            strength_modifier: None,
592        };
593
594        let buffer = &[0x50, 0x4b, 0x03, 0x04]; // ZIP magic bytes
595        let result = evaluate_single_rule(&rule, buffer).unwrap();
596        assert!(result.is_none());
597    }
598
599    #[test]
600    fn test_evaluate_single_rule_byte_not_equal_match() {
601        let rule = MagicRule {
602            offset: OffsetSpec::Absolute(0),
603            typ: TypeKind::Byte { signed: true },
604            op: Operator::NotEqual,
605            value: Value::Uint(0x00),
606            message: "Non-zero byte".to_string(),
607            children: vec![],
608            level: 0,
609            strength_modifier: None,
610        };
611
612        let buffer = &[0x7f, 0x45, 0x4c, 0x46];
613        let result = evaluate_single_rule(&rule, buffer).unwrap();
614        assert!(result.is_some()); // 0x7f != 0x00
615    }
616
617    #[test]
618    fn test_evaluate_single_rule_byte_not_equal_no_match() {
619        let rule = MagicRule {
620            offset: OffsetSpec::Absolute(0),
621            typ: TypeKind::Byte { signed: true },
622            op: Operator::NotEqual,
623            value: Value::Uint(0x7f),
624            message: "Not ELF magic".to_string(),
625            children: vec![],
626            level: 0,
627            strength_modifier: None,
628        };
629
630        let buffer = &[0x7f, 0x45, 0x4c, 0x46];
631        let result = evaluate_single_rule(&rule, buffer).unwrap();
632        assert!(result.is_none()); // 0x7f == 0x7f, so NotEqual is false
633    }
634
635    #[test]
636    fn test_evaluate_single_rule_byte_bitwise_and_match() {
637        let rule = MagicRule {
638            offset: OffsetSpec::Absolute(0),
639            typ: TypeKind::Byte { signed: true },
640            op: Operator::BitwiseAnd,
641            value: Value::Uint(0x80), // Check if high bit is set
642            message: "High bit set".to_string(),
643            children: vec![],
644            level: 0,
645            strength_modifier: None,
646        };
647
648        let buffer = &[0xff, 0x45, 0x4c, 0x46]; // 0xff has high bit set
649        let result = evaluate_single_rule(&rule, buffer).unwrap();
650        assert!(result.is_some()); // 0xff & 0x80 = 0x80 (non-zero)
651    }
652
653    #[test]
654    fn test_evaluate_single_rule_byte_bitwise_and_no_match() {
655        let rule = MagicRule {
656            offset: OffsetSpec::Absolute(0),
657            typ: TypeKind::Byte { signed: true },
658            op: Operator::BitwiseAnd,
659            value: Value::Uint(0x80), // Check if high bit is set
660            message: "High bit set".to_string(),
661            children: vec![],
662            level: 0,
663            strength_modifier: None,
664        };
665
666        let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // 0x7f has high bit clear
667        let result = evaluate_single_rule(&rule, buffer).unwrap();
668        assert!(result.is_none()); // 0x7f & 0x80 = 0x00 (zero)
669    }
670
671    #[test]
672    fn test_evaluate_single_rule_short_little_endian() {
673        let rule = MagicRule {
674            offset: OffsetSpec::Absolute(0),
675            typ: TypeKind::Short {
676                endian: Endianness::Little,
677                signed: false,
678            },
679            op: Operator::Equal,
680            value: Value::Uint(0x1234),
681            message: "Little-endian short".to_string(),
682            children: vec![],
683            level: 0,
684            strength_modifier: None,
685        };
686
687        let buffer = &[0x34, 0x12, 0x56, 0x78]; // 0x1234 in little-endian
688        let result = evaluate_single_rule(&rule, buffer).unwrap();
689        assert!(result.is_some());
690    }
691
692    #[test]
693    fn test_evaluate_single_rule_short_big_endian() {
694        let rule = MagicRule {
695            offset: OffsetSpec::Absolute(0),
696            typ: TypeKind::Short {
697                endian: Endianness::Big,
698                signed: false,
699            },
700            op: Operator::Equal,
701            value: Value::Uint(0x1234),
702            message: "Big-endian short".to_string(),
703            children: vec![],
704            level: 0,
705            strength_modifier: None,
706        };
707
708        let buffer = &[0x12, 0x34, 0x56, 0x78]; // 0x1234 in big-endian
709        let result = evaluate_single_rule(&rule, buffer).unwrap();
710        assert!(result.is_some());
711    }
712
713    #[test]
714    fn test_evaluate_single_rule_short_signed_positive() {
715        let rule = MagicRule {
716            offset: OffsetSpec::Absolute(0),
717            typ: TypeKind::Short {
718                endian: Endianness::Little,
719                signed: true,
720            },
721            op: Operator::Equal,
722            value: Value::Int(32767), // 0x7fff
723            message: "Positive signed short".to_string(),
724            children: vec![],
725            level: 0,
726            strength_modifier: None,
727        };
728
729        let buffer = &[0xff, 0x7f, 0x00, 0x00]; // 0x7fff in little-endian
730        let result = evaluate_single_rule(&rule, buffer).unwrap();
731        assert!(result.is_some());
732    }
733
734    #[test]
735    fn test_evaluate_single_rule_short_signed_negative() {
736        let rule = MagicRule {
737            offset: OffsetSpec::Absolute(0),
738            typ: TypeKind::Short {
739                endian: Endianness::Little,
740                signed: true,
741            },
742            op: Operator::Equal,
743            value: Value::Int(-1), // 0xffff as signed
744            message: "Negative signed short".to_string(),
745            children: vec![],
746            level: 0,
747            strength_modifier: None,
748        };
749
750        let buffer = &[0xff, 0xff, 0x00, 0x00]; // 0xffff in little-endian
751        let result = evaluate_single_rule(&rule, buffer).unwrap();
752        assert!(result.is_some());
753    }
754
755    #[test]
756    fn test_evaluate_single_rule_long_little_endian() {
757        let rule = MagicRule {
758            offset: OffsetSpec::Absolute(0),
759            typ: TypeKind::Long {
760                endian: Endianness::Little,
761                signed: false,
762            },
763            op: Operator::Equal,
764            value: Value::Uint(0x1234_5678),
765            message: "Little-endian long".to_string(),
766            children: vec![],
767            level: 0,
768            strength_modifier: None,
769        };
770
771        let buffer = &[0x78, 0x56, 0x34, 0x12, 0x00]; // 0x12345678 in little-endian
772        let result = evaluate_single_rule(&rule, buffer).unwrap();
773        assert!(result.is_some());
774    }
775
776    #[test]
777    fn test_evaluate_single_rule_long_big_endian() {
778        let rule = MagicRule {
779            offset: OffsetSpec::Absolute(0),
780            typ: TypeKind::Long {
781                endian: Endianness::Big,
782                signed: false,
783            },
784            op: Operator::Equal,
785            value: Value::Uint(0x1234_5678),
786            message: "Big-endian long".to_string(),
787            children: vec![],
788            level: 0,
789            strength_modifier: None,
790        };
791
792        let buffer = &[0x12, 0x34, 0x56, 0x78, 0x00]; // 0x12345678 in big-endian
793        let result = evaluate_single_rule(&rule, buffer).unwrap();
794        assert!(result.is_some());
795    }
796
797    #[test]
798    fn test_evaluate_single_rule_long_signed_positive() {
799        let rule = MagicRule {
800            offset: OffsetSpec::Absolute(0),
801            typ: TypeKind::Long {
802                endian: Endianness::Little,
803                signed: true,
804            },
805            op: Operator::Equal,
806            value: Value::Int(2_147_483_647), // 0x7fffffff
807            message: "Positive signed long".to_string(),
808            children: vec![],
809            level: 0,
810            strength_modifier: None,
811        };
812
813        let buffer = &[0xff, 0xff, 0xff, 0x7f, 0x00]; // 0x7fffffff in little-endian
814        let result = evaluate_single_rule(&rule, buffer).unwrap();
815        assert!(result.is_some());
816    }
817
818    #[test]
819    fn test_evaluate_single_rule_long_signed_negative() {
820        let rule = MagicRule {
821            offset: OffsetSpec::Absolute(0),
822            typ: TypeKind::Long {
823                endian: Endianness::Little,
824                signed: true,
825            },
826            op: Operator::Equal,
827            value: Value::Int(-1), // 0xffffffff as signed
828            message: "Negative signed long".to_string(),
829            children: vec![],
830            level: 0,
831            strength_modifier: None,
832        };
833
834        let buffer = &[0xff, 0xff, 0xff, 0xff, 0x00]; // 0xffffffff in little-endian
835        let result = evaluate_single_rule(&rule, buffer).unwrap();
836        assert!(result.is_some());
837    }
838
839    #[test]
840    fn test_evaluate_single_rule_different_offsets() {
841        let rule = MagicRule {
842            offset: OffsetSpec::Absolute(2), // Read from offset 2
843            typ: TypeKind::Byte { signed: true },
844            op: Operator::Equal,
845            value: Value::Uint(0x4c),
846            message: "ELF class byte".to_string(),
847            children: vec![],
848            level: 0,
849            strength_modifier: None,
850        };
851
852        let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // ELF magic bytes
853        let result = evaluate_single_rule(&rule, buffer).unwrap();
854        assert!(result.is_some()); // buffer[2] == 0x4c
855    }
856
857    #[test]
858    fn test_evaluate_single_rule_negative_offset() {
859        let rule = MagicRule {
860            offset: OffsetSpec::Absolute(-1), // Last byte
861            typ: TypeKind::Byte { signed: true },
862            op: Operator::Equal,
863            value: Value::Uint(0x46),
864            message: "Last byte".to_string(),
865            children: vec![],
866            level: 0,
867            strength_modifier: None,
868        };
869
870        let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // ELF magic bytes
871        let result = evaluate_single_rule(&rule, buffer).unwrap();
872        assert!(result.is_some()); // Last byte is 0x46
873    }
874
875    #[test]
876    fn test_evaluate_single_rule_from_end_offset() {
877        let rule = MagicRule {
878            offset: OffsetSpec::FromEnd(-2), // Second to last byte
879            typ: TypeKind::Byte { signed: true },
880            op: Operator::Equal,
881            value: Value::Uint(0x4c),
882            message: "Second to last byte".to_string(),
883            children: vec![],
884            level: 0,
885            strength_modifier: None,
886        };
887
888        let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // ELF magic bytes
889        let result = evaluate_single_rule(&rule, buffer).unwrap();
890        assert!(result.is_some()); // buffer[2] == 0x4c (second to last)
891    }
892
893    #[test]
894    fn test_evaluate_single_rule_offset_out_of_bounds() {
895        let rule = MagicRule {
896            offset: OffsetSpec::Absolute(10), // Beyond buffer
897            typ: TypeKind::Byte { signed: true },
898            op: Operator::Equal,
899            value: Value::Uint(0x00),
900            message: "Out of bounds".to_string(),
901            children: vec![],
902            level: 0,
903            strength_modifier: None,
904        };
905
906        let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // Only 4 bytes
907        let result = evaluate_single_rule(&rule, buffer);
908        assert!(result.is_err());
909
910        match result.unwrap_err() {
911            LibmagicError::EvaluationError(msg) => {
912                let error_string = format!("{msg}");
913                assert!(error_string.contains("Buffer overrun"));
914            }
915            _ => panic!("Expected EvaluationError"),
916        }
917    }
918
919    #[test]
920    fn test_evaluate_single_rule_short_insufficient_bytes() {
921        let rule = MagicRule {
922            offset: OffsetSpec::Absolute(3), // Only 1 byte left
923            typ: TypeKind::Short {
924                endian: Endianness::Little,
925                signed: false,
926            },
927            op: Operator::Equal,
928            value: Value::Uint(0x1234),
929            message: "Insufficient bytes".to_string(),
930            children: vec![],
931            level: 0,
932            strength_modifier: None,
933        };
934
935        let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // 4 bytes total
936        let result = evaluate_single_rule(&rule, buffer);
937        assert!(result.is_err());
938
939        match result.unwrap_err() {
940            LibmagicError::EvaluationError(msg) => {
941                let error_string = format!("{msg}");
942                assert!(error_string.contains("Buffer overrun"));
943            }
944            _ => panic!("Expected EvaluationError"),
945        }
946    }
947
948    #[test]
949    fn test_evaluate_single_rule_long_insufficient_bytes() {
950        let rule = MagicRule {
951            offset: OffsetSpec::Absolute(2), // Only 2 bytes left
952            typ: TypeKind::Long {
953                endian: Endianness::Little,
954                signed: false,
955            },
956            op: Operator::Equal,
957            value: Value::Uint(0x1234_5678),
958            message: "Insufficient bytes".to_string(),
959            children: vec![],
960            level: 0,
961            strength_modifier: None,
962        };
963
964        let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // 4 bytes total
965        let result = evaluate_single_rule(&rule, buffer);
966        assert!(result.is_err());
967
968        match result.unwrap_err() {
969            LibmagicError::EvaluationError(msg) => {
970                let error_string = format!("{msg}");
971                assert!(error_string.contains("Buffer overrun"));
972            }
973            _ => panic!("Expected EvaluationError"),
974        }
975    }
976
977    #[test]
978    fn test_evaluate_single_rule_empty_buffer() {
979        let rule = MagicRule {
980            offset: OffsetSpec::Absolute(0),
981            typ: TypeKind::Byte { signed: true },
982            op: Operator::Equal,
983            value: Value::Uint(0x00),
984            message: "Empty buffer".to_string(),
985            children: vec![],
986            level: 0,
987            strength_modifier: None,
988        };
989
990        let buffer = &[]; // Empty buffer
991        let result = evaluate_single_rule(&rule, buffer);
992        assert!(result.is_err());
993
994        match result.unwrap_err() {
995            LibmagicError::EvaluationError(msg) => {
996                let error_string = format!("{msg}");
997                assert!(error_string.contains("Buffer overrun"));
998            }
999            _ => panic!("Expected EvaluationError"),
1000        }
1001    }
1002
1003    #[test]
1004    fn test_evaluate_single_rule_string_type_supported() {
1005        let rule = MagicRule {
1006            offset: OffsetSpec::Absolute(0),
1007            typ: TypeKind::String { max_length: None },
1008            op: Operator::Equal,
1009            value: Value::String("test".to_string()),
1010            message: "String type".to_string(),
1011            children: vec![],
1012            level: 0,
1013            strength_modifier: None,
1014        };
1015
1016        // Test matching string
1017        let buffer = b"test\x00 data";
1018        let result = evaluate_single_rule(&rule, buffer);
1019        assert!(result.is_ok());
1020        let matches = result.unwrap();
1021        assert!(matches.is_some()); // Should match
1022
1023        // Test non-matching string
1024        let rule_no_match = MagicRule {
1025            offset: OffsetSpec::Absolute(0),
1026            typ: TypeKind::String { max_length: None },
1027            op: Operator::Equal,
1028            value: Value::String("hello".to_string()),
1029            message: "String type".to_string(),
1030            children: vec![],
1031            level: 0,
1032            strength_modifier: None,
1033        };
1034
1035        let result = evaluate_single_rule(&rule_no_match, buffer);
1036        assert!(result.is_ok());
1037        let matches = result.unwrap();
1038        assert!(matches.is_none()); // Should not match
1039    }
1040}
1041
1042#[test]
1043fn test_evaluate_single_rule_cross_type_comparison() {
1044    // Test that cross-type integer comparisons use coercion (Uint(42) == Int(42))
1045    let rule = MagicRule {
1046        offset: OffsetSpec::Absolute(0),
1047        typ: TypeKind::Byte { signed: true },
1048        op: Operator::Equal,
1049        value: Value::Int(42), // Int value vs Uint from byte read
1050        message: "Cross-type comparison".to_string(),
1051        children: vec![],
1052        level: 0,
1053        strength_modifier: None,
1054    };
1055
1056    let buffer = &[42]; // Byte value 42
1057    let result = evaluate_single_rule(&rule, buffer).unwrap();
1058    assert!(result.is_some()); // Should match via cross-type integer coercion
1059}
1060
1061#[test]
1062fn test_evaluate_single_rule_bitwise_and_with_shorts() {
1063    let rule = MagicRule {
1064        offset: OffsetSpec::Absolute(0),
1065        typ: TypeKind::Short {
1066            endian: Endianness::Little,
1067            signed: false,
1068        },
1069        op: Operator::BitwiseAnd,
1070        value: Value::Uint(0xff00), // Check high byte
1071        message: "High byte check".to_string(),
1072        children: vec![],
1073        level: 0,
1074        strength_modifier: None,
1075    };
1076
1077    let buffer = &[0x34, 0x12]; // 0x1234 in little-endian
1078    let result = evaluate_single_rule(&rule, buffer).unwrap();
1079    assert!(result.is_some()); // 0x1234 & 0xff00 = 0x1200 (non-zero)
1080}
1081
1082#[test]
1083fn test_evaluate_single_rule_bitwise_and_with_longs() {
1084    let rule = MagicRule {
1085        offset: OffsetSpec::Absolute(0),
1086        typ: TypeKind::Long {
1087            endian: Endianness::Big,
1088            signed: false,
1089        },
1090        op: Operator::BitwiseAnd,
1091        value: Value::Uint(0xffff_0000), // Check high word
1092        message: "High word check".to_string(),
1093        children: vec![],
1094        level: 0,
1095        strength_modifier: None,
1096    };
1097
1098    let buffer = &[0x12, 0x34, 0x56, 0x78]; // 0x12345678 in big-endian
1099    let result = evaluate_single_rule(&rule, buffer).unwrap();
1100    assert!(result.is_some()); // 0x12345678 & 0xffff0000 = 0x12340000 (non-zero)
1101}
1102
1103#[test]
1104fn test_evaluate_single_rule_comprehensive_elf_check() {
1105    // Test a comprehensive ELF magic check
1106    let rule = MagicRule {
1107        offset: OffsetSpec::Absolute(0),
1108        typ: TypeKind::Long {
1109            endian: Endianness::Little,
1110            signed: false,
1111        },
1112        op: Operator::Equal,
1113        value: Value::Uint(0x464c_457f), // ELF magic as 32-bit little-endian
1114        message: "ELF executable".to_string(),
1115        children: vec![],
1116        level: 0,
1117        strength_modifier: None,
1118    };
1119
1120    let elf_buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; // ELF64 header start
1121    let result = evaluate_single_rule(&rule, elf_buffer).unwrap();
1122    assert!(result.is_some());
1123
1124    let non_elf_buffer = &[0x50, 0x4b, 0x03, 0x04, 0x14, 0x00]; // ZIP header
1125    let result = evaluate_single_rule(&rule, non_elf_buffer).unwrap();
1126    assert!(result.is_none());
1127}
1128
1129#[test]
1130fn test_evaluate_single_rule_native_endianness() {
1131    let rule = MagicRule {
1132        offset: OffsetSpec::Absolute(0),
1133        typ: TypeKind::Short {
1134            endian: Endianness::Native,
1135            signed: false,
1136        },
1137        op: Operator::NotEqual,
1138        value: Value::Uint(0),
1139        message: "Non-zero native short".to_string(),
1140        children: vec![],
1141        level: 0,
1142        strength_modifier: None,
1143    };
1144
1145    let buffer = &[0x01, 0x02]; // Non-zero bytes
1146    let result = evaluate_single_rule(&rule, buffer).unwrap();
1147    assert!(result.is_some()); // Should be non-zero regardless of endianness
1148}
1149
1150#[test]
1151fn test_evaluate_single_rule_all_operators() {
1152    let buffer = &[0x42, 0x00, 0xff, 0x80];
1153
1154    // Test Equal operator
1155    let equal_rule = MagicRule {
1156        offset: OffsetSpec::Absolute(0),
1157        typ: TypeKind::Byte { signed: true },
1158        op: Operator::Equal,
1159        value: Value::Uint(0x42),
1160        message: "Equal test".to_string(),
1161        children: vec![],
1162        level: 0,
1163        strength_modifier: None,
1164    };
1165    assert!(evaluate_single_rule(&equal_rule, buffer).unwrap().is_some());
1166
1167    // Test NotEqual operator
1168    let not_equal_rule = MagicRule {
1169        offset: OffsetSpec::Absolute(1),
1170        typ: TypeKind::Byte { signed: true },
1171        op: Operator::NotEqual,
1172        value: Value::Uint(0x42),
1173        message: "NotEqual test".to_string(),
1174        children: vec![],
1175        level: 0,
1176        strength_modifier: None,
1177    };
1178    assert!(
1179        evaluate_single_rule(&not_equal_rule, buffer)
1180            .unwrap()
1181            .is_some()
1182    ); // 0x00 != 0x42
1183
1184    // Test BitwiseAnd operator
1185    let bitwise_and_rule = MagicRule {
1186        offset: OffsetSpec::Absolute(3),
1187        typ: TypeKind::Byte { signed: true },
1188        op: Operator::BitwiseAnd,
1189        value: Value::Uint(0x80),
1190        message: "BitwiseAnd test".to_string(),
1191        children: vec![],
1192        level: 0,
1193        strength_modifier: None,
1194    };
1195    assert!(
1196        evaluate_single_rule(&bitwise_and_rule, buffer)
1197            .unwrap()
1198            .is_some()
1199    ); // 0x80 & 0x80 = 0x80
1200}
1201
1202#[test]
1203fn test_evaluate_single_rule_comparison_operators() {
1204    let buffer = &[0x42, 0x00, 0xff, 0x80];
1205
1206    // LessThan: byte at offset 1 is 0x00, 0x00 < 0x42 = true
1207    let less_than_rule = MagicRule {
1208        offset: OffsetSpec::Absolute(1),
1209        typ: TypeKind::Byte { signed: false },
1210        op: Operator::LessThan,
1211        value: Value::Uint(0x42),
1212        message: "LessThan test".to_string(),
1213        children: vec![],
1214        level: 0,
1215        strength_modifier: None,
1216    };
1217    assert!(
1218        evaluate_single_rule(&less_than_rule, buffer)
1219            .unwrap()
1220            .is_some()
1221    );
1222
1223    // GreaterThan: byte at offset 2 is 0xff, 0xff > 0x42 = true
1224    let greater_than_rule = MagicRule {
1225        offset: OffsetSpec::Absolute(2),
1226        typ: TypeKind::Byte { signed: false },
1227        op: Operator::GreaterThan,
1228        value: Value::Uint(0x42),
1229        message: "GreaterThan test".to_string(),
1230        children: vec![],
1231        level: 0,
1232        strength_modifier: None,
1233    };
1234    assert!(
1235        evaluate_single_rule(&greater_than_rule, buffer)
1236            .unwrap()
1237            .is_some()
1238    );
1239
1240    // LessEqual: byte at offset 0 is 0x42, 0x42 <= 0x42 = true
1241    let less_equal_rule = MagicRule {
1242        offset: OffsetSpec::Absolute(0),
1243        typ: TypeKind::Byte { signed: false },
1244        op: Operator::LessEqual,
1245        value: Value::Uint(0x42),
1246        message: "LessEqual test".to_string(),
1247        children: vec![],
1248        level: 0,
1249        strength_modifier: None,
1250    };
1251    assert!(
1252        evaluate_single_rule(&less_equal_rule, buffer)
1253            .unwrap()
1254            .is_some()
1255    );
1256
1257    // GreaterEqual: byte at offset 0 is 0x42, 0x42 >= 0x42 = true
1258    let greater_equal_rule = MagicRule {
1259        offset: OffsetSpec::Absolute(0),
1260        typ: TypeKind::Byte { signed: false },
1261        op: Operator::GreaterEqual,
1262        value: Value::Uint(0x42),
1263        message: "GreaterEqual test".to_string(),
1264        children: vec![],
1265        level: 0,
1266        strength_modifier: None,
1267    };
1268    assert!(
1269        evaluate_single_rule(&greater_equal_rule, buffer)
1270            .unwrap()
1271            .is_some()
1272    );
1273}
1274
1275#[test]
1276fn test_evaluate_comparison_with_signed_byte() {
1277    // 0x80 = -128 as signed byte, 128 as unsigned byte
1278    let buffer = &[0x80];
1279
1280    // Signed byte: reads as Int(-128), which IS less than Uint(0)
1281    let signed_rule = MagicRule {
1282        offset: OffsetSpec::Absolute(0),
1283        typ: TypeKind::Byte { signed: true },
1284        op: Operator::LessThan,
1285        value: Value::Uint(0),
1286        message: "signed less".to_string(),
1287        children: vec![],
1288        level: 0,
1289        strength_modifier: None,
1290    };
1291    assert!(
1292        evaluate_single_rule(&signed_rule, buffer)
1293            .unwrap()
1294            .is_some()
1295    );
1296
1297    // Unsigned byte: reads as Uint(128), which is NOT less than Uint(0)
1298    let unsigned_rule = MagicRule {
1299        offset: OffsetSpec::Absolute(0),
1300        typ: TypeKind::Byte { signed: false },
1301        op: Operator::LessThan,
1302        value: Value::Uint(0),
1303        message: "unsigned less".to_string(),
1304        children: vec![],
1305        level: 0,
1306        strength_modifier: None,
1307    };
1308    assert!(
1309        evaluate_single_rule(&unsigned_rule, buffer)
1310            .unwrap()
1311            .is_none()
1312    );
1313}
1314
1315#[test]
1316fn test_evaluate_comparison_operators_negative_cases() {
1317    let buffer = &[0x42]; // 66
1318
1319    let cases: Vec<(Operator, u64, bool)> = vec![
1320        // LessThan: 66 < 66 = false, 66 < 67 = true
1321        (Operator::LessThan, 66, false),
1322        (Operator::LessThan, 67, true),
1323        // GreaterThan: 66 > 66 = false, 66 > 65 = true
1324        (Operator::GreaterThan, 66, false),
1325        (Operator::GreaterThan, 65, true),
1326        // LessEqual: 66 <= 65 = false, 66 <= 66 = true
1327        (Operator::LessEqual, 65, false),
1328        (Operator::LessEqual, 66, true),
1329        // GreaterEqual: 66 >= 67 = false, 66 >= 66 = true
1330        (Operator::GreaterEqual, 67, false),
1331        (Operator::GreaterEqual, 66, true),
1332    ];
1333
1334    for (op, value, expected) in cases {
1335        let rule = MagicRule {
1336            offset: OffsetSpec::Absolute(0),
1337            typ: TypeKind::Byte { signed: false },
1338            op: op.clone(),
1339            value: Value::Uint(value),
1340            message: "test".to_string(),
1341            children: vec![],
1342            level: 0,
1343            strength_modifier: None,
1344        };
1345        let result = evaluate_single_rule(&rule, buffer).unwrap();
1346        assert_eq!(
1347            result.is_some(),
1348            expected,
1349            "{op:?} with value {value}: expected {expected}"
1350        );
1351    }
1352}
1353
1354#[test]
1355fn test_evaluate_single_rule_edge_case_values() {
1356    // Test with maximum values
1357    let max_uint_rule = MagicRule {
1358        offset: OffsetSpec::Absolute(0),
1359        typ: TypeKind::Long {
1360            endian: Endianness::Little,
1361            signed: false,
1362        },
1363        op: Operator::Equal,
1364        value: Value::Uint(0xffff_ffff),
1365        message: "Max uint32".to_string(),
1366        children: vec![],
1367        level: 0,
1368        strength_modifier: None,
1369    };
1370
1371    let max_buffer = &[0xff, 0xff, 0xff, 0xff];
1372    let result = evaluate_single_rule(&max_uint_rule, max_buffer).unwrap();
1373    assert!(result.is_some());
1374
1375    // Test with minimum signed value
1376    let min_int_rule = MagicRule {
1377        offset: OffsetSpec::Absolute(0),
1378        typ: TypeKind::Long {
1379            endian: Endianness::Little,
1380            signed: true,
1381        },
1382        op: Operator::Equal,
1383        value: Value::Int(-2_147_483_648), // i32::MIN
1384        message: "Min int32".to_string(),
1385        children: vec![],
1386        level: 0,
1387        strength_modifier: None,
1388    };
1389
1390    let min_buffer = &[0x00, 0x00, 0x00, 0x80]; // 0x80000000 in little-endian
1391    let result = evaluate_single_rule(&min_int_rule, min_buffer).unwrap();
1392    assert!(result.is_some());
1393}
1394
1395#[test]
1396fn test_evaluate_single_rule_various_buffer_sizes() {
1397    // Test with single byte buffer (unsigned for values > 127)
1398    let single_byte_rule = MagicRule {
1399        offset: OffsetSpec::Absolute(0),
1400        typ: TypeKind::Byte { signed: false },
1401        op: Operator::Equal,
1402        value: Value::Uint(0xaa),
1403        message: "Single byte".to_string(),
1404        children: vec![],
1405        level: 0,
1406        strength_modifier: None,
1407    };
1408
1409    let single_buffer = &[0xaa];
1410    let result = evaluate_single_rule(&single_byte_rule, single_buffer).unwrap();
1411    assert!(result.is_some());
1412
1413    // Test with large buffer
1414    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
1415    let large_buffer: Vec<u8> = (0..1024).map(|i| (i % 256) as u8).collect();
1416    let large_rule = MagicRule {
1417        offset: OffsetSpec::Absolute(1000),
1418        typ: TypeKind::Byte { signed: false },
1419        op: Operator::Equal,
1420        value: Value::Uint((1000 % 256) as u64),
1421        message: "Large buffer".to_string(),
1422        children: vec![],
1423        level: 0,
1424        strength_modifier: None,
1425    };
1426
1427    let result = evaluate_single_rule(&large_rule, &large_buffer).unwrap();
1428    assert!(result.is_some());
1429}
1430
1431// Tests for EvaluationContext
1432#[test]
1433fn test_evaluation_context_new() {
1434    let config = EvaluationConfig::default();
1435    let context = EvaluationContext::new(config.clone());
1436
1437    assert_eq!(context.current_offset(), 0);
1438    assert_eq!(context.recursion_depth(), 0);
1439    assert_eq!(
1440        context.config().max_recursion_depth,
1441        config.max_recursion_depth
1442    );
1443    assert_eq!(context.config().max_string_length, config.max_string_length);
1444    assert_eq!(
1445        context.config().stop_at_first_match,
1446        config.stop_at_first_match
1447    );
1448}
1449
1450#[test]
1451fn test_evaluation_context_offset_management() {
1452    let config = EvaluationConfig::default();
1453    let mut context = EvaluationContext::new(config);
1454
1455    // Test initial offset
1456    assert_eq!(context.current_offset(), 0);
1457
1458    // Test setting offset
1459    context.set_current_offset(42);
1460    assert_eq!(context.current_offset(), 42);
1461
1462    // Test setting different offset
1463    context.set_current_offset(1024);
1464    assert_eq!(context.current_offset(), 1024);
1465
1466    // Test setting offset to 0
1467    context.set_current_offset(0);
1468    assert_eq!(context.current_offset(), 0);
1469}
1470
1471#[test]
1472fn test_evaluation_context_recursion_depth_management() {
1473    let config = EvaluationConfig::default();
1474    let mut context = EvaluationContext::new(config);
1475
1476    // Test initial recursion depth
1477    assert_eq!(context.recursion_depth(), 0);
1478
1479    // Test incrementing recursion depth
1480    context.increment_recursion_depth().unwrap();
1481    assert_eq!(context.recursion_depth(), 1);
1482
1483    context.increment_recursion_depth().unwrap();
1484    assert_eq!(context.recursion_depth(), 2);
1485
1486    // Test decrementing recursion depth
1487    context.decrement_recursion_depth().unwrap();
1488    assert_eq!(context.recursion_depth(), 1);
1489
1490    context.decrement_recursion_depth().unwrap();
1491    assert_eq!(context.recursion_depth(), 0);
1492}
1493
1494#[test]
1495fn test_evaluation_context_recursion_depth_limit() {
1496    let config = EvaluationConfig {
1497        max_recursion_depth: 2,
1498        ..Default::default()
1499    };
1500    let mut context = EvaluationContext::new(config);
1501
1502    // Should be able to increment up to the limit
1503    assert!(context.increment_recursion_depth().is_ok());
1504    assert_eq!(context.recursion_depth(), 1);
1505
1506    assert!(context.increment_recursion_depth().is_ok());
1507    assert_eq!(context.recursion_depth(), 2);
1508
1509    // Should fail when exceeding the limit
1510    let result = context.increment_recursion_depth();
1511    assert!(result.is_err());
1512    assert_eq!(context.recursion_depth(), 2); // Should not have changed
1513
1514    match result.unwrap_err() {
1515        LibmagicError::EvaluationError(msg) => {
1516            let error_string = format!("{msg}");
1517            assert!(error_string.contains("Recursion limit exceeded"));
1518        }
1519        _ => panic!("Expected EvaluationError"),
1520    }
1521}
1522
1523#[test]
1524fn test_evaluation_context_recursion_depth_underflow() {
1525    let config = EvaluationConfig::default();
1526    let mut context = EvaluationContext::new(config);
1527
1528    // Should return an error when trying to decrement below 0
1529    let result = context.decrement_recursion_depth();
1530    assert!(result.is_err());
1531
1532    let err = result.unwrap_err();
1533    let err_msg = err.to_string();
1534    assert!(
1535        err_msg.contains("decrement recursion depth below 0"),
1536        "Expected error about decrementing below 0, got: {err_msg}"
1537    );
1538}
1539
1540#[test]
1541fn test_evaluation_context_config_access() {
1542    let config = EvaluationConfig {
1543        max_recursion_depth: 10,
1544        max_string_length: 4096,
1545        stop_at_first_match: false,
1546        enable_mime_types: true,
1547        timeout_ms: Some(2000),
1548    };
1549
1550    let context = EvaluationContext::new(config);
1551
1552    // Test config access
1553    assert_eq!(context.config().max_recursion_depth, 10);
1554    assert_eq!(context.config().max_string_length, 4096);
1555    assert!(!context.config().stop_at_first_match);
1556
1557    // Test convenience methods
1558    assert!(!context.should_stop_at_first_match());
1559    assert_eq!(context.max_string_length(), 4096);
1560}
1561
1562#[test]
1563fn test_evaluation_context_reset() {
1564    let config = EvaluationConfig::default();
1565    let mut context = EvaluationContext::new(config.clone());
1566
1567    // Modify the context state
1568    context.set_current_offset(100);
1569    context.increment_recursion_depth().unwrap();
1570    context.increment_recursion_depth().unwrap();
1571
1572    assert_eq!(context.current_offset(), 100);
1573    assert_eq!(context.recursion_depth(), 2);
1574
1575    // Reset should restore initial state but keep config
1576    context.reset();
1577
1578    assert_eq!(context.current_offset(), 0);
1579    assert_eq!(context.recursion_depth(), 0);
1580    assert_eq!(
1581        context.config().max_recursion_depth,
1582        config.max_recursion_depth
1583    );
1584}
1585
1586#[test]
1587fn test_evaluation_context_clone() {
1588    let config = EvaluationConfig {
1589        max_recursion_depth: 5,
1590        max_string_length: 2048,
1591        ..Default::default()
1592    };
1593
1594    let mut context = EvaluationContext::new(config);
1595    context.set_current_offset(50);
1596    context.increment_recursion_depth().unwrap();
1597
1598    // Clone the context
1599    let cloned_context = context.clone();
1600
1601    // Both should have the same state
1602    assert_eq!(context.current_offset(), cloned_context.current_offset());
1603    assert_eq!(context.recursion_depth(), cloned_context.recursion_depth());
1604    assert_eq!(
1605        context.config().max_recursion_depth,
1606        cloned_context.config().max_recursion_depth
1607    );
1608    assert_eq!(
1609        context.config().max_string_length,
1610        cloned_context.config().max_string_length
1611    );
1612
1613    // Modifying one should not affect the other
1614    context.set_current_offset(75);
1615    assert_eq!(context.current_offset(), 75);
1616    assert_eq!(cloned_context.current_offset(), 50);
1617}
1618
1619#[test]
1620fn test_evaluation_context_with_custom_config() {
1621    let config = EvaluationConfig {
1622        max_recursion_depth: 15,
1623        max_string_length: 16384,
1624        stop_at_first_match: false,
1625        enable_mime_types: true,
1626        timeout_ms: Some(5000),
1627    };
1628
1629    let context = EvaluationContext::new(config);
1630
1631    assert_eq!(context.config().max_recursion_depth, 15);
1632    assert_eq!(context.max_string_length(), 16384);
1633    assert!(!context.should_stop_at_first_match());
1634
1635    // Test that we can increment up to the custom limit
1636    let mut mutable_context = context;
1637    for i in 1..=15 {
1638        assert!(mutable_context.increment_recursion_depth().is_ok());
1639        assert_eq!(mutable_context.recursion_depth(), i);
1640    }
1641
1642    // Should fail on the 16th increment
1643    let result = mutable_context.increment_recursion_depth();
1644    assert!(result.is_err());
1645}
1646
1647#[test]
1648fn test_evaluation_context_mime_types_access() {
1649    let config_with_mime = EvaluationConfig {
1650        enable_mime_types: true,
1651        ..Default::default()
1652    };
1653    let context_with_mime = EvaluationContext::new(config_with_mime);
1654    assert!(context_with_mime.enable_mime_types());
1655
1656    let config_without_mime = EvaluationConfig {
1657        enable_mime_types: false,
1658        ..Default::default()
1659    };
1660    let context_without_mime = EvaluationContext::new(config_without_mime);
1661    assert!(!context_without_mime.enable_mime_types());
1662}
1663
1664#[test]
1665fn test_evaluation_context_timeout_access() {
1666    let config_with_timeout = EvaluationConfig {
1667        timeout_ms: Some(5000),
1668        ..Default::default()
1669    };
1670    let context_with_timeout = EvaluationContext::new(config_with_timeout);
1671    assert_eq!(context_with_timeout.timeout_ms(), Some(5000));
1672
1673    let config_without_timeout = EvaluationConfig {
1674        timeout_ms: None,
1675        ..Default::default()
1676    };
1677    let context_without_timeout = EvaluationContext::new(config_without_timeout);
1678    assert_eq!(context_without_timeout.timeout_ms(), None);
1679}
1680
1681#[test]
1682fn test_evaluation_context_comprehensive_config() {
1683    let config = EvaluationConfig {
1684        max_recursion_depth: 30,
1685        max_string_length: 16384,
1686        stop_at_first_match: false,
1687        enable_mime_types: true,
1688        timeout_ms: Some(10000),
1689    };
1690    let context = EvaluationContext::new(config);
1691
1692    assert_eq!(context.config().max_recursion_depth, 30);
1693    assert_eq!(context.config().max_string_length, 16384);
1694    assert!(!context.should_stop_at_first_match());
1695    assert!(context.enable_mime_types());
1696    assert_eq!(context.timeout_ms(), Some(10000));
1697    assert_eq!(context.max_string_length(), 16384);
1698}
1699
1700#[test]
1701fn test_evaluation_context_performance_config() {
1702    let config = EvaluationConfig {
1703        max_recursion_depth: 5,
1704        max_string_length: 512,
1705        stop_at_first_match: true,
1706        enable_mime_types: false,
1707        timeout_ms: Some(1000),
1708    };
1709    let context = EvaluationContext::new(config);
1710
1711    assert_eq!(context.config().max_recursion_depth, 5);
1712    assert_eq!(context.max_string_length(), 512);
1713    assert!(context.should_stop_at_first_match());
1714    assert!(!context.enable_mime_types());
1715    assert_eq!(context.timeout_ms(), Some(1000));
1716}
1717
1718#[test]
1719fn test_rule_match_creation() {
1720    let match_result = RuleMatch {
1721        message: "ELF executable".to_string(),
1722        offset: 0,
1723        level: 0,
1724        value: Value::Uint(0x7f),
1725        confidence: RuleMatch::calculate_confidence(0),
1726    };
1727
1728    assert_eq!(match_result.message, "ELF executable");
1729    assert_eq!(match_result.offset, 0);
1730    assert_eq!(match_result.level, 0);
1731    assert_eq!(match_result.value, Value::Uint(0x7f));
1732    assert!((match_result.confidence - 0.3).abs() < 0.001);
1733}
1734
1735#[test]
1736fn test_rule_match_clone() {
1737    let original = RuleMatch {
1738        message: "Test message".to_string(),
1739        offset: 42,
1740        level: 1,
1741        value: Value::String("test".to_string()),
1742        confidence: RuleMatch::calculate_confidence(1),
1743    };
1744
1745    let cloned = original.clone();
1746    assert_eq!(original, cloned);
1747}
1748
1749#[test]
1750fn test_rule_match_debug() {
1751    let match_result = RuleMatch {
1752        message: "Debug test".to_string(),
1753        offset: 10,
1754        level: 2,
1755        value: Value::Bytes(vec![0x01, 0x02]),
1756        confidence: RuleMatch::calculate_confidence(2),
1757    };
1758
1759    let debug_str = format!("{match_result:?}");
1760    assert!(debug_str.contains("RuleMatch"));
1761    assert!(debug_str.contains("Debug test"));
1762    assert!(debug_str.contains("10"));
1763    assert!(debug_str.contains('2'));
1764}
1765
1766#[test]
1767fn test_confidence_calculation_depth_0() {
1768    let confidence = RuleMatch::calculate_confidence(0);
1769    assert!((confidence - 0.3).abs() < 0.001);
1770}
1771
1772#[test]
1773fn test_confidence_calculation_depth_1() {
1774    let confidence = RuleMatch::calculate_confidence(1);
1775    assert!((confidence - 0.5).abs() < 0.001);
1776}
1777
1778#[test]
1779fn test_confidence_calculation_depth_2() {
1780    let confidence = RuleMatch::calculate_confidence(2);
1781    assert!((confidence - 0.7).abs() < 0.001);
1782}
1783
1784#[test]
1785fn test_confidence_calculation_depth_3() {
1786    let confidence = RuleMatch::calculate_confidence(3);
1787    assert!((confidence - 0.9).abs() < 0.001);
1788}
1789
1790#[test]
1791fn test_confidence_calculation_capped_at_1() {
1792    // Level 4+ should cap at 1.0
1793    let confidence_4 = RuleMatch::calculate_confidence(4);
1794    assert!((confidence_4 - 1.0).abs() < 0.001);
1795
1796    let confidence_10 = RuleMatch::calculate_confidence(10);
1797    assert!((confidence_10 - 1.0).abs() < 0.001);
1798
1799    let confidence_100 = RuleMatch::calculate_confidence(100);
1800    assert!((confidence_100 - 1.0).abs() < 0.001);
1801}
1802
1803#[test]
1804fn test_evaluate_rules_empty_list() {
1805    let rules = vec![];
1806    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1807    let config = EvaluationConfig::default();
1808    let mut context = EvaluationContext::new(config);
1809
1810    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1811    assert!(matches.is_empty());
1812}
1813
1814#[test]
1815fn test_evaluate_rules_single_matching_rule() {
1816    let rule = MagicRule {
1817        offset: OffsetSpec::Absolute(0),
1818        typ: TypeKind::Byte { signed: true },
1819        op: Operator::Equal,
1820        value: Value::Uint(0x7f),
1821        message: "ELF magic".to_string(),
1822        children: vec![],
1823        level: 0,
1824        strength_modifier: None,
1825    };
1826
1827    let rules = vec![rule];
1828    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1829    let config = EvaluationConfig::default();
1830    let mut context = EvaluationContext::new(config);
1831
1832    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1833    assert_eq!(matches.len(), 1);
1834    assert_eq!(matches[0].message, "ELF magic");
1835    assert_eq!(matches[0].offset, 0);
1836    assert_eq!(matches[0].level, 0);
1837    // Signed byte read: 0x7f -> Value::Int(127)
1838    assert_eq!(matches[0].value, Value::Int(0x7f));
1839}
1840
1841#[test]
1842fn test_evaluate_rules_single_non_matching_rule() {
1843    let rule = MagicRule {
1844        offset: OffsetSpec::Absolute(0),
1845        typ: TypeKind::Byte { signed: true },
1846        op: Operator::Equal,
1847        value: Value::Uint(0x50), // ZIP magic, not ELF
1848        message: "ZIP magic".to_string(),
1849        children: vec![],
1850        level: 0,
1851        strength_modifier: None,
1852    };
1853
1854    let rules = vec![rule];
1855    let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // ELF buffer
1856    let config = EvaluationConfig::default();
1857    let mut context = EvaluationContext::new(config);
1858
1859    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1860    assert!(matches.is_empty());
1861}
1862
1863#[test]
1864fn test_evaluate_rules_multiple_rules_stop_at_first() {
1865    let rule1 = MagicRule {
1866        offset: OffsetSpec::Absolute(0),
1867        typ: TypeKind::Byte { signed: true },
1868        op: Operator::Equal,
1869        value: Value::Uint(0x7f),
1870        message: "First match".to_string(),
1871        children: vec![],
1872        level: 0,
1873        strength_modifier: None,
1874    };
1875
1876    let rule2 = MagicRule {
1877        offset: OffsetSpec::Absolute(1),
1878        typ: TypeKind::Byte { signed: true },
1879        op: Operator::Equal,
1880        value: Value::Uint(0x45),
1881        message: "Second match".to_string(),
1882        children: vec![],
1883        level: 0,
1884        strength_modifier: None,
1885    };
1886
1887    let rule_list = vec![rule1, rule2];
1888    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1889    let config = EvaluationConfig {
1890        stop_at_first_match: true,
1891        ..Default::default()
1892    };
1893    let mut context = EvaluationContext::new(config);
1894
1895    let matches = evaluate_rules(&rule_list, buffer, &mut context).unwrap();
1896    assert_eq!(matches.len(), 1);
1897    assert_eq!(matches[0].message, "First match");
1898}
1899
1900#[test]
1901fn test_evaluate_rules_multiple_rules_find_all() {
1902    let rule1 = MagicRule {
1903        offset: OffsetSpec::Absolute(0),
1904        typ: TypeKind::Byte { signed: true },
1905        op: Operator::Equal,
1906        value: Value::Uint(0x7f),
1907        message: "First match".to_string(),
1908        children: vec![],
1909        level: 0,
1910        strength_modifier: None,
1911    };
1912
1913    let rule2 = MagicRule {
1914        offset: OffsetSpec::Absolute(1),
1915        typ: TypeKind::Byte { signed: true },
1916        op: Operator::Equal,
1917        value: Value::Uint(0x45),
1918        message: "Second match".to_string(),
1919        children: vec![],
1920        level: 0,
1921        strength_modifier: None,
1922    };
1923
1924    let rule_set = vec![rule1, rule2];
1925    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
1926    let config = EvaluationConfig {
1927        stop_at_first_match: false,
1928        ..Default::default()
1929    };
1930    let mut context = EvaluationContext::new(config);
1931
1932    let matches = evaluate_rules(&rule_set, buffer, &mut context).unwrap();
1933    assert_eq!(matches.len(), 2);
1934    assert_eq!(matches[0].message, "First match");
1935    assert_eq!(matches[1].message, "Second match");
1936}
1937
1938#[test]
1939fn test_evaluate_rules_hierarchical_parent_child() {
1940    let child_rule = MagicRule {
1941        offset: OffsetSpec::Absolute(4),
1942        typ: TypeKind::Byte { signed: true },
1943        op: Operator::Equal,
1944        value: Value::Uint(0x02), // ELF class 64-bit
1945        message: "64-bit".to_string(),
1946        children: vec![],
1947        level: 1,
1948        strength_modifier: None,
1949    };
1950
1951    let parent_rule = MagicRule {
1952        offset: OffsetSpec::Absolute(0),
1953        typ: TypeKind::Byte { signed: true },
1954        op: Operator::Equal,
1955        value: Value::Uint(0x7f),
1956        message: "ELF".to_string(),
1957        children: vec![child_rule],
1958        level: 0,
1959        strength_modifier: None,
1960    };
1961
1962    let rules = vec![parent_rule];
1963    let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; // ELF64 header
1964    let config = EvaluationConfig::default();
1965    let mut context = EvaluationContext::new(config);
1966
1967    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
1968    assert_eq!(matches.len(), 2);
1969    assert_eq!(matches[0].message, "ELF");
1970    assert_eq!(matches[0].level, 0);
1971    assert_eq!(matches[1].message, "64-bit");
1972    assert_eq!(matches[1].level, 1);
1973}
1974
1975#[test]
1976fn test_evaluate_rules_hierarchical_parent_no_match() {
1977    let child_rule = MagicRule {
1978        offset: OffsetSpec::Absolute(4),
1979        typ: TypeKind::Byte { signed: true },
1980        op: Operator::Equal,
1981        value: Value::Uint(0x02),
1982        message: "64-bit".to_string(),
1983        children: vec![],
1984        level: 1,
1985        strength_modifier: None,
1986    };
1987
1988    let parent_rule = MagicRule {
1989        offset: OffsetSpec::Absolute(0),
1990        typ: TypeKind::Byte { signed: true },
1991        op: Operator::Equal,
1992        value: Value::Uint(0x50), // ZIP magic, not ELF
1993        message: "ZIP".to_string(),
1994        children: vec![child_rule],
1995        level: 0,
1996        strength_modifier: None,
1997    };
1998
1999    let rules = vec![parent_rule];
2000    let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; // ELF buffer
2001    let config = EvaluationConfig::default();
2002    let mut context = EvaluationContext::new(config);
2003
2004    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2005    assert!(matches.is_empty()); // Parent doesn't match, so children shouldn't be evaluated
2006}
2007
2008#[test]
2009fn test_evaluate_rules_hierarchical_parent_match_child_no_match() {
2010    let child_rule = MagicRule {
2011        offset: OffsetSpec::Absolute(4),
2012        typ: TypeKind::Byte { signed: true },
2013        op: Operator::Equal,
2014        value: Value::Uint(0x01), // ELF class 32-bit, but buffer has 64-bit
2015        message: "32-bit".to_string(),
2016        children: vec![],
2017        level: 1,
2018        strength_modifier: None,
2019    };
2020
2021    let parent_rule = MagicRule {
2022        offset: OffsetSpec::Absolute(0),
2023        typ: TypeKind::Byte { signed: true },
2024        op: Operator::Equal,
2025        value: Value::Uint(0x7f),
2026        message: "ELF".to_string(),
2027        children: vec![child_rule],
2028        level: 0,
2029        strength_modifier: None,
2030    };
2031
2032    let rules = vec![parent_rule];
2033    let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; // ELF64 header
2034    let config = EvaluationConfig::default();
2035    let mut context = EvaluationContext::new(config);
2036
2037    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2038    assert_eq!(matches.len(), 1); // Only parent matches
2039    assert_eq!(matches[0].message, "ELF");
2040    assert_eq!(matches[0].level, 0);
2041}
2042
2043#[test]
2044fn test_evaluate_rules_deep_hierarchy() {
2045    let grandchild_rule = MagicRule {
2046        offset: OffsetSpec::Absolute(5),
2047        typ: TypeKind::Byte { signed: true },
2048        op: Operator::Equal,
2049        value: Value::Uint(0x01), // Little endian
2050        message: "little-endian".to_string(),
2051        children: vec![],
2052        level: 2,
2053        strength_modifier: None,
2054    };
2055
2056    let child_rule = MagicRule {
2057        offset: OffsetSpec::Absolute(4),
2058        typ: TypeKind::Byte { signed: true },
2059        op: Operator::Equal,
2060        value: Value::Uint(0x02), // 64-bit
2061        message: "64-bit".to_string(),
2062        children: vec![grandchild_rule],
2063        level: 1,
2064        strength_modifier: None,
2065    };
2066
2067    let parent_rule = MagicRule {
2068        offset: OffsetSpec::Absolute(0),
2069        typ: TypeKind::Byte { signed: true },
2070        op: Operator::Equal,
2071        value: Value::Uint(0x7f),
2072        message: "ELF".to_string(),
2073        children: vec![child_rule],
2074        level: 0,
2075        strength_modifier: None,
2076    };
2077
2078    let rules = vec![parent_rule];
2079    let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01]; // ELF64 little-endian header
2080    let config = EvaluationConfig::default();
2081    let mut context = EvaluationContext::new(config);
2082
2083    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2084    assert_eq!(matches.len(), 3);
2085    assert_eq!(matches[0].message, "ELF");
2086    assert_eq!(matches[0].level, 0);
2087    assert_eq!(matches[1].message, "64-bit");
2088    assert_eq!(matches[1].level, 1);
2089    assert_eq!(matches[2].message, "little-endian");
2090    assert_eq!(matches[2].level, 2);
2091}
2092
2093#[test]
2094fn test_evaluate_rules_multiple_children() {
2095    let child1 = MagicRule {
2096        offset: OffsetSpec::Absolute(4),
2097        typ: TypeKind::Byte { signed: true },
2098        op: Operator::Equal,
2099        value: Value::Uint(0x02),
2100        message: "64-bit".to_string(),
2101        children: vec![],
2102        level: 1,
2103        strength_modifier: None,
2104    };
2105
2106    let child2 = MagicRule {
2107        offset: OffsetSpec::Absolute(5),
2108        typ: TypeKind::Byte { signed: true },
2109        op: Operator::Equal,
2110        value: Value::Uint(0x01),
2111        message: "little-endian".to_string(),
2112        children: vec![],
2113        level: 1,
2114        strength_modifier: None,
2115    };
2116
2117    let parent_rule = MagicRule {
2118        offset: OffsetSpec::Absolute(0),
2119        typ: TypeKind::Byte { signed: true },
2120        op: Operator::Equal,
2121        value: Value::Uint(0x7f),
2122        message: "ELF".to_string(),
2123        children: vec![child1, child2],
2124        level: 0,
2125        strength_modifier: None,
2126    };
2127
2128    let rules = vec![parent_rule];
2129    let buffer = &[0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01];
2130    let config = EvaluationConfig {
2131        stop_at_first_match: false, // Find all matches
2132        ..Default::default()
2133    };
2134    let mut context = EvaluationContext::new(config);
2135
2136    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2137    assert_eq!(matches.len(), 3);
2138    assert_eq!(matches[0].message, "ELF");
2139    assert_eq!(matches[1].message, "64-bit");
2140    assert_eq!(matches[2].message, "little-endian");
2141}
2142
2143#[test]
2144fn test_evaluate_rules_recursion_depth_limit() {
2145    // Create a deeply nested rule structure that exceeds the limit
2146    let mut current_rule = MagicRule {
2147        offset: OffsetSpec::Absolute(10),
2148        typ: TypeKind::Byte { signed: true },
2149        op: Operator::Equal,
2150        value: Value::Uint(0x00),
2151        message: "Deep level".to_string(),
2152        children: vec![],
2153        level: 10,
2154        strength_modifier: None,
2155    };
2156
2157    // Build a chain of nested rules
2158    for i in (0u32..10u32).rev() {
2159        current_rule = MagicRule {
2160            offset: OffsetSpec::Absolute(i64::from(i)),
2161            typ: TypeKind::Byte { signed: true },
2162            op: Operator::Equal,
2163            value: Value::Uint(u64::from(i)),
2164            message: format!("Level {i}"),
2165            children: vec![current_rule],
2166            level: i,
2167            strength_modifier: None,
2168        };
2169    }
2170
2171    let rules = vec![current_rule];
2172    let buffer = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; // Matches all levels
2173    let config = EvaluationConfig {
2174        max_recursion_depth: 5, // Limit to 5 levels
2175        ..Default::default()
2176    };
2177    let mut context = EvaluationContext::new(config);
2178
2179    let result = evaluate_rules(&rules, buffer, &mut context);
2180    assert!(result.is_err());
2181
2182    match result.unwrap_err() {
2183        LibmagicError::EvaluationError(msg) => {
2184            let error_string = format!("{msg}");
2185            assert!(error_string.contains("Recursion limit exceeded"));
2186        }
2187        _ => panic!("Expected EvaluationError for recursion limit"),
2188    }
2189}
2190
2191#[test]
2192fn test_evaluate_rules_with_config_convenience() {
2193    let rule = MagicRule {
2194        offset: OffsetSpec::Absolute(0),
2195        typ: TypeKind::Byte { signed: true },
2196        op: Operator::Equal,
2197        value: Value::Uint(0x7f),
2198        message: "ELF magic".to_string(),
2199        children: vec![],
2200        level: 0,
2201        strength_modifier: None,
2202    };
2203
2204    let rules = vec![rule];
2205    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2206    let config = EvaluationConfig::default();
2207
2208    let matches = evaluate_rules_with_config(&rules, buffer, &config).unwrap();
2209    assert_eq!(matches.len(), 1);
2210    assert_eq!(matches[0].message, "ELF magic");
2211}
2212
2213#[test]
2214fn test_evaluate_rules_timeout() {
2215    let rule = MagicRule {
2216        offset: OffsetSpec::Absolute(0),
2217        typ: TypeKind::Byte { signed: true },
2218        op: Operator::Equal,
2219        value: Value::Uint(0x7f),
2220        message: "ELF magic".to_string(),
2221        children: vec![],
2222        level: 0,
2223        strength_modifier: None,
2224    };
2225
2226    let rules = vec![rule];
2227    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2228    let config = EvaluationConfig {
2229        timeout_ms: Some(0), // Immediate timeout
2230        ..Default::default()
2231    };
2232    let mut context = EvaluationContext::new(config);
2233
2234    // Note: This test might be flaky due to timing, but it demonstrates the timeout mechanism
2235    let result = evaluate_rules(&rules, buffer, &mut context);
2236    // The result could be either success (if evaluation is very fast) or timeout
2237    // We just verify that timeout errors are handled correctly when they occur
2238    if let Err(LibmagicError::Timeout { timeout_ms }) = result {
2239        assert_eq!(timeout_ms, 0);
2240    }
2241}
2242
2243#[test]
2244fn test_evaluate_rules_empty_buffer() {
2245    let rule = MagicRule {
2246        offset: OffsetSpec::Absolute(0),
2247        typ: TypeKind::Byte { signed: true },
2248        op: Operator::Equal,
2249        value: Value::Uint(0x7f),
2250        message: "Should not match".to_string(),
2251        children: vec![],
2252        level: 0,
2253        strength_modifier: None,
2254    };
2255
2256    let rules = vec![rule];
2257    let buffer = &[]; // Empty buffer
2258    let config = EvaluationConfig::default();
2259    let mut context = EvaluationContext::new(config);
2260
2261    // With graceful error handling, this should succeed but return no matches
2262    let result = evaluate_rules(&rules, buffer, &mut context);
2263    assert!(result.is_ok());
2264
2265    let matches = result.unwrap();
2266    assert_eq!(matches.len(), 0); // No matches due to buffer overrun being handled gracefully
2267}
2268
2269#[test]
2270fn test_evaluate_rules_mixed_matching_non_matching() {
2271    let rule1 = MagicRule {
2272        offset: OffsetSpec::Absolute(0),
2273        typ: TypeKind::Byte { signed: true },
2274        op: Operator::Equal,
2275        value: Value::Uint(0x7f),
2276        message: "Matches".to_string(),
2277        children: vec![],
2278        level: 0,
2279        strength_modifier: None,
2280    };
2281
2282    let rule2 = MagicRule {
2283        offset: OffsetSpec::Absolute(1),
2284        typ: TypeKind::Byte { signed: true },
2285        op: Operator::Equal,
2286        value: Value::Uint(0x99), // Doesn't match
2287        message: "Doesn't match".to_string(),
2288        children: vec![],
2289        level: 0,
2290        strength_modifier: None,
2291    };
2292
2293    let rule3 = MagicRule {
2294        offset: OffsetSpec::Absolute(2),
2295        typ: TypeKind::Byte { signed: true },
2296        op: Operator::Equal,
2297        value: Value::Uint(0x4c),
2298        message: "Also matches".to_string(),
2299        children: vec![],
2300        level: 0,
2301        strength_modifier: None,
2302    };
2303
2304    let rule_collection = vec![rule1, rule2, rule3];
2305    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2306    let config = EvaluationConfig {
2307        stop_at_first_match: false,
2308        ..Default::default()
2309    };
2310    let mut context = EvaluationContext::new(config);
2311
2312    let matches = evaluate_rules(&rule_collection, buffer, &mut context).unwrap();
2313    assert_eq!(matches.len(), 2);
2314    assert_eq!(matches[0].message, "Matches");
2315    assert_eq!(matches[1].message, "Also matches");
2316}
2317
2318#[test]
2319fn test_evaluate_rules_context_state_preservation() {
2320    let rule = MagicRule {
2321        offset: OffsetSpec::Absolute(0),
2322        typ: TypeKind::Byte { signed: true },
2323        op: Operator::Equal,
2324        value: Value::Uint(0x7f),
2325        message: "ELF magic".to_string(),
2326        children: vec![],
2327        level: 0,
2328        strength_modifier: None,
2329    };
2330
2331    let rules = vec![rule];
2332    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2333    let config = EvaluationConfig::default();
2334    let mut context = EvaluationContext::new(config);
2335
2336    // Set some initial state
2337    context.set_current_offset(100);
2338    let initial_offset = context.current_offset();
2339    let initial_depth = context.recursion_depth();
2340
2341    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2342    assert_eq!(matches.len(), 1);
2343
2344    // Context state should be preserved
2345    assert_eq!(context.current_offset(), initial_offset);
2346    assert_eq!(context.recursion_depth(), initial_depth);
2347}
2348
2349#[test]
2350fn test_evaluation_context_state_management_sequence() {
2351    let config = EvaluationConfig::default();
2352    let mut context = EvaluationContext::new(config);
2353
2354    // Simulate a sequence of evaluation operations
2355    assert_eq!(context.current_offset(), 0);
2356    assert_eq!(context.recursion_depth(), 0);
2357
2358    // Start evaluation at offset 10
2359    context.set_current_offset(10);
2360    assert_eq!(context.current_offset(), 10);
2361
2362    // Enter nested rule evaluation
2363    context.increment_recursion_depth().unwrap();
2364    assert_eq!(context.recursion_depth(), 1);
2365
2366    // Move to different offset during nested evaluation
2367    context.set_current_offset(25);
2368    assert_eq!(context.current_offset(), 25);
2369
2370    // Enter deeper nesting
2371    context.increment_recursion_depth().unwrap();
2372    assert_eq!(context.recursion_depth(), 2);
2373
2374    // Exit nested evaluation
2375    context.decrement_recursion_depth().unwrap();
2376    assert_eq!(context.recursion_depth(), 1);
2377
2378    // Continue evaluation at different offset
2379    context.set_current_offset(50);
2380    assert_eq!(context.current_offset(), 50);
2381
2382    // Exit all nesting
2383    context.decrement_recursion_depth().unwrap();
2384    assert_eq!(context.recursion_depth(), 0);
2385
2386    // Final state check
2387    assert_eq!(context.current_offset(), 50);
2388    assert_eq!(context.recursion_depth(), 0);
2389}
2390#[test]
2391fn test_error_recovery_skip_problematic_rules() {
2392    // Test that evaluation continues when individual rules fail
2393    let rules = vec![
2394        // Valid rule that should match
2395        MagicRule {
2396            offset: OffsetSpec::Absolute(0),
2397            typ: TypeKind::Byte { signed: true },
2398            op: Operator::Equal,
2399            value: Value::Uint(0x7f),
2400            message: "Valid rule".to_string(),
2401            children: vec![],
2402            level: 0,
2403            strength_modifier: None,
2404        },
2405        // Invalid rule with out-of-bounds offset
2406        MagicRule {
2407            offset: OffsetSpec::Absolute(100), // Beyond buffer
2408            typ: TypeKind::Byte { signed: true },
2409            op: Operator::Equal,
2410            value: Value::Uint(0x00),
2411            message: "Invalid rule".to_string(),
2412            children: vec![],
2413            level: 0,
2414            strength_modifier: None,
2415        },
2416        // Another valid rule that should match
2417        MagicRule {
2418            offset: OffsetSpec::Absolute(1),
2419            typ: TypeKind::Byte { signed: true },
2420            op: Operator::Equal,
2421            value: Value::Uint(0x45),
2422            message: "Another valid rule".to_string(),
2423            children: vec![],
2424            level: 0,
2425            strength_modifier: None,
2426        },
2427    ];
2428
2429    let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // ELF magic bytes
2430    let config = EvaluationConfig {
2431        max_recursion_depth: 20,
2432        max_string_length: 8192,
2433        stop_at_first_match: false, // Don't stop at first match
2434        enable_mime_types: false,
2435        timeout_ms: None,
2436    };
2437    let mut context = EvaluationContext::new(config);
2438
2439    // Evaluation should succeed despite the problematic rule
2440    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2441
2442    // Should have 2 matches (skipping the problematic one)
2443    assert_eq!(matches.len(), 2);
2444    assert_eq!(matches[0].message, "Valid rule");
2445    assert_eq!(matches[1].message, "Another valid rule");
2446}
2447
2448#[test]
2449fn test_error_recovery_child_rule_failures() {
2450    // Test that parent evaluation continues when child rules fail
2451    let rules = vec![MagicRule {
2452        offset: OffsetSpec::Absolute(0),
2453        typ: TypeKind::Byte { signed: true },
2454        op: Operator::Equal,
2455        value: Value::Uint(0x7f),
2456        message: "Parent rule".to_string(),
2457        children: vec![
2458            // Valid child rule
2459            MagicRule {
2460                offset: OffsetSpec::Absolute(1),
2461                typ: TypeKind::Byte { signed: true },
2462                op: Operator::Equal,
2463                value: Value::Uint(0x45),
2464                message: "Valid child".to_string(),
2465                children: vec![],
2466                level: 1,
2467                strength_modifier: None,
2468            },
2469            // Invalid child rule
2470            MagicRule {
2471                offset: OffsetSpec::Absolute(100), // Beyond buffer
2472                typ: TypeKind::Byte { signed: true },
2473                op: Operator::Equal,
2474                value: Value::Uint(0x00),
2475                message: "Invalid child".to_string(),
2476                children: vec![],
2477                level: 1,
2478                strength_modifier: None,
2479            },
2480        ],
2481        level: 0,
2482        strength_modifier: None,
2483    }];
2484
2485    let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // ELF magic bytes
2486    let config = EvaluationConfig::default();
2487    let mut context = EvaluationContext::new(config);
2488
2489    // Evaluation should succeed with parent and valid child
2490    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2491
2492    // Should have parent match and valid child match
2493    assert_eq!(matches.len(), 2);
2494    assert_eq!(matches[0].message, "Parent rule");
2495    assert_eq!(matches[1].message, "Valid child");
2496}
2497
2498#[test]
2499fn test_error_recovery_mixed_rule_types() {
2500    // Test error recovery with different types of rule failures
2501    let rules = vec![
2502        // Valid byte rule
2503        MagicRule {
2504            offset: OffsetSpec::Absolute(0),
2505            typ: TypeKind::Byte { signed: true },
2506            op: Operator::Equal,
2507            value: Value::Uint(0x7f),
2508            message: "Valid byte".to_string(),
2509            children: vec![],
2510            level: 0,
2511            strength_modifier: None,
2512        },
2513        // Invalid short rule (insufficient bytes)
2514        MagicRule {
2515            offset: OffsetSpec::Absolute(3), // Only 1 byte left for short
2516            typ: TypeKind::Short {
2517                endian: Endianness::Little,
2518                signed: false,
2519            },
2520            op: Operator::Equal,
2521            value: Value::Uint(0x1234),
2522            message: "Invalid short".to_string(),
2523            children: vec![],
2524            level: 0,
2525            strength_modifier: None,
2526        },
2527        // Valid string rule
2528        MagicRule {
2529            offset: OffsetSpec::Absolute(1),
2530            typ: TypeKind::String {
2531                max_length: Some(3),
2532            },
2533            op: Operator::Equal,
2534            value: Value::String("ELF".to_string()),
2535            message: "Valid string".to_string(),
2536            children: vec![],
2537            level: 0,
2538            strength_modifier: None,
2539        },
2540    ];
2541
2542    let buffer = &[0x7f, b'E', b'L', b'F']; // ELF magic bytes
2543    let config = EvaluationConfig {
2544        max_recursion_depth: 20,
2545        max_string_length: 8192,
2546        stop_at_first_match: false, // Don't stop at first match
2547        enable_mime_types: false,
2548        timeout_ms: None,
2549    };
2550    let mut context = EvaluationContext::new(config);
2551
2552    // Evaluation should succeed with valid rules
2553    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2554
2555    // Should have 2 matches (byte and string, skipping invalid short)
2556    assert_eq!(matches.len(), 2);
2557    assert_eq!(matches[0].message, "Valid byte");
2558    assert_eq!(matches[1].message, "Valid string");
2559}
2560
2561#[test]
2562fn test_error_recovery_all_rules_fail() {
2563    // Test behavior when all rules fail
2564    let rules = vec![
2565        // Out of bounds offset
2566        MagicRule {
2567            offset: OffsetSpec::Absolute(100),
2568            typ: TypeKind::Byte { signed: true },
2569            op: Operator::Equal,
2570            value: Value::Uint(0x00),
2571            message: "Out of bounds".to_string(),
2572            children: vec![],
2573            level: 0,
2574            strength_modifier: None,
2575        },
2576        // Insufficient bytes for type
2577        MagicRule {
2578            offset: OffsetSpec::Absolute(2),
2579            typ: TypeKind::Long {
2580                endian: Endianness::Little,
2581                signed: false,
2582            },
2583            op: Operator::Equal,
2584            value: Value::Uint(0x1234_5678),
2585            message: "Insufficient bytes".to_string(),
2586            children: vec![],
2587            level: 0,
2588            strength_modifier: None,
2589        },
2590    ];
2591
2592    let buffer = &[0x7f, 0x45]; // Short buffer
2593    let config = EvaluationConfig::default();
2594    let mut context = EvaluationContext::new(config);
2595
2596    // Evaluation should succeed but return no matches
2597    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2598    assert_eq!(matches.len(), 0);
2599}
2600
2601#[test]
2602fn test_error_recovery_timeout_propagation() {
2603    // Test that timeout errors are properly propagated (not gracefully handled)
2604    let rules = vec![MagicRule {
2605        offset: OffsetSpec::Absolute(0),
2606        typ: TypeKind::Byte { signed: true },
2607        op: Operator::Equal,
2608        value: Value::Uint(0x7f),
2609        message: "Test rule".to_string(),
2610        children: vec![],
2611        level: 0,
2612        strength_modifier: None,
2613    }];
2614
2615    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2616    let config = EvaluationConfig {
2617        max_recursion_depth: 10,
2618        max_string_length: 1024,
2619        stop_at_first_match: false,
2620        enable_mime_types: false,
2621        timeout_ms: Some(0), // Immediate timeout
2622    };
2623    let mut context = EvaluationContext::new(config);
2624
2625    // The timeout test is inherently flaky due to timing, so we'll just test
2626    // that the timeout configuration is properly set and the function doesn't panic
2627    let result = evaluate_rules(&rules, buffer, &mut context);
2628
2629    // The result should either be success (if evaluation was fast) or timeout error
2630    match result {
2631        Ok(_) | Err(LibmagicError::Timeout { .. }) => {
2632            // Evaluation was fast enough or timeout occurred, both are acceptable
2633        }
2634        Err(e) => {
2635            panic!("Unexpected error type: {e:?}");
2636        }
2637    }
2638}
2639
2640#[test]
2641fn test_error_recovery_recursion_limit_propagation() {
2642    // Test that recursion limit errors are properly propagated
2643    let rules = vec![MagicRule {
2644        offset: OffsetSpec::Absolute(0),
2645        typ: TypeKind::Byte { signed: true },
2646        op: Operator::Equal,
2647        value: Value::Uint(0x7f),
2648        message: "Parent".to_string(),
2649        children: vec![MagicRule {
2650            offset: OffsetSpec::Absolute(1),
2651            typ: TypeKind::Byte { signed: true },
2652            op: Operator::Equal,
2653            value: Value::Uint(0x45),
2654            message: "Child".to_string(),
2655            children: vec![],
2656            level: 1,
2657            strength_modifier: None,
2658        }],
2659        level: 0,
2660        strength_modifier: None,
2661    }];
2662
2663    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2664    let config = EvaluationConfig {
2665        max_recursion_depth: 0, // No recursion allowed
2666        max_string_length: 1024,
2667        stop_at_first_match: false,
2668        enable_mime_types: false,
2669        timeout_ms: None,
2670    };
2671    let mut context = EvaluationContext::new(config);
2672
2673    // Should return recursion limit error when trying to evaluate children
2674    let result = evaluate_rules(&rules, buffer, &mut context);
2675    assert!(result.is_err());
2676
2677    match result.unwrap_err() {
2678        LibmagicError::EvaluationError(crate::error::EvaluationError::RecursionLimitExceeded {
2679            ..
2680        }) => {
2681            // Expected recursion limit error
2682        }
2683        _ => panic!("Expected recursion limit error"),
2684    }
2685}
2686
2687#[test]
2688fn test_error_recovery_preserves_context_state() {
2689    // Test that context state is preserved despite rule failures
2690    let rules = vec![
2691        // Valid rule
2692        MagicRule {
2693            offset: OffsetSpec::Absolute(0),
2694            typ: TypeKind::Byte { signed: true },
2695            op: Operator::Equal,
2696            value: Value::Uint(0x7f),
2697            message: "Valid rule".to_string(),
2698            children: vec![],
2699            level: 0,
2700            strength_modifier: None,
2701        },
2702        // Invalid rule
2703        MagicRule {
2704            offset: OffsetSpec::Absolute(100),
2705            typ: TypeKind::Byte { signed: true },
2706            op: Operator::Equal,
2707            value: Value::Uint(0x00),
2708            message: "Invalid rule".to_string(),
2709            children: vec![],
2710            level: 0,
2711            strength_modifier: None,
2712        },
2713    ];
2714
2715    let buffer = &[0x7f, 0x45, 0x4c, 0x46];
2716    let config = EvaluationConfig::default();
2717    let mut context = EvaluationContext::new(config);
2718
2719    // Set initial context state
2720    context.set_current_offset(42);
2721    let initial_offset = context.current_offset();
2722    let initial_depth = context.recursion_depth();
2723
2724    // Evaluation should succeed
2725    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2726    assert_eq!(matches.len(), 1);
2727
2728    // Context state should be preserved
2729    assert_eq!(context.current_offset(), initial_offset);
2730    assert_eq!(context.recursion_depth(), initial_depth);
2731}
2732#[test]
2733fn test_debug_error_recovery() {
2734    // Simple test to debug error recovery
2735    let rule = MagicRule {
2736        offset: OffsetSpec::Absolute(100), // Beyond buffer
2737        typ: TypeKind::Byte { signed: true },
2738        op: Operator::Equal,
2739        value: Value::Uint(0x00),
2740        message: "Out of bounds rule".to_string(),
2741        children: vec![],
2742        level: 0,
2743        strength_modifier: None,
2744    };
2745
2746    let buffer = &[0x7f, 0x45]; // Short buffer
2747
2748    // Test single rule evaluation - should fail
2749    let single_result = evaluate_single_rule(&rule, buffer);
2750    println!("Single rule result: {single_result:?}");
2751    assert!(single_result.is_err());
2752
2753    // Test rules evaluation - should succeed with no matches
2754    let rules = vec![rule];
2755    let config = EvaluationConfig::default();
2756    let mut context = EvaluationContext::new(config);
2757
2758    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2759    println!("Rules evaluation matches: {}", matches.len());
2760    assert_eq!(matches.len(), 0);
2761}
2762#[test]
2763fn test_debug_mixed_rules() {
2764    let rules = vec![
2765        // Valid rule that should match
2766        MagicRule {
2767            offset: OffsetSpec::Absolute(0),
2768            typ: TypeKind::Byte { signed: true },
2769            op: Operator::Equal,
2770            value: Value::Uint(0x7f),
2771            message: "Valid rule".to_string(),
2772            children: vec![],
2773            level: 0,
2774            strength_modifier: None,
2775        },
2776        // Invalid rule with out-of-bounds offset
2777        MagicRule {
2778            offset: OffsetSpec::Absolute(100), // Beyond buffer
2779            typ: TypeKind::Byte { signed: true },
2780            op: Operator::Equal,
2781            value: Value::Uint(0x00),
2782            message: "Invalid rule".to_string(),
2783            children: vec![],
2784            level: 0,
2785            strength_modifier: None,
2786        },
2787        // Another valid rule that should match
2788        MagicRule {
2789            offset: OffsetSpec::Absolute(1),
2790            typ: TypeKind::Byte { signed: true },
2791            op: Operator::Equal,
2792            value: Value::Uint(0x45),
2793            message: "Another valid rule".to_string(),
2794            children: vec![],
2795            level: 0,
2796            strength_modifier: None,
2797        },
2798    ];
2799
2800    let buffer = &[0x7f, 0x45, 0x4c, 0x46]; // ELF magic bytes
2801
2802    // Test each rule individually
2803    for (i, rule) in rules.iter().enumerate() {
2804        let result = evaluate_single_rule(rule, buffer);
2805        println!("Rule {}: '{}' -> {:?}", i, rule.message, result);
2806    }
2807
2808    // Test rules evaluation
2809    let config = EvaluationConfig::default();
2810    let mut context = EvaluationContext::new(config);
2811
2812    let matches = evaluate_rules(&rules, buffer, &mut context).unwrap();
2813    println!("Total matches: {}", matches.len());
2814    for (i, m) in matches.iter().enumerate() {
2815        println!("Match {}: '{}'", i, m.message);
2816    }
2817}