Skip to main content

libmagic_rs/evaluator/operators/
mod.rs

1// Copyright (c) 2025-2026 the libmagic-rs contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Operator application for magic rule evaluation
5//!
6//! This module provides functions for applying comparison and bitwise operators
7//! to values during magic rule evaluation. It handles type-safe comparisons
8//! between different Value variants. Supports XOR (`^`), NOT (`~`), and
9//! any-value (`x`) matching in addition to equality, comparison, and bitwise AND.
10
11mod bitwise;
12mod comparison;
13mod equality;
14
15pub use bitwise::{
16    apply_bitwise_and, apply_bitwise_and_mask, apply_bitwise_not, apply_bitwise_not_with_width,
17    apply_bitwise_xor,
18};
19pub use comparison::{
20    apply_greater_equal, apply_greater_than, apply_less_equal, apply_less_than, compare_values,
21};
22pub use equality::{apply_equal, apply_not_equal};
23
24use crate::error::EvaluationError;
25use crate::parser::ast::{Operator, Value, ValueTransform, ValueTransformOp};
26
27/// Apply a magic-file pre-comparison `ValueTransform` to a numeric value
28/// read from the file. The result is what the rule's comparison operator
29/// sees, and what printf-style format specifiers (`%d`, `%x`, ...) render
30/// into the message.
31///
32/// Magic file usage examples:
33/// - `lelong+1 x volume %d` -- read a long, add 1, format as `%d`
34/// - `ulequad/1073741824 x size %lluGB` -- read a quad, divide by 1 GiB
35///
36/// # Numeric promotion
37///
38/// Both `Value::Uint` and `Value::Int` operands are supported. The
39/// transform is computed in the value's existing type:
40/// - `Uint` op `i64` -> the `i64` operand is reinterpreted bitwise as
41///   `u64` for `Mul`/`Or`/`Xor` and as `i64` for the others (matching
42///   libmagic's `apprentice.c::mconvert`, which treats the operand as a
43///   raw machine word for bitwise ops and a signed integer for
44///   arithmetic). `Sub` on a `Uint` is rejected if it would underflow;
45///   `Add` clamps a negative operand to subtraction.
46/// - `Int` uses signed arithmetic throughout.
47///
48/// `Float`, `String`, and `Bytes` values are left unchanged because
49/// magic-file arithmetic transforms are only meaningful on integer
50/// reads. Returning the value unchanged keeps the comparison flow
51/// well-defined while preserving the GOTCHAS S2.3 catch-all discipline
52/// for unknown `Value` variants.
53///
54/// # Errors
55///
56/// Returns `EvaluationError::InvalidValueTransform` when:
57/// - `Div` or `Mod` is applied with a zero operand;
58/// - any arithmetic op overflows the natural integer range.
59pub fn apply_value_transform(
60    value: &Value,
61    transform: ValueTransform,
62) -> Result<Value, EvaluationError> {
63    let signed_operand = transform.operand;
64    // For bitwise ops on a `Uint`, reinterpret the i64 operand bit-for-bit
65    // as u64. For arithmetic ops, use unsigned_abs / sign-aware
66    // checked_{add,sub}.
67    #[allow(clippy::cast_sign_loss)]
68    let bitwise_operand = signed_operand as u64;
69
70    match (value, transform.op) {
71        (Value::Uint(v), ValueTransformOp::Add) => {
72            let lhs = *v;
73            // Allow negative operand to mean subtraction (consistent with
74            // how `Sub` is encoded as `Add(-N)` on the parser side, but
75            // the parser actually emits an explicit `Sub` op -- this
76            // branch handles AST values constructed programmatically).
77            let result = if signed_operand >= 0 {
78                lhs.checked_add(signed_operand.unsigned_abs())
79            } else {
80                lhs.checked_sub(signed_operand.unsigned_abs())
81            };
82            result
83                .map(Value::Uint)
84                .ok_or_else(|| invalid_transform("Add", value, signed_operand))
85        }
86        (Value::Uint(v), ValueTransformOp::Sub) => {
87            let lhs = *v;
88            // Sub on a Uint with a positive operand subtracts; with a
89            // negative operand it adds. Either way, reject overflow.
90            let result = if signed_operand >= 0 {
91                lhs.checked_sub(signed_operand.unsigned_abs())
92            } else {
93                lhs.checked_add(signed_operand.unsigned_abs())
94            };
95            result
96                .map(Value::Uint)
97                .ok_or_else(|| invalid_transform("Sub", value, signed_operand))
98        }
99        (Value::Uint(v), ValueTransformOp::Mul) => v
100            .checked_mul(bitwise_operand)
101            .map(Value::Uint)
102            .ok_or_else(|| invalid_transform("Mul", value, signed_operand)),
103        (Value::Uint(v), ValueTransformOp::Div) => {
104            if signed_operand == 0 {
105                return Err(invalid_transform("Div", value, signed_operand));
106            }
107            v.checked_div(bitwise_operand)
108                .map(Value::Uint)
109                .ok_or_else(|| invalid_transform("Div", value, signed_operand))
110        }
111        (Value::Uint(v), ValueTransformOp::Mod) => {
112            if signed_operand == 0 {
113                return Err(invalid_transform("Mod", value, signed_operand));
114            }
115            v.checked_rem(bitwise_operand)
116                .map(Value::Uint)
117                .ok_or_else(|| invalid_transform("Mod", value, signed_operand))
118        }
119        (Value::Uint(v), ValueTransformOp::BitAnd) => Ok(Value::Uint(v & bitwise_operand)),
120        (Value::Uint(v), ValueTransformOp::Or) => Ok(Value::Uint(v | bitwise_operand)),
121        (Value::Uint(v), ValueTransformOp::Xor) => Ok(Value::Uint(v ^ bitwise_operand)),
122
123        (Value::Int(v), ValueTransformOp::Add) => v
124            .checked_add(signed_operand)
125            .map(Value::Int)
126            .ok_or_else(|| invalid_transform("Add", value, signed_operand)),
127        (Value::Int(v), ValueTransformOp::Sub) => v
128            .checked_sub(signed_operand)
129            .map(Value::Int)
130            .ok_or_else(|| invalid_transform("Sub", value, signed_operand)),
131        (Value::Int(v), ValueTransformOp::Mul) => v
132            .checked_mul(signed_operand)
133            .map(Value::Int)
134            .ok_or_else(|| invalid_transform("Mul", value, signed_operand)),
135        (Value::Int(v), ValueTransformOp::Div) => {
136            if signed_operand == 0 {
137                return Err(invalid_transform("Div", value, signed_operand));
138            }
139            v.checked_div(signed_operand)
140                .map(Value::Int)
141                .ok_or_else(|| invalid_transform("Div", value, signed_operand))
142        }
143        (Value::Int(v), ValueTransformOp::Mod) => {
144            if signed_operand == 0 {
145                return Err(invalid_transform("Mod", value, signed_operand));
146            }
147            v.checked_rem(signed_operand)
148                .map(Value::Int)
149                .ok_or_else(|| invalid_transform("Mod", value, signed_operand))
150        }
151        (Value::Int(v), ValueTransformOp::BitAnd) => {
152            #[allow(clippy::cast_possible_wrap)]
153            let result = *v & (bitwise_operand as i64);
154            Ok(Value::Int(result))
155        }
156        (Value::Int(v), ValueTransformOp::Or) => {
157            // Bitwise OR on i64 view of the integer.
158            #[allow(clippy::cast_possible_wrap)]
159            let result = *v | (bitwise_operand as i64);
160            Ok(Value::Int(result))
161        }
162        (Value::Int(v), ValueTransformOp::Xor) => {
163            #[allow(clippy::cast_possible_wrap)]
164            let result = *v ^ (bitwise_operand as i64);
165            Ok(Value::Int(result))
166        }
167
168        // Non-numeric values are returned unchanged. Magic-file
169        // arithmetic transforms on a string-valued read are not
170        // well-defined; libmagic-compatible behavior is to skip the
171        // transform rather than fail the whole rule.
172        _ => Ok(value.clone()),
173    }
174}
175
176fn invalid_transform(op: &str, value: &Value, operand: i64) -> EvaluationError {
177    EvaluationError::InvalidValueTransform {
178        reason: format!("{op}({operand}) failed on {value:?} (overflow or div-by-zero)"),
179    }
180}
181
182/// Apply any-value operator: always returns true (unconditional match).
183///
184/// The `x` operator in libmagic matches any value unconditionally. This is used
185/// for rules that should always match at a given offset regardless of the data.
186///
187/// # Arguments
188///
189/// * `_left` - The left-hand side value (ignored)
190/// * `_right` - The right-hand side value (ignored)
191///
192/// # Returns
193///
194/// Always returns `true`.
195///
196/// # Examples
197///
198/// ```
199/// use libmagic_rs::parser::ast::Value;
200/// use libmagic_rs::evaluator::operators::apply_any_value;
201///
202/// // Always returns true regardless of values
203/// assert!(apply_any_value(&Value::Uint(0), &Value::Uint(0)));
204/// assert!(apply_any_value(
205///     &Value::String("anything".to_string()),
206///     &Value::Uint(42),
207/// ));
208/// ```
209#[must_use]
210pub fn apply_any_value(_left: &Value, _right: &Value) -> bool {
211    true
212}
213
214/// Apply operator to two values using the specified operator type
215///
216/// This is the main operator application interface that dispatches to the appropriate
217/// operator function based on the `Operator` enum variant. This function serves as
218/// the primary entry point for operator evaluation in magic rule processing.
219///
220/// # Arguments
221///
222/// * `operator` - The operator to apply (`Equal`, `NotEqual`, `LessThan`,
223///   `GreaterThan`, `LessEqual`, `GreaterEqual`, `BitwiseAnd`, `BitwiseAndMask`,
224///   `BitwiseXor`, `BitwiseNot`, or `AnyValue`)
225/// * `left` - The left-hand side value (typically from file data)
226/// * `right` - The right-hand side value (typically from magic rule)
227///
228/// # Returns
229///
230/// `true` if the operator condition is satisfied, `false` otherwise
231///
232/// # Examples
233///
234/// ```
235/// use libmagic_rs::parser::ast::{Operator, Value};
236/// use libmagic_rs::evaluator::operators::apply_operator;
237///
238/// // Equality comparison
239/// assert!(apply_operator(
240///     &Operator::Equal,
241///     &Value::Uint(42),
242///     &Value::Uint(42)
243/// ));
244///
245/// // Inequality comparison
246/// assert!(apply_operator(
247///     &Operator::NotEqual,
248///     &Value::Uint(42),
249///     &Value::Uint(24)
250/// ));
251///
252/// // Less-than comparison
253/// assert!(apply_operator(
254///     &Operator::LessThan,
255///     &Value::Uint(5),
256///     &Value::Uint(10)
257/// ));
258///
259/// // Greater-than comparison
260/// assert!(apply_operator(
261///     &Operator::GreaterThan,
262///     &Value::Uint(10),
263///     &Value::Uint(5)
264/// ));
265///
266/// // Less-than-or-equal comparison
267/// assert!(apply_operator(
268///     &Operator::LessEqual,
269///     &Value::Uint(10),
270///     &Value::Uint(10)
271/// ));
272///
273/// // Greater-than-or-equal comparison
274/// assert!(apply_operator(
275///     &Operator::GreaterEqual,
276///     &Value::Uint(10),
277///     &Value::Uint(10)
278/// ));
279///
280/// // Bitwise AND operation
281/// assert!(apply_operator(
282///     &Operator::BitwiseAnd,
283///     &Value::Uint(0xFF),
284///     &Value::Uint(0x0F)
285/// ));
286///
287/// // Cross-type integer coercion
288/// assert!(apply_operator(
289///     &Operator::Equal,
290///     &Value::Uint(42),
291///     &Value::Int(42)
292/// ));
293///
294/// // Bitwise XOR, NOT, and any-value
295/// assert!(apply_operator(
296///     &Operator::BitwiseXor,
297///     &Value::Uint(0xFF),
298///     &Value::Uint(0x0F)
299/// ));
300/// assert!(apply_operator(
301///     &Operator::AnyValue,
302///     &Value::Uint(0),
303///     &Value::Uint(0)
304/// ));
305/// ```
306#[must_use]
307pub fn apply_operator(operator: &Operator, left: &Value, right: &Value) -> bool {
308    match operator {
309        Operator::Equal => apply_equal(left, right),
310        Operator::NotEqual => apply_not_equal(left, right),
311        Operator::LessThan => apply_less_than(left, right),
312        Operator::GreaterThan => apply_greater_than(left, right),
313        Operator::LessEqual => apply_less_equal(left, right),
314        Operator::GreaterEqual => apply_greater_equal(left, right),
315        Operator::BitwiseAnd => apply_bitwise_and(left, right),
316        Operator::BitwiseAndMask(mask) => apply_bitwise_and_mask(*mask, left, right),
317        Operator::BitwiseXor => apply_bitwise_xor(left, right),
318        Operator::BitwiseNot => apply_bitwise_not(left, right),
319        Operator::AnyValue => apply_any_value(left, right),
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326
327    // ========================================================================
328    // apply_value_transform tests
329    // ========================================================================
330
331    fn t(op: ValueTransformOp, operand: i64) -> ValueTransform {
332        ValueTransform { op, operand }
333    }
334
335    #[test]
336    fn test_apply_value_transform_uint_arithmetic() {
337        // Add: 100 + 1 = 101 (regression for `lelong+1` in filesystems)
338        assert_eq!(
339            apply_value_transform(&Value::Uint(100), t(ValueTransformOp::Add, 1)).unwrap(),
340            Value::Uint(101)
341        );
342        // Sub: 100 - 25 = 75
343        assert_eq!(
344            apply_value_transform(&Value::Uint(100), t(ValueTransformOp::Sub, 25)).unwrap(),
345            Value::Uint(75)
346        );
347        // Mul: 4 * 8 = 32
348        assert_eq!(
349            apply_value_transform(&Value::Uint(4), t(ValueTransformOp::Mul, 8)).unwrap(),
350            Value::Uint(32)
351        );
352        // Div: 16 GiB / 1 GiB = 16 (regression for `ulequad/1073741824`)
353        let sixteen_gib = 16u64 * 1024 * 1024 * 1024;
354        assert_eq!(
355            apply_value_transform(
356                &Value::Uint(sixteen_gib),
357                t(ValueTransformOp::Div, 1_073_741_824)
358            )
359            .unwrap(),
360            Value::Uint(16)
361        );
362        // Mod: 17 % 5 = 2
363        assert_eq!(
364            apply_value_transform(&Value::Uint(17), t(ValueTransformOp::Mod, 5)).unwrap(),
365            Value::Uint(2)
366        );
367        // Or:  0xF0 | 0x0F = 0xFF
368        assert_eq!(
369            apply_value_transform(&Value::Uint(0xF0), t(ValueTransformOp::Or, 0x0F)).unwrap(),
370            Value::Uint(0xFF)
371        );
372        // Xor: 0xFF ^ 0x0F = 0xF0
373        assert_eq!(
374            apply_value_transform(&Value::Uint(0xFF), t(ValueTransformOp::Xor, 0x0F)).unwrap(),
375            Value::Uint(0xF0)
376        );
377    }
378
379    #[test]
380    fn test_apply_value_transform_div_or_mod_by_zero_errors() {
381        let err = apply_value_transform(&Value::Uint(10), t(ValueTransformOp::Div, 0)).unwrap_err();
382        assert!(err.to_string().contains("Div"));
383        let err = apply_value_transform(&Value::Uint(10), t(ValueTransformOp::Mod, 0)).unwrap_err();
384        assert!(err.to_string().contains("Mod"));
385    }
386
387    #[test]
388    fn test_apply_value_transform_overflow_errors() {
389        let err =
390            apply_value_transform(&Value::Uint(u64::MAX), t(ValueTransformOp::Add, 1)).unwrap_err();
391        assert!(err.to_string().contains("Add"));
392        let err =
393            apply_value_transform(&Value::Uint(u64::MAX), t(ValueTransformOp::Mul, 2)).unwrap_err();
394        assert!(err.to_string().contains("Mul"));
395    }
396
397    /// Regression: errors from `apply_value_transform` must be classified as
398    /// `EvaluationError::InvalidValueTransform` so the engine's graceful-skip
399    /// arm catches them. Previously the helper used `internal_error`, which is
400    /// NOT in the graceful-skip list -- a single rule with `lequad*N` against
401    /// an overflow-prone buffer would abort the entire evaluation instead of
402    /// dropping the rule and continuing.
403    #[test]
404    fn test_apply_value_transform_errors_use_invalid_value_transform_variant() {
405        let err = apply_value_transform(&Value::Uint(10), t(ValueTransformOp::Div, 0)).unwrap_err();
406        assert!(
407            matches!(err, EvaluationError::InvalidValueTransform { .. }),
408            "Div-by-zero must produce InvalidValueTransform, got {err:?}"
409        );
410
411        let err =
412            apply_value_transform(&Value::Uint(u64::MAX), t(ValueTransformOp::Mul, 2)).unwrap_err();
413        assert!(
414            matches!(err, EvaluationError::InvalidValueTransform { .. }),
415            "Overflow must produce InvalidValueTransform, got {err:?}"
416        );
417
418        let err =
419            apply_value_transform(&Value::Int(i64::MIN), t(ValueTransformOp::Sub, 1)).unwrap_err();
420        assert!(
421            matches!(err, EvaluationError::InvalidValueTransform { .. }),
422            "Int underflow must produce InvalidValueTransform, got {err:?}"
423        );
424    }
425
426    #[test]
427    fn test_apply_value_transform_int_arithmetic() {
428        assert_eq!(
429            apply_value_transform(&Value::Int(-5), t(ValueTransformOp::Add, 10)).unwrap(),
430            Value::Int(5)
431        );
432        assert_eq!(
433            apply_value_transform(&Value::Int(-3), t(ValueTransformOp::Mul, -2)).unwrap(),
434            Value::Int(6)
435        );
436    }
437
438    #[test]
439    fn test_apply_value_transform_non_numeric_passthrough() {
440        // String/Bytes/Float values are returned unchanged because
441        // magic-file arithmetic transforms only make sense on integer
442        // reads.
443        let s = Value::String("abc".to_string());
444        assert_eq!(
445            apply_value_transform(&s, t(ValueTransformOp::Add, 1)).unwrap(),
446            s
447        );
448    }
449
450    #[test]
451    fn test_apply_operator_equal() {
452        // Test Equal operator dispatch
453        assert!(apply_operator(
454            &Operator::Equal,
455            &Value::Uint(42),
456            &Value::Uint(42)
457        ));
458        assert!(!apply_operator(
459            &Operator::Equal,
460            &Value::Uint(42),
461            &Value::Uint(24)
462        ));
463
464        // Test with different value types
465        assert!(apply_operator(
466            &Operator::Equal,
467            &Value::String("hello".to_string()),
468            &Value::String("hello".to_string())
469        ));
470        assert!(!apply_operator(
471            &Operator::Equal,
472            &Value::String("hello".to_string()),
473            &Value::String("world".to_string())
474        ));
475
476        // Cross-type integer coercion
477        assert!(apply_operator(
478            &Operator::Equal,
479            &Value::Uint(42),
480            &Value::Int(42)
481        ));
482    }
483
484    #[test]
485    fn test_apply_operator_not_equal() {
486        // Test NotEqual operator dispatch
487        assert!(!apply_operator(
488            &Operator::NotEqual,
489            &Value::Uint(42),
490            &Value::Uint(42)
491        ));
492        assert!(apply_operator(
493            &Operator::NotEqual,
494            &Value::Uint(42),
495            &Value::Uint(24)
496        ));
497
498        // Test with different value types
499        assert!(!apply_operator(
500            &Operator::NotEqual,
501            &Value::String("hello".to_string()),
502            &Value::String("hello".to_string())
503        ));
504        assert!(apply_operator(
505            &Operator::NotEqual,
506            &Value::String("hello".to_string()),
507            &Value::String("world".to_string())
508        ));
509
510        // Cross-type integer coercion: same value, so not-equal is false
511        assert!(!apply_operator(
512            &Operator::NotEqual,
513            &Value::Uint(42),
514            &Value::Int(42)
515        ));
516    }
517
518    #[test]
519    fn test_apply_operator_bitwise_and() {
520        // Test BitwiseAnd operator dispatch
521        assert!(apply_operator(
522            &Operator::BitwiseAnd,
523            &Value::Uint(0xFF),
524            &Value::Uint(0x0F)
525        ));
526        assert!(!apply_operator(
527            &Operator::BitwiseAnd,
528            &Value::Uint(0xF0),
529            &Value::Uint(0x0F)
530        ));
531
532        // Test with signed integers
533        assert!(apply_operator(
534            &Operator::BitwiseAnd,
535            &Value::Int(-1),
536            &Value::Int(1)
537        ));
538        assert!(!apply_operator(
539            &Operator::BitwiseAnd,
540            &Value::Int(-2),
541            &Value::Int(1)
542        ));
543
544        // Test with mixed types
545        assert!(apply_operator(
546            &Operator::BitwiseAnd,
547            &Value::Uint(0xFF),
548            &Value::Int(0x0F)
549        ));
550
551        // Non-integer types should return false
552        assert!(!apply_operator(
553            &Operator::BitwiseAnd,
554            &Value::String("test".to_string()),
555            &Value::Uint(0x01)
556        ));
557    }
558
559    #[test]
560    fn test_apply_operator_all_operators_with_same_values() {
561        let test_cases = vec![
562            // Same values - Equal should be true, NotEqual false, BitwiseAnd depends on value
563            (Value::Uint(42), Value::Uint(42)),
564            (Value::Int(-100), Value::Int(-100)),
565            (
566                Value::String("test".to_string()),
567                Value::String("test".to_string()),
568            ),
569            (Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![1, 2, 3])),
570        ];
571
572        for (left, right) in test_cases {
573            // Equal should always be true for same values
574            assert!(
575                apply_operator(&Operator::Equal, &left, &right),
576                "Equal should be true for same values: {left:?} == {right:?}"
577            );
578
579            // NotEqual should always be false for same values
580            assert!(
581                !apply_operator(&Operator::NotEqual, &left, &right),
582                "NotEqual should be false for same values: {left:?} != {right:?}"
583            );
584
585            // BitwiseAnd behavior depends on the value type and content
586            let bitwise_result = apply_operator(&Operator::BitwiseAnd, &left, &right);
587            match &left {
588                Value::Uint(n) => {
589                    // For unsigned integers, BitwiseAnd should be true if value is non-zero
590                    let expected = *n != 0;
591                    assert_eq!(
592                        bitwise_result, expected,
593                        "BitwiseAnd for Uint({n}) should be {expected}"
594                    );
595                }
596                Value::Int(n) => {
597                    // For signed integers, BitwiseAnd should be true if value is non-zero
598                    let expected = *n != 0;
599                    assert_eq!(
600                        bitwise_result, expected,
601                        "BitwiseAnd for Int({n}) should be {expected}"
602                    );
603                }
604                _ => {
605                    // For non-integers, BitwiseAnd should always be false
606                    assert!(
607                        !bitwise_result,
608                        "BitwiseAnd should be false for non-integer types: {left:?}"
609                    );
610                }
611            }
612        }
613    }
614
615    #[test]
616    fn test_apply_operator_all_operators_with_different_values() {
617        let test_cases = vec![
618            // Different values of same type
619            (Value::Uint(42), Value::Uint(24)),
620            (Value::Int(100), Value::Int(-100)),
621            (
622                Value::String("hello".to_string()),
623                Value::String("world".to_string()),
624            ),
625            (Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![4, 5, 6])),
626            // Different types (non-coercible)
627            (Value::Uint(42), Value::String("42".to_string())),
628            (Value::Int(42), Value::Bytes(vec![42])),
629        ];
630
631        for (left, right) in test_cases {
632            // Equal should always be false for truly different values
633            assert!(
634                !apply_operator(&Operator::Equal, &left, &right),
635                "Equal should be false for different values: {left:?} == {right:?}"
636            );
637
638            // NotEqual should always be true for truly different values
639            assert!(
640                apply_operator(&Operator::NotEqual, &left, &right),
641                "NotEqual should be true for different values: {left:?} != {right:?}"
642            );
643
644            // BitwiseAnd behavior depends on the value types and content
645            let bitwise_result = apply_operator(&Operator::BitwiseAnd, &left, &right);
646            match (&left, &right) {
647                (Value::Uint(a), Value::Uint(b)) => {
648                    let expected = (a & b) != 0;
649                    assert_eq!(
650                        bitwise_result, expected,
651                        "BitwiseAnd for Uint({a}) & Uint({b}) should be {expected}"
652                    );
653                }
654                (Value::Int(a), Value::Int(b)) => {
655                    #[allow(clippy::cast_sign_loss)]
656                    let expected = ((*a as u64) & (*b as u64)) != 0;
657                    assert_eq!(
658                        bitwise_result, expected,
659                        "BitwiseAnd for Int({a}) & Int({b}) should be {expected}"
660                    );
661                }
662                (Value::Uint(a), Value::Int(b)) | (Value::Int(b), Value::Uint(a)) => {
663                    #[allow(clippy::cast_sign_loss)]
664                    let expected = (a & (*b as u64)) != 0;
665                    assert_eq!(
666                        bitwise_result, expected,
667                        "BitwiseAnd for mixed Uint/Int should be {expected}"
668                    );
669                }
670                _ => {
671                    // For non-integer types, BitwiseAnd should always be false
672                    assert!(
673                        !bitwise_result,
674                        "BitwiseAnd should be false for non-integer types: {left:?} & {right:?}"
675                    );
676                }
677            }
678        }
679    }
680
681    #[test]
682    fn test_apply_operator_consistency_with_individual_functions() {
683        let test_cases = vec![
684            (Value::Uint(42), Value::Uint(42)),
685            (Value::Uint(42), Value::Uint(24)),
686            (Value::Int(-100), Value::Int(-100)),
687            (Value::Int(100), Value::Int(-100)),
688            (
689                Value::String("test".to_string()),
690                Value::String("test".to_string()),
691            ),
692            (
693                Value::String("hello".to_string()),
694                Value::String("world".to_string()),
695            ),
696            (Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![1, 2, 3])),
697            (Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![4, 5, 6])),
698            // Cross-type cases
699            (Value::Uint(42), Value::Int(42)),
700            (Value::Uint(42), Value::String("42".to_string())),
701            (Value::Int(42), Value::Bytes(vec![42])),
702        ];
703
704        for (left, right) in test_cases {
705            // Test that apply_operator gives same results as individual functions
706            assert_eq!(
707                apply_operator(&Operator::Equal, &left, &right),
708                apply_equal(&left, &right),
709                "apply_operator(Equal) should match apply_equal for {left:?}, {right:?}"
710            );
711
712            assert_eq!(
713                apply_operator(&Operator::NotEqual, &left, &right),
714                apply_not_equal(&left, &right),
715                "apply_operator(NotEqual) should match apply_not_equal for {left:?}, {right:?}"
716            );
717
718            assert_eq!(
719                apply_operator(&Operator::BitwiseAnd, &left, &right),
720                apply_bitwise_and(&left, &right),
721                "apply_operator(BitwiseAnd) should match apply_bitwise_and for {left:?}, {right:?}"
722            );
723
724            assert_eq!(
725                apply_operator(&Operator::BitwiseXor, &left, &right),
726                apply_bitwise_xor(&left, &right),
727                "apply_operator(BitwiseXor) should match apply_bitwise_xor for {left:?}, {right:?}"
728            );
729
730            assert_eq!(
731                apply_operator(&Operator::BitwiseNot, &left, &right),
732                apply_bitwise_not(&left, &right),
733                "apply_operator(BitwiseNot) should match apply_bitwise_not for {left:?}, {right:?}"
734            );
735
736            assert!(
737                apply_operator(&Operator::AnyValue, &left, &right),
738                "apply_operator(AnyValue) should always be true for {left:?}, {right:?}"
739            );
740        }
741    }
742
743    #[test]
744    fn test_apply_operator_magic_rule_scenarios() {
745        // Test scenarios that would commonly appear in magic rules
746
747        // ELF magic number check
748        let elf_magic = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
749        let elf_expected = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
750        assert!(apply_operator(&Operator::Equal, &elf_magic, &elf_expected));
751        assert!(!apply_operator(
752            &Operator::NotEqual,
753            &elf_magic,
754            &elf_expected
755        ));
756
757        // ZIP magic number check
758        let zip_magic = Value::Uint(0x504B_0304);
759        let zip_expected = Value::Uint(0x504B_0304);
760        assert!(apply_operator(&Operator::Equal, &zip_magic, &zip_expected));
761
762        // Bit flag checking (common in binary formats)
763        let flags = Value::Uint(0b1101_0110);
764        let flag_mask = Value::Uint(0b0000_0010); // Check if bit 1 is set
765        assert!(apply_operator(&Operator::BitwiseAnd, &flags, &flag_mask));
766
767        let no_flag_mask = Value::Uint(0b0000_0001); // Check if bit 0 is set
768        assert!(!apply_operator(
769            &Operator::BitwiseAnd,
770            &flags,
771            &no_flag_mask
772        ));
773
774        // String matching for text-based formats
775        let content = Value::String("#!/bin/bash".to_string());
776        let shebang = Value::String("#!/bin/bash".to_string());
777        assert!(apply_operator(&Operator::Equal, &content, &shebang));
778
779        let not_shebang = Value::String("#!/usr/bin/python".to_string());
780        assert!(apply_operator(&Operator::NotEqual, &content, &not_shebang));
781
782        // Version number checking
783        let version = Value::Uint(2);
784        let expected_version = Value::Uint(2);
785        let old_version = Value::Uint(1);
786        assert!(apply_operator(
787            &Operator::Equal,
788            &version,
789            &expected_version
790        ));
791        assert!(apply_operator(&Operator::NotEqual, &version, &old_version));
792    }
793
794    #[test]
795    fn test_apply_operator_edge_cases() {
796        // Test with extreme values
797        let max_uint = Value::Uint(u64::MAX);
798        let min_signed = Value::Int(i64::MIN);
799        let max_signed = Value::Int(i64::MAX);
800
801        // Self-comparison should work
802        assert!(apply_operator(&Operator::Equal, &max_uint, &max_uint));
803        assert!(apply_operator(&Operator::Equal, &min_signed, &min_signed));
804        assert!(apply_operator(&Operator::Equal, &max_signed, &max_signed));
805
806        // Cross-extreme comparisons
807        assert!(apply_operator(&Operator::NotEqual, &max_uint, &min_signed));
808        assert!(apply_operator(
809            &Operator::NotEqual,
810            &max_signed,
811            &min_signed
812        ));
813
814        // Bitwise operations with extreme values
815        assert!(apply_operator(
816            &Operator::BitwiseAnd,
817            &max_uint,
818            &Value::Uint(1)
819        ));
820        assert!(apply_operator(
821            &Operator::BitwiseAnd,
822            &min_signed,
823            &min_signed
824        ));
825
826        // Empty collections
827        let empty_bytes = Value::Bytes(vec![]);
828        let empty_string = Value::String(String::new());
829        assert!(apply_operator(&Operator::Equal, &empty_bytes, &empty_bytes));
830        assert!(apply_operator(
831            &Operator::Equal,
832            &empty_string,
833            &empty_string
834        ));
835        // Cross-type empty Bytes vs empty String now compare equal (libmagic
836        // policy: same byte sequence wins). NotEqual flips accordingly.
837        assert!(apply_operator(
838            &Operator::Equal,
839            &empty_bytes,
840            &empty_string
841        ));
842
843        // Zero values
844        let zero_uint = Value::Uint(0);
845        let zero_signed = Value::Int(0);
846        assert!(!apply_operator(
847            &Operator::BitwiseAnd,
848            &zero_uint,
849            &Value::Uint(0xFF)
850        ));
851        assert!(!apply_operator(
852            &Operator::BitwiseAnd,
853            &zero_signed,
854            &Value::Int(0xFF)
855        ));
856        assert!(!apply_operator(
857            &Operator::NotEqual,
858            &zero_uint,
859            &zero_signed
860        )); // Cross-type integer coercion: 0 == 0
861    }
862
863    #[test]
864    fn test_apply_operator_bitwise_xor() {
865        assert!(apply_operator(
866            &Operator::BitwiseXor,
867            &Value::Uint(0xFF),
868            &Value::Uint(0x0F)
869        ));
870        assert!(!apply_operator(
871            &Operator::BitwiseXor,
872            &Value::Uint(42),
873            &Value::Uint(42)
874        ));
875        assert!(!apply_operator(
876            &Operator::BitwiseXor,
877            &Value::String("x".to_string()),
878            &Value::Uint(1)
879        ));
880    }
881
882    #[test]
883    fn test_apply_operator_bitwise_not() {
884        assert!(apply_operator(
885            &Operator::BitwiseNot,
886            &Value::Uint(0),
887            &Value::Uint(u64::MAX)
888        ));
889        assert!(apply_operator(
890            &Operator::BitwiseNot,
891            &Value::Int(-1),
892            &Value::Int(0)
893        ));
894        assert!(!apply_operator(
895            &Operator::BitwiseNot,
896            &Value::Bytes(vec![0]),
897            &Value::Uint(0xFF)
898        ));
899    }
900
901    #[test]
902    fn test_apply_operator_any_value() {
903        assert!(apply_operator(
904            &Operator::AnyValue,
905            &Value::Uint(0),
906            &Value::Uint(0)
907        ));
908        assert!(apply_operator(
909            &Operator::AnyValue,
910            &Value::Int(42),
911            &Value::Int(0)
912        ));
913        assert!(apply_operator(
914            &Operator::AnyValue,
915            &Value::Bytes(vec![1, 2, 3]),
916            &Value::Bytes(vec![])
917        ));
918        assert!(apply_operator(
919            &Operator::AnyValue,
920            &Value::String("x".to_string()),
921            &Value::String("y".to_string())
922        ));
923        assert!(apply_operator(
924            &Operator::AnyValue,
925            &Value::Uint(1),
926            &Value::String(String::new())
927        ));
928        assert!(apply_operator(
929            &Operator::AnyValue,
930            &Value::Bytes(vec![]),
931            &Value::Bytes(vec![])
932        ));
933    }
934
935    #[test]
936    fn test_apply_operator_all_combinations() {
937        let operators = [
938            Operator::Equal,
939            Operator::NotEqual,
940            Operator::LessThan,
941            Operator::GreaterThan,
942            Operator::LessEqual,
943            Operator::GreaterEqual,
944            Operator::BitwiseAnd,
945            Operator::BitwiseAndMask(0xFF),
946            Operator::BitwiseXor,
947            Operator::BitwiseNot,
948            Operator::AnyValue,
949        ];
950        let values = [
951            Value::Uint(42),
952            Value::Int(-42),
953            Value::Bytes(vec![42]),
954            Value::String("42".to_string()),
955        ];
956
957        // Test all operator-value combinations to ensure no panics
958        for operator in &operators {
959            for left in &values {
960                for right in &values {
961                    // This should not panic for any combination
962                    let result = apply_operator(operator, left, right);
963
964                    // Verify the result is consistent with individual function calls
965                    let expected = match operator {
966                        Operator::Equal => apply_equal(left, right),
967                        Operator::NotEqual => apply_not_equal(left, right),
968                        Operator::LessThan => apply_less_than(left, right),
969                        Operator::GreaterThan => apply_greater_than(left, right),
970                        Operator::LessEqual => apply_less_equal(left, right),
971                        Operator::GreaterEqual => apply_greater_equal(left, right),
972                        Operator::BitwiseAnd => apply_bitwise_and(left, right),
973                        Operator::BitwiseAndMask(mask) => {
974                            apply_bitwise_and_mask(*mask, left, right)
975                        }
976                        Operator::BitwiseXor => apply_bitwise_xor(left, right),
977                        Operator::BitwiseNot => apply_bitwise_not(left, right),
978                        Operator::AnyValue => apply_any_value(left, right),
979                    };
980
981                    assert_eq!(
982                        result, expected,
983                        "apply_operator({operator:?}, {left:?}, {right:?}) should match individual function"
984                    );
985                }
986            }
987        }
988    }
989}