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