Skip to main content

libmagic_rs/parser/
ast.rs

1// Copyright (c) 2025-2026 the libmagic-rs contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Abstract Syntax Tree definitions for magic rules
5//!
6//! This module contains the core data structures that represent parsed magic rules
7//! and their components, including offset specifications, type kinds, operators, and values.
8
9use serde::{Deserialize, Serialize};
10
11/// Offset specification for locating data in files
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
13pub enum OffsetSpec {
14    /// Absolute offset from file start (or from file end if negative)
15    ///
16    /// Positive values are offsets from the start of the file.
17    /// Negative values are offsets from the end of the file (same as `FromEnd`).
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// use libmagic_rs::parser::ast::OffsetSpec;
23    ///
24    /// let offset = OffsetSpec::Absolute(0x10); // Read at byte 16 from start
25    /// let from_end = OffsetSpec::Absolute(-4); // 4 bytes before end of file
26    /// ```
27    Absolute(i64),
28
29    /// Indirect offset through pointer dereferencing
30    ///
31    /// Reads a pointer value at `base_offset`, interprets it according to `pointer_type`
32    /// and `endian`, then adds `adjustment` to get the final offset.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use libmagic_rs::parser::ast::{OffsetSpec, TypeKind, Endianness};
38    ///
39    /// let indirect = OffsetSpec::Indirect {
40    ///     base_offset: 0x20,
41    ///     pointer_type: TypeKind::Long { endian: Endianness::Little, signed: false },
42    ///     adjustment: 4,
43    ///     endian: Endianness::Little,
44    /// };
45    /// ```
46    Indirect {
47        /// Base offset to read pointer from
48        base_offset: i64,
49        /// Type of pointer value
50        pointer_type: TypeKind,
51        /// Adjustment to add to pointer value
52        adjustment: i64,
53        /// Endianness for pointer reading
54        endian: Endianness,
55    },
56
57    /// Relative offset from previous match position
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// use libmagic_rs::parser::ast::OffsetSpec;
63    ///
64    /// let relative = OffsetSpec::Relative(8); // 8 bytes after previous match
65    /// ```
66    Relative(i64),
67
68    /// Offset from end of file (negative values move towards start)
69    ///
70    /// # Examples
71    ///
72    /// ```
73    /// use libmagic_rs::parser::ast::OffsetSpec;
74    ///
75    /// let from_end = OffsetSpec::FromEnd(-16); // 16 bytes before end of file
76    /// ```
77    FromEnd(i64),
78}
79
80/// Data type specifications for interpreting bytes
81#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
82pub enum TypeKind {
83    /// Single byte
84    Byte {
85        /// Whether value is signed
86        signed: bool,
87    },
88    /// 16-bit integer
89    Short {
90        /// Byte order
91        endian: Endianness,
92        /// Whether value is signed
93        signed: bool,
94    },
95    /// 32-bit integer
96    Long {
97        /// Byte order
98        endian: Endianness,
99        /// Whether value is signed
100        signed: bool,
101    },
102    /// String data
103    String {
104        /// Maximum length to read
105        max_length: Option<usize>,
106    },
107}
108
109/// Comparison and bitwise operators
110#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
111pub enum Operator {
112    /// Equality comparison (`=` or `==`)
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// use libmagic_rs::parser::ast::Operator;
118    ///
119    /// let op = Operator::Equal;
120    /// assert_eq!(op, Operator::Equal);
121    /// ```
122    Equal,
123    /// Inequality comparison (`!=` or `<>`)
124    ///
125    /// # Examples
126    ///
127    /// ```
128    /// use libmagic_rs::parser::ast::Operator;
129    ///
130    /// let op = Operator::NotEqual;
131    /// assert_eq!(op, Operator::NotEqual);
132    /// ```
133    NotEqual,
134    /// Less-than comparison (`<`)
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use libmagic_rs::parser::ast::Operator;
140    ///
141    /// let op = Operator::LessThan;
142    /// assert_eq!(op, Operator::LessThan);
143    /// ```
144    LessThan,
145    /// Greater-than comparison (`>`)
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// use libmagic_rs::parser::ast::Operator;
151    ///
152    /// let op = Operator::GreaterThan;
153    /// assert_eq!(op, Operator::GreaterThan);
154    /// ```
155    GreaterThan,
156    /// Less-than-or-equal comparison (`<=`)
157    ///
158    /// # Examples
159    ///
160    /// ```
161    /// use libmagic_rs::parser::ast::Operator;
162    ///
163    /// let op = Operator::LessEqual;
164    /// assert_eq!(op, Operator::LessEqual);
165    /// ```
166    LessEqual,
167    /// Greater-than-or-equal comparison (`>=`)
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use libmagic_rs::parser::ast::Operator;
173    ///
174    /// let op = Operator::GreaterEqual;
175    /// assert_eq!(op, Operator::GreaterEqual);
176    /// ```
177    GreaterEqual,
178    /// Bitwise AND operation without mask (`&`)
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// use libmagic_rs::parser::ast::Operator;
184    ///
185    /// let op = Operator::BitwiseAnd;
186    /// assert_eq!(op, Operator::BitwiseAnd);
187    /// ```
188    BitwiseAnd,
189    /// Bitwise AND operation with mask value (`&` with a mask operand)
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// use libmagic_rs::parser::ast::Operator;
195    ///
196    /// let op = Operator::BitwiseAndMask(0xFF00);
197    /// assert_eq!(op, Operator::BitwiseAndMask(0xFF00));
198    /// ```
199    BitwiseAndMask(u64),
200}
201
202/// Value types for rule matching
203#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
204pub enum Value {
205    /// Unsigned integer value
206    Uint(u64),
207    /// Signed integer value
208    Int(i64),
209    /// Byte sequence
210    Bytes(Vec<u8>),
211    /// String value
212    String(String),
213}
214
215/// Endianness specification for multi-byte values
216#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
217pub enum Endianness {
218    /// Little-endian byte order (least significant byte first)
219    Little,
220    /// Big-endian byte order (most significant byte first)
221    Big,
222    /// Native system byte order (matches target architecture)
223    Native,
224}
225
226/// Strength modifier for magic rules
227///
228/// Strength modifiers adjust the default strength calculation for a rule.
229/// They are specified using the `!:strength` directive in magic files.
230///
231/// # Examples
232///
233/// ```
234/// use libmagic_rs::parser::ast::StrengthModifier;
235///
236/// let add = StrengthModifier::Add(10);      // !:strength +10
237/// let sub = StrengthModifier::Subtract(5);  // !:strength -5
238/// let mul = StrengthModifier::Multiply(2);  // !:strength *2
239/// let div = StrengthModifier::Divide(2);    // !:strength /2
240/// let set = StrengthModifier::Set(50);      // !:strength =50
241/// ```
242#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
243pub enum StrengthModifier {
244    /// Add to the default strength: `!:strength +N`
245    Add(i32),
246    /// Subtract from the default strength: `!:strength -N`
247    Subtract(i32),
248    /// Multiply the default strength: `!:strength *N`
249    Multiply(i32),
250    /// Divide the default strength: `!:strength /N`
251    Divide(i32),
252    /// Set strength to an absolute value: `!:strength =N` or `!:strength N`
253    Set(i32),
254}
255
256/// Magic rule representation in the AST
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct MagicRule {
259    /// Offset specification for where to read data
260    pub offset: OffsetSpec,
261    /// Type of data to read and interpret
262    pub typ: TypeKind,
263    /// Comparison operator to apply
264    pub op: Operator,
265    /// Expected value for comparison
266    pub value: Value,
267    /// Human-readable message for this rule
268    pub message: String,
269    /// Child rules that are evaluated if this rule matches
270    pub children: Vec<MagicRule>,
271    /// Indentation level for hierarchical rules
272    pub level: u32,
273    /// Optional strength modifier from `!:strength` directive
274    pub strength_modifier: Option<StrengthModifier>,
275}
276
277// TODO: Add validation methods for MagicRule:
278// - validate() method to check rule consistency
279// - Ensure message is not empty and contains valid characters
280// - Validate that value type matches the TypeKind
281// - Check that child rule levels are properly nested
282// - Validate offset specifications are reasonable
283// - Add bounds checking for level depth to prevent stack overflow
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    #[test]
290    fn test_offset_spec_absolute() {
291        let offset = OffsetSpec::Absolute(42);
292        assert_eq!(offset, OffsetSpec::Absolute(42));
293
294        // Test negative offset
295        let negative = OffsetSpec::Absolute(-10);
296        assert_eq!(negative, OffsetSpec::Absolute(-10));
297    }
298
299    #[test]
300    fn test_offset_spec_indirect() {
301        let indirect = OffsetSpec::Indirect {
302            base_offset: 0x20,
303            pointer_type: TypeKind::Long {
304                endian: Endianness::Little,
305                signed: false,
306            },
307            adjustment: 4,
308            endian: Endianness::Little,
309        };
310
311        match indirect {
312            OffsetSpec::Indirect {
313                base_offset,
314                adjustment,
315                ..
316            } => {
317                assert_eq!(base_offset, 0x20);
318                assert_eq!(adjustment, 4);
319            }
320            _ => panic!("Expected Indirect variant"),
321        }
322    }
323
324    #[test]
325    fn test_offset_spec_relative() {
326        let relative = OffsetSpec::Relative(8);
327        assert_eq!(relative, OffsetSpec::Relative(8));
328
329        // Test negative relative offset
330        let negative_relative = OffsetSpec::Relative(-4);
331        assert_eq!(negative_relative, OffsetSpec::Relative(-4));
332    }
333
334    #[test]
335    fn test_offset_spec_from_end() {
336        let from_end = OffsetSpec::FromEnd(-16);
337        assert_eq!(from_end, OffsetSpec::FromEnd(-16));
338
339        // Test positive from_end (though unusual)
340        let positive_from_end = OffsetSpec::FromEnd(8);
341        assert_eq!(positive_from_end, OffsetSpec::FromEnd(8));
342    }
343
344    #[test]
345    fn test_offset_spec_debug() {
346        let offset = OffsetSpec::Absolute(100);
347        let debug_str = format!("{offset:?}");
348        assert!(debug_str.contains("Absolute"));
349        assert!(debug_str.contains("100"));
350    }
351
352    #[test]
353    fn test_offset_spec_clone() {
354        let original = OffsetSpec::Indirect {
355            base_offset: 0x10,
356            pointer_type: TypeKind::Short {
357                endian: Endianness::Big,
358                signed: true,
359            },
360            adjustment: -2,
361            endian: Endianness::Big,
362        };
363
364        let cloned = original.clone();
365        assert_eq!(original, cloned);
366    }
367
368    #[test]
369    fn test_offset_spec_serialization() {
370        let offset = OffsetSpec::Absolute(42);
371
372        // Test JSON serialization
373        let json = serde_json::to_string(&offset).expect("Failed to serialize");
374        let deserialized: OffsetSpec = serde_json::from_str(&json).expect("Failed to deserialize");
375
376        assert_eq!(offset, deserialized);
377    }
378
379    #[test]
380    fn test_offset_spec_indirect_serialization() {
381        let indirect = OffsetSpec::Indirect {
382            base_offset: 0x100,
383            pointer_type: TypeKind::Long {
384                endian: Endianness::Native,
385                signed: false,
386            },
387            adjustment: 12,
388            endian: Endianness::Native,
389        };
390
391        // Test JSON serialization for complex variant
392        let json = serde_json::to_string(&indirect).expect("Failed to serialize");
393        let deserialized: OffsetSpec = serde_json::from_str(&json).expect("Failed to deserialize");
394
395        assert_eq!(indirect, deserialized);
396    }
397
398    #[test]
399    fn test_all_offset_spec_variants() {
400        let variants = [
401            OffsetSpec::Absolute(0),
402            OffsetSpec::Absolute(-100),
403            OffsetSpec::Indirect {
404                base_offset: 0x20,
405                pointer_type: TypeKind::Byte { signed: true },
406                adjustment: 0,
407                endian: Endianness::Little,
408            },
409            OffsetSpec::Relative(50),
410            OffsetSpec::Relative(-25),
411            OffsetSpec::FromEnd(-8),
412            OffsetSpec::FromEnd(4),
413        ];
414
415        // Test that all variants can be created and are distinct
416        for (i, variant) in variants.iter().enumerate() {
417            for (j, other) in variants.iter().enumerate() {
418                if i != j {
419                    assert_ne!(
420                        variant, other,
421                        "Variants at indices {i} and {j} should be different"
422                    );
423                }
424            }
425        }
426    }
427
428    #[test]
429    fn test_endianness_variants() {
430        let endianness_values = vec![Endianness::Little, Endianness::Big, Endianness::Native];
431
432        for endian in endianness_values {
433            let indirect = OffsetSpec::Indirect {
434                base_offset: 0,
435                pointer_type: TypeKind::Long {
436                    endian,
437                    signed: false,
438                },
439                adjustment: 0,
440                endian,
441            };
442
443            // Verify the endianness is preserved
444            match indirect {
445                OffsetSpec::Indirect {
446                    endian: actual_endian,
447                    ..
448                } => {
449                    assert_eq!(endian, actual_endian);
450                }
451                _ => panic!("Expected Indirect variant"),
452            }
453        }
454    }
455
456    // Value enum tests
457    #[test]
458    fn test_value_uint() {
459        let value = Value::Uint(42);
460        assert_eq!(value, Value::Uint(42));
461
462        // Test large values
463        let large_value = Value::Uint(u64::MAX);
464        assert_eq!(large_value, Value::Uint(u64::MAX));
465    }
466
467    #[test]
468    fn test_value_int() {
469        let positive = Value::Int(100);
470        assert_eq!(positive, Value::Int(100));
471
472        let negative = Value::Int(-50);
473        assert_eq!(negative, Value::Int(-50));
474
475        // Test extreme values
476        let max_int = Value::Int(i64::MAX);
477        let min_int = Value::Int(i64::MIN);
478        assert_eq!(max_int, Value::Int(i64::MAX));
479        assert_eq!(min_int, Value::Int(i64::MIN));
480    }
481
482    #[test]
483    fn test_value_bytes() {
484        let empty_bytes = Value::Bytes(vec![]);
485        assert_eq!(empty_bytes, Value::Bytes(vec![]));
486
487        let some_bytes = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
488        assert_eq!(some_bytes, Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]));
489
490        // Test that different byte sequences are not equal
491        let other_bytes = Value::Bytes(vec![0x50, 0x4b, 0x03, 0x04]);
492        assert_ne!(some_bytes, other_bytes);
493    }
494
495    #[test]
496    fn test_value_string() {
497        let empty_string = Value::String(String::new());
498        assert_eq!(empty_string, Value::String(String::new()));
499
500        let hello = Value::String("Hello, World!".to_string());
501        assert_eq!(hello, Value::String("Hello, World!".to_string()));
502
503        // Test Unicode strings
504        let unicode = Value::String("🦀 Rust".to_string());
505        assert_eq!(unicode, Value::String("🦀 Rust".to_string()));
506    }
507
508    #[test]
509    fn test_value_comparison() {
510        // Test that different value types are not equal
511        let uint_val = Value::Uint(42);
512        let int_val = Value::Int(42);
513        let bytes_val = Value::Bytes(vec![42]);
514        let string_val = Value::String("42".to_string());
515
516        assert_ne!(uint_val, int_val);
517        assert_ne!(uint_val, bytes_val);
518        assert_ne!(uint_val, string_val);
519        assert_ne!(int_val, bytes_val);
520        assert_ne!(int_val, string_val);
521        assert_ne!(bytes_val, string_val);
522    }
523
524    #[test]
525    fn test_value_debug() {
526        let uint_val = Value::Uint(123);
527        let debug_str = format!("{uint_val:?}");
528        assert!(debug_str.contains("Uint"));
529        assert!(debug_str.contains("123"));
530
531        let string_val = Value::String("test".to_string());
532        let debug_str = format!("{string_val:?}");
533        assert!(debug_str.contains("String"));
534        assert!(debug_str.contains("test"));
535    }
536
537    #[test]
538    fn test_value_clone() {
539        let original = Value::Bytes(vec![1, 2, 3, 4]);
540        let cloned = original.clone();
541        assert_eq!(original, cloned);
542
543        // Verify they are independent copies
544        match (original, cloned) {
545            (Value::Bytes(orig_bytes), Value::Bytes(cloned_bytes)) => {
546                assert_eq!(orig_bytes, cloned_bytes);
547                // They should have the same content but be different Vec instances
548            }
549            _ => panic!("Expected Bytes variants"),
550        }
551    }
552
553    #[test]
554    fn test_value_serialization() {
555        let values = vec![
556            Value::Uint(42),
557            Value::Int(-100),
558            Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]),
559            Value::String("ELF executable".to_string()),
560        ];
561
562        for value in values {
563            // Test JSON serialization
564            let json = serde_json::to_string(&value).expect("Failed to serialize Value");
565            let deserialized: Value =
566                serde_json::from_str(&json).expect("Failed to deserialize Value");
567            assert_eq!(value, deserialized);
568        }
569    }
570
571    #[test]
572    fn test_value_serialization_edge_cases() {
573        // Test empty collections
574        let empty_bytes = Value::Bytes(vec![]);
575        let json = serde_json::to_string(&empty_bytes).expect("Failed to serialize empty bytes");
576        let deserialized: Value =
577            serde_json::from_str(&json).expect("Failed to deserialize empty bytes");
578        assert_eq!(empty_bytes, deserialized);
579
580        let empty_string = Value::String(String::new());
581        let json = serde_json::to_string(&empty_string).expect("Failed to serialize empty string");
582        let deserialized: Value =
583            serde_json::from_str(&json).expect("Failed to deserialize empty string");
584        assert_eq!(empty_string, deserialized);
585
586        // Test extreme values
587        let max_uint = Value::Uint(u64::MAX);
588        let json = serde_json::to_string(&max_uint).expect("Failed to serialize max uint");
589        let deserialized: Value =
590            serde_json::from_str(&json).expect("Failed to deserialize max uint");
591        assert_eq!(max_uint, deserialized);
592
593        let min_int = Value::Int(i64::MIN);
594        let json = serde_json::to_string(&min_int).expect("Failed to serialize min int");
595        let deserialized: Value =
596            serde_json::from_str(&json).expect("Failed to deserialize min int");
597        assert_eq!(min_int, deserialized);
598    }
599
600    // TypeKind tests
601    #[test]
602    fn test_type_kind_byte() {
603        let byte_type = TypeKind::Byte { signed: true };
604        assert_eq!(byte_type, TypeKind::Byte { signed: true });
605    }
606
607    #[test]
608    fn test_type_kind_short() {
609        let short_little_endian = TypeKind::Short {
610            endian: Endianness::Little,
611            signed: false,
612        };
613        let short_big_endian = TypeKind::Short {
614            endian: Endianness::Big,
615            signed: true,
616        };
617
618        assert_ne!(short_little_endian, short_big_endian);
619        assert_eq!(short_little_endian, short_little_endian.clone());
620    }
621
622    #[test]
623    fn test_type_kind_long() {
624        let long_native = TypeKind::Long {
625            endian: Endianness::Native,
626            signed: true,
627        };
628
629        match long_native {
630            TypeKind::Long { endian, signed } => {
631                assert_eq!(endian, Endianness::Native);
632                assert!(signed);
633            }
634            _ => panic!("Expected Long variant"),
635        }
636    }
637
638    #[test]
639    fn test_type_kind_string() {
640        let unlimited_string = TypeKind::String { max_length: None };
641        let limited_string = TypeKind::String {
642            max_length: Some(256),
643        };
644
645        assert_ne!(unlimited_string, limited_string);
646        assert_eq!(unlimited_string, unlimited_string.clone());
647    }
648
649    #[test]
650    fn test_type_kind_serialization() {
651        let types = vec![
652            TypeKind::Byte { signed: true },
653            TypeKind::Short {
654                endian: Endianness::Little,
655                signed: false,
656            },
657            TypeKind::Long {
658                endian: Endianness::Big,
659                signed: true,
660            },
661            TypeKind::String { max_length: None },
662            TypeKind::String {
663                max_length: Some(128),
664            },
665        ];
666
667        for typ in types {
668            let json = serde_json::to_string(&typ).expect("Failed to serialize TypeKind");
669            let deserialized: TypeKind =
670                serde_json::from_str(&json).expect("Failed to deserialize TypeKind");
671            assert_eq!(typ, deserialized);
672        }
673    }
674
675    // Operator tests
676    #[test]
677    fn test_operator_variants() {
678        let operators = [Operator::Equal, Operator::NotEqual, Operator::BitwiseAnd];
679
680        for (i, op) in operators.iter().enumerate() {
681            for (j, other) in operators.iter().enumerate() {
682                if i == j {
683                    assert_eq!(op, other);
684                } else {
685                    assert_ne!(op, other);
686                }
687            }
688        }
689    }
690
691    #[test]
692    fn test_operator_serialization() {
693        let operators = vec![Operator::Equal, Operator::NotEqual, Operator::BitwiseAnd];
694
695        for op in operators {
696            let json = serde_json::to_string(&op).expect("Failed to serialize Operator");
697            let deserialized: Operator =
698                serde_json::from_str(&json).expect("Failed to deserialize Operator");
699            assert_eq!(op, deserialized);
700        }
701    }
702
703    // MagicRule tests
704    #[test]
705    fn test_magic_rule_creation() {
706        let rule = MagicRule {
707            offset: OffsetSpec::Absolute(0),
708            typ: TypeKind::Byte { signed: true },
709            op: Operator::Equal,
710            value: Value::Uint(0x7f),
711            message: "ELF magic".to_string(),
712            children: vec![],
713            level: 0,
714            strength_modifier: None,
715        };
716
717        assert_eq!(rule.message, "ELF magic");
718        assert_eq!(rule.level, 0);
719        assert!(rule.children.is_empty());
720    }
721
722    #[test]
723    fn test_magic_rule_with_children() {
724        let child_rule = MagicRule {
725            offset: OffsetSpec::Absolute(4),
726            typ: TypeKind::Byte { signed: true },
727            op: Operator::Equal,
728            value: Value::Uint(1),
729            message: "32-bit".to_string(),
730            children: vec![],
731            level: 1,
732            strength_modifier: None,
733        };
734
735        let parent_rule = MagicRule {
736            offset: OffsetSpec::Absolute(0),
737            typ: TypeKind::Long {
738                endian: Endianness::Little,
739                signed: false,
740            },
741            op: Operator::Equal,
742            value: Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]),
743            message: "ELF executable".to_string(),
744            children: vec![child_rule],
745            level: 0,
746            strength_modifier: None,
747        };
748
749        assert_eq!(parent_rule.children.len(), 1);
750        assert_eq!(parent_rule.children[0].level, 1);
751        assert_eq!(parent_rule.children[0].message, "32-bit");
752    }
753
754    #[test]
755    fn test_magic_rule_serialization() {
756        let rule = MagicRule {
757            offset: OffsetSpec::Absolute(16),
758            typ: TypeKind::Short {
759                endian: Endianness::Little,
760                signed: false,
761            },
762            op: Operator::NotEqual,
763            value: Value::Uint(0),
764            message: "Non-zero short value".to_string(),
765            children: vec![],
766            level: 2,
767            strength_modifier: None,
768        };
769
770        let json = serde_json::to_string(&rule).expect("Failed to serialize MagicRule");
771        let deserialized: MagicRule =
772            serde_json::from_str(&json).expect("Failed to deserialize MagicRule");
773
774        assert_eq!(rule.message, deserialized.message);
775        assert_eq!(rule.level, deserialized.level);
776        assert_eq!(rule.children.len(), deserialized.children.len());
777    }
778
779    // StrengthModifier tests
780    #[test]
781    fn test_strength_modifier_variants() {
782        let add = StrengthModifier::Add(10);
783        let sub = StrengthModifier::Subtract(5);
784        let mul = StrengthModifier::Multiply(2);
785        let div = StrengthModifier::Divide(2);
786        let set = StrengthModifier::Set(50);
787
788        // Test that each variant has the correct inner value
789        assert_eq!(add, StrengthModifier::Add(10));
790        assert_eq!(sub, StrengthModifier::Subtract(5));
791        assert_eq!(mul, StrengthModifier::Multiply(2));
792        assert_eq!(div, StrengthModifier::Divide(2));
793        assert_eq!(set, StrengthModifier::Set(50));
794
795        // Test that different variants are not equal
796        assert_ne!(add, sub);
797        assert_ne!(mul, div);
798        assert_ne!(set, add);
799    }
800
801    #[test]
802    fn test_strength_modifier_negative_values() {
803        let add_negative = StrengthModifier::Add(-10);
804        let sub_negative = StrengthModifier::Subtract(-5);
805        let set_negative = StrengthModifier::Set(-50);
806
807        assert_eq!(add_negative, StrengthModifier::Add(-10));
808        assert_eq!(sub_negative, StrengthModifier::Subtract(-5));
809        assert_eq!(set_negative, StrengthModifier::Set(-50));
810    }
811
812    #[test]
813    fn test_strength_modifier_serialization() {
814        let modifiers = vec![
815            StrengthModifier::Add(10),
816            StrengthModifier::Subtract(5),
817            StrengthModifier::Multiply(2),
818            StrengthModifier::Divide(3),
819            StrengthModifier::Set(100),
820        ];
821
822        for modifier in modifiers {
823            let json =
824                serde_json::to_string(&modifier).expect("Failed to serialize StrengthModifier");
825            let deserialized: StrengthModifier =
826                serde_json::from_str(&json).expect("Failed to deserialize StrengthModifier");
827            assert_eq!(modifier, deserialized);
828        }
829    }
830
831    #[test]
832    fn test_strength_modifier_debug() {
833        let modifier = StrengthModifier::Add(25);
834        let debug_str = format!("{modifier:?}");
835        assert!(debug_str.contains("Add"));
836        assert!(debug_str.contains("25"));
837    }
838
839    #[test]
840    fn test_strength_modifier_clone() {
841        let original = StrengthModifier::Multiply(4);
842        let cloned = original;
843        assert_eq!(original, cloned);
844    }
845
846    #[test]
847    fn test_magic_rule_with_strength_modifier() {
848        let rule = MagicRule {
849            offset: OffsetSpec::Absolute(0),
850            typ: TypeKind::Byte { signed: true },
851            op: Operator::Equal,
852            value: Value::Uint(0x7f),
853            message: "ELF magic".to_string(),
854            children: vec![],
855            level: 0,
856            strength_modifier: Some(StrengthModifier::Add(20)),
857        };
858
859        assert_eq!(rule.strength_modifier, Some(StrengthModifier::Add(20)));
860
861        // Test serialization with strength_modifier
862        let json = serde_json::to_string(&rule).expect("Failed to serialize MagicRule");
863        let deserialized: MagicRule =
864            serde_json::from_str(&json).expect("Failed to deserialize MagicRule");
865        assert_eq!(rule.strength_modifier, deserialized.strength_modifier);
866    }
867
868    #[test]
869    fn test_magic_rule_without_strength_modifier() {
870        let rule = MagicRule {
871            offset: OffsetSpec::Absolute(0),
872            typ: TypeKind::Byte { signed: true },
873            op: Operator::Equal,
874            value: Value::Uint(0x7f),
875            message: "ELF magic".to_string(),
876            children: vec![],
877            level: 0,
878            strength_modifier: None,
879        };
880
881        assert_eq!(rule.strength_modifier, None);
882    }
883}