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