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