use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum OffsetSpec {
Absolute(i64),
Indirect {
base_offset: i64,
pointer_type: TypeKind,
adjustment: i64,
endian: Endianness,
},
Relative(i64),
FromEnd(i64),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum TypeKind {
Byte {
signed: bool,
},
Short {
endian: Endianness,
signed: bool,
},
Long {
endian: Endianness,
signed: bool,
},
Quad {
endian: Endianness,
signed: bool,
},
Float {
endian: Endianness,
},
Double {
endian: Endianness,
},
String {
max_length: Option<usize>,
},
}
impl TypeKind {
#[must_use]
pub const fn bit_width(&self) -> Option<u32> {
match self {
Self::Byte { .. } => Some(8),
Self::Short { .. } => Some(16),
Self::Long { .. } | Self::Float { .. } => Some(32),
Self::Quad { .. } | Self::Double { .. } => Some(64),
Self::String { .. } => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum Operator {
Equal,
NotEqual,
LessThan,
GreaterThan,
LessEqual,
GreaterEqual,
BitwiseAnd,
BitwiseAndMask(u64),
BitwiseXor,
BitwiseNot,
AnyValue,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
Uint(u64),
Int(i64),
Float(f64),
Bytes(Vec<u8>),
String(String),
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum Endianness {
Little,
Big,
Native,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum StrengthModifier {
Add(i32),
Subtract(i32),
Multiply(i32),
Divide(i32),
Set(i32),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MagicRule {
pub offset: OffsetSpec,
pub typ: TypeKind,
pub op: Operator,
pub value: Value,
pub message: String,
pub children: Vec<MagicRule>,
pub level: u32,
pub strength_modifier: Option<StrengthModifier>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_offset_spec_absolute() {
let offset = OffsetSpec::Absolute(42);
assert_eq!(offset, OffsetSpec::Absolute(42));
let negative = OffsetSpec::Absolute(-10);
assert_eq!(negative, OffsetSpec::Absolute(-10));
}
#[test]
fn test_offset_spec_indirect() {
let indirect = OffsetSpec::Indirect {
base_offset: 0x20,
pointer_type: TypeKind::Long {
endian: Endianness::Little,
signed: false,
},
adjustment: 4,
endian: Endianness::Little,
};
match indirect {
OffsetSpec::Indirect {
base_offset,
adjustment,
..
} => {
assert_eq!(base_offset, 0x20);
assert_eq!(adjustment, 4);
}
_ => panic!("Expected Indirect variant"),
}
}
#[test]
fn test_offset_spec_relative() {
let relative = OffsetSpec::Relative(8);
assert_eq!(relative, OffsetSpec::Relative(8));
let negative_relative = OffsetSpec::Relative(-4);
assert_eq!(negative_relative, OffsetSpec::Relative(-4));
}
#[test]
fn test_offset_spec_from_end() {
let from_end = OffsetSpec::FromEnd(-16);
assert_eq!(from_end, OffsetSpec::FromEnd(-16));
let positive_from_end = OffsetSpec::FromEnd(8);
assert_eq!(positive_from_end, OffsetSpec::FromEnd(8));
}
#[test]
fn test_offset_spec_debug() {
let offset = OffsetSpec::Absolute(100);
let debug_str = format!("{offset:?}");
assert!(debug_str.contains("Absolute"));
assert!(debug_str.contains("100"));
}
#[test]
fn test_offset_spec_clone() {
let original = OffsetSpec::Indirect {
base_offset: 0x10,
pointer_type: TypeKind::Short {
endian: Endianness::Big,
signed: true,
},
adjustment: -2,
endian: Endianness::Big,
};
let cloned = original.clone();
assert_eq!(original, cloned);
}
#[test]
fn test_offset_spec_serialization() {
let offset = OffsetSpec::Absolute(42);
let json = serde_json::to_string(&offset).expect("Failed to serialize");
let deserialized: OffsetSpec = serde_json::from_str(&json).expect("Failed to deserialize");
assert_eq!(offset, deserialized);
}
#[test]
fn test_offset_spec_indirect_serialization() {
let indirect = OffsetSpec::Indirect {
base_offset: 0x100,
pointer_type: TypeKind::Long {
endian: Endianness::Native,
signed: false,
},
adjustment: 12,
endian: Endianness::Native,
};
let json = serde_json::to_string(&indirect).expect("Failed to serialize");
let deserialized: OffsetSpec = serde_json::from_str(&json).expect("Failed to deserialize");
assert_eq!(indirect, deserialized);
}
#[test]
fn test_all_offset_spec_variants() {
let variants = [
OffsetSpec::Absolute(0),
OffsetSpec::Absolute(-100),
OffsetSpec::Indirect {
base_offset: 0x20,
pointer_type: TypeKind::Byte { signed: true },
adjustment: 0,
endian: Endianness::Little,
},
OffsetSpec::Relative(50),
OffsetSpec::Relative(-25),
OffsetSpec::FromEnd(-8),
OffsetSpec::FromEnd(4),
];
for (i, variant) in variants.iter().enumerate() {
for (j, other) in variants.iter().enumerate() {
if i != j {
assert_ne!(
variant, other,
"Variants at indices {i} and {j} should be different"
);
}
}
}
}
#[test]
fn test_endianness_variants() {
let endianness_values = vec![Endianness::Little, Endianness::Big, Endianness::Native];
for endian in endianness_values {
let indirect = OffsetSpec::Indirect {
base_offset: 0,
pointer_type: TypeKind::Long {
endian,
signed: false,
},
adjustment: 0,
endian,
};
match indirect {
OffsetSpec::Indirect {
endian: actual_endian,
..
} => {
assert_eq!(endian, actual_endian);
}
_ => panic!("Expected Indirect variant"),
}
}
}
#[test]
fn test_value_uint() {
let value = Value::Uint(42);
assert_eq!(value, Value::Uint(42));
let large_value = Value::Uint(u64::MAX);
assert_eq!(large_value, Value::Uint(u64::MAX));
}
#[test]
fn test_value_int() {
let positive = Value::Int(100);
assert_eq!(positive, Value::Int(100));
let negative = Value::Int(-50);
assert_eq!(negative, Value::Int(-50));
let max_int = Value::Int(i64::MAX);
let min_int = Value::Int(i64::MIN);
assert_eq!(max_int, Value::Int(i64::MAX));
assert_eq!(min_int, Value::Int(i64::MIN));
}
#[test]
fn test_value_bytes() {
let empty_bytes = Value::Bytes(vec![]);
assert_eq!(empty_bytes, Value::Bytes(vec![]));
let some_bytes = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
assert_eq!(some_bytes, Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]));
let other_bytes = Value::Bytes(vec![0x50, 0x4b, 0x03, 0x04]);
assert_ne!(some_bytes, other_bytes);
}
#[test]
fn test_value_string() {
let empty_string = Value::String(String::new());
assert_eq!(empty_string, Value::String(String::new()));
let hello = Value::String("Hello, World!".to_string());
assert_eq!(hello, Value::String("Hello, World!".to_string()));
let unicode = Value::String("🦀 Rust".to_string());
assert_eq!(unicode, Value::String("🦀 Rust".to_string()));
}
#[test]
fn test_value_comparison() {
let uint_val = Value::Uint(42);
let int_val = Value::Int(42);
let float_val = Value::Float(42.0);
let bytes_val = Value::Bytes(vec![42]);
let string_val = Value::String("42".to_string());
assert_ne!(uint_val, int_val);
assert_ne!(uint_val, float_val);
assert_ne!(uint_val, bytes_val);
assert_ne!(uint_val, string_val);
assert_ne!(int_val, float_val);
assert_ne!(int_val, bytes_val);
assert_ne!(int_val, string_val);
assert_ne!(float_val, bytes_val);
assert_ne!(float_val, string_val);
assert_ne!(bytes_val, string_val);
}
#[test]
fn test_value_debug() {
let uint_val = Value::Uint(123);
let debug_str = format!("{uint_val:?}");
assert!(debug_str.contains("Uint"));
assert!(debug_str.contains("123"));
let string_val = Value::String("test".to_string());
let debug_str = format!("{string_val:?}");
assert!(debug_str.contains("String"));
assert!(debug_str.contains("test"));
}
#[test]
fn test_value_clone() {
let original = Value::Bytes(vec![1, 2, 3, 4]);
let cloned = original.clone();
assert_eq!(original, cloned);
match (original, cloned) {
(Value::Bytes(orig_bytes), Value::Bytes(cloned_bytes)) => {
assert_eq!(orig_bytes, cloned_bytes);
}
_ => panic!("Expected Bytes variants"),
}
}
#[test]
fn test_value_float() {
let value = Value::Float(3.125);
assert_eq!(value, Value::Float(3.125));
let negative = Value::Float(-1.5);
assert_eq!(negative, Value::Float(-1.5));
let zero = Value::Float(0.0);
assert_eq!(zero, Value::Float(0.0));
}
#[test]
fn test_value_serialization() {
let values = vec![
Value::Uint(42),
Value::Int(-100),
Value::Float(3.125),
Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]),
Value::String("ELF executable".to_string()),
];
for value in values {
let json = serde_json::to_string(&value).expect("Failed to serialize Value");
let deserialized: Value =
serde_json::from_str(&json).expect("Failed to deserialize Value");
assert_eq!(value, deserialized);
}
}
#[test]
fn test_value_serialization_edge_cases() {
let empty_bytes = Value::Bytes(vec![]);
let json = serde_json::to_string(&empty_bytes).expect("Failed to serialize empty bytes");
let deserialized: Value =
serde_json::from_str(&json).expect("Failed to deserialize empty bytes");
assert_eq!(empty_bytes, deserialized);
let empty_string = Value::String(String::new());
let json = serde_json::to_string(&empty_string).expect("Failed to serialize empty string");
let deserialized: Value =
serde_json::from_str(&json).expect("Failed to deserialize empty string");
assert_eq!(empty_string, deserialized);
let max_uint = Value::Uint(u64::MAX);
let json = serde_json::to_string(&max_uint).expect("Failed to serialize max uint");
let deserialized: Value =
serde_json::from_str(&json).expect("Failed to deserialize max uint");
assert_eq!(max_uint, deserialized);
let min_int = Value::Int(i64::MIN);
let json = serde_json::to_string(&min_int).expect("Failed to serialize min int");
let deserialized: Value =
serde_json::from_str(&json).expect("Failed to deserialize min int");
assert_eq!(min_int, deserialized);
}
#[test]
fn test_type_kind_byte() {
let byte_type = TypeKind::Byte { signed: true };
assert_eq!(byte_type, TypeKind::Byte { signed: true });
}
#[test]
fn test_type_kind_short() {
let short_little_endian = TypeKind::Short {
endian: Endianness::Little,
signed: false,
};
let short_big_endian = TypeKind::Short {
endian: Endianness::Big,
signed: true,
};
assert_ne!(short_little_endian, short_big_endian);
assert_eq!(short_little_endian, short_little_endian.clone());
}
#[test]
fn test_type_kind_long() {
let long_native = TypeKind::Long {
endian: Endianness::Native,
signed: true,
};
match long_native {
TypeKind::Long { endian, signed } => {
assert_eq!(endian, Endianness::Native);
assert!(signed);
}
_ => panic!("Expected Long variant"),
}
}
#[test]
fn test_type_kind_string() {
let unlimited_string = TypeKind::String { max_length: None };
let limited_string = TypeKind::String {
max_length: Some(256),
};
assert_ne!(unlimited_string, limited_string);
assert_eq!(unlimited_string, unlimited_string.clone());
}
#[test]
fn test_type_kind_serialization() {
let types = vec![
TypeKind::Byte { signed: true },
TypeKind::Short {
endian: Endianness::Little,
signed: false,
},
TypeKind::Long {
endian: Endianness::Big,
signed: true,
},
TypeKind::Quad {
endian: Endianness::Little,
signed: false,
},
TypeKind::Quad {
endian: Endianness::Big,
signed: true,
},
TypeKind::Float {
endian: Endianness::Native,
},
TypeKind::Float {
endian: Endianness::Big,
},
TypeKind::Double {
endian: Endianness::Little,
},
TypeKind::Double {
endian: Endianness::Native,
},
TypeKind::String { max_length: None },
TypeKind::String {
max_length: Some(128),
},
];
for typ in types {
let json = serde_json::to_string(&typ).expect("Failed to serialize TypeKind");
let deserialized: TypeKind =
serde_json::from_str(&json).expect("Failed to deserialize TypeKind");
assert_eq!(typ, deserialized);
}
}
#[test]
fn test_operator_variants() {
let operators = [
Operator::Equal,
Operator::NotEqual,
Operator::BitwiseAnd,
Operator::BitwiseXor,
Operator::BitwiseNot,
Operator::AnyValue,
];
for (i, op) in operators.iter().enumerate() {
for (j, other) in operators.iter().enumerate() {
if i == j {
assert_eq!(op, other);
} else {
assert_ne!(op, other);
}
}
}
}
#[test]
fn test_operator_serialization() {
let operators = vec![
Operator::Equal,
Operator::NotEqual,
Operator::BitwiseAnd,
Operator::BitwiseXor,
Operator::BitwiseNot,
Operator::AnyValue,
];
for op in operators {
let json = serde_json::to_string(&op).expect("Failed to serialize Operator");
let deserialized: Operator =
serde_json::from_str(&json).expect("Failed to deserialize Operator");
assert_eq!(op, deserialized);
}
}
#[test]
fn test_magic_rule_creation() {
let rule = MagicRule {
offset: OffsetSpec::Absolute(0),
typ: TypeKind::Byte { signed: true },
op: Operator::Equal,
value: Value::Uint(0x7f),
message: "ELF magic".to_string(),
children: vec![],
level: 0,
strength_modifier: None,
};
assert_eq!(rule.message, "ELF magic");
assert_eq!(rule.level, 0);
assert!(rule.children.is_empty());
}
#[test]
fn test_magic_rule_with_children() {
let child_rule = MagicRule {
offset: OffsetSpec::Absolute(4),
typ: TypeKind::Byte { signed: true },
op: Operator::Equal,
value: Value::Uint(1),
message: "32-bit".to_string(),
children: vec![],
level: 1,
strength_modifier: None,
};
let parent_rule = MagicRule {
offset: OffsetSpec::Absolute(0),
typ: TypeKind::Long {
endian: Endianness::Little,
signed: false,
},
op: Operator::Equal,
value: Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]),
message: "ELF executable".to_string(),
children: vec![child_rule],
level: 0,
strength_modifier: None,
};
assert_eq!(parent_rule.children.len(), 1);
assert_eq!(parent_rule.children[0].level, 1);
assert_eq!(parent_rule.children[0].message, "32-bit");
}
#[test]
fn test_magic_rule_serialization() {
let rule = MagicRule {
offset: OffsetSpec::Absolute(16),
typ: TypeKind::Short {
endian: Endianness::Little,
signed: false,
},
op: Operator::NotEqual,
value: Value::Uint(0),
message: "Non-zero short value".to_string(),
children: vec![],
level: 2,
strength_modifier: None,
};
let json = serde_json::to_string(&rule).expect("Failed to serialize MagicRule");
let deserialized: MagicRule =
serde_json::from_str(&json).expect("Failed to deserialize MagicRule");
assert_eq!(rule.message, deserialized.message);
assert_eq!(rule.level, deserialized.level);
assert_eq!(rule.children.len(), deserialized.children.len());
}
#[test]
fn test_strength_modifier_variants() {
let add = StrengthModifier::Add(10);
let sub = StrengthModifier::Subtract(5);
let mul = StrengthModifier::Multiply(2);
let div = StrengthModifier::Divide(2);
let set = StrengthModifier::Set(50);
assert_eq!(add, StrengthModifier::Add(10));
assert_eq!(sub, StrengthModifier::Subtract(5));
assert_eq!(mul, StrengthModifier::Multiply(2));
assert_eq!(div, StrengthModifier::Divide(2));
assert_eq!(set, StrengthModifier::Set(50));
assert_ne!(add, sub);
assert_ne!(mul, div);
assert_ne!(set, add);
}
#[test]
fn test_strength_modifier_negative_values() {
let add_negative = StrengthModifier::Add(-10);
let sub_negative = StrengthModifier::Subtract(-5);
let set_negative = StrengthModifier::Set(-50);
assert_eq!(add_negative, StrengthModifier::Add(-10));
assert_eq!(sub_negative, StrengthModifier::Subtract(-5));
assert_eq!(set_negative, StrengthModifier::Set(-50));
}
#[test]
fn test_strength_modifier_serialization() {
let modifiers = vec![
StrengthModifier::Add(10),
StrengthModifier::Subtract(5),
StrengthModifier::Multiply(2),
StrengthModifier::Divide(3),
StrengthModifier::Set(100),
];
for modifier in modifiers {
let json =
serde_json::to_string(&modifier).expect("Failed to serialize StrengthModifier");
let deserialized: StrengthModifier =
serde_json::from_str(&json).expect("Failed to deserialize StrengthModifier");
assert_eq!(modifier, deserialized);
}
}
#[test]
fn test_strength_modifier_debug() {
let modifier = StrengthModifier::Add(25);
let debug_str = format!("{modifier:?}");
assert!(debug_str.contains("Add"));
assert!(debug_str.contains("25"));
}
#[test]
fn test_strength_modifier_clone() {
let original = StrengthModifier::Multiply(4);
let cloned = original;
assert_eq!(original, cloned);
}
#[test]
fn test_magic_rule_with_strength_modifier() {
let rule = MagicRule {
offset: OffsetSpec::Absolute(0),
typ: TypeKind::Byte { signed: true },
op: Operator::Equal,
value: Value::Uint(0x7f),
message: "ELF magic".to_string(),
children: vec![],
level: 0,
strength_modifier: Some(StrengthModifier::Add(20)),
};
assert_eq!(rule.strength_modifier, Some(StrengthModifier::Add(20)));
let json = serde_json::to_string(&rule).expect("Failed to serialize MagicRule");
let deserialized: MagicRule =
serde_json::from_str(&json).expect("Failed to deserialize MagicRule");
assert_eq!(rule.strength_modifier, deserialized.strength_modifier);
}
#[test]
fn test_magic_rule_without_strength_modifier() {
let rule = MagicRule {
offset: OffsetSpec::Absolute(0),
typ: TypeKind::Byte { signed: true },
op: Operator::Equal,
value: Value::Uint(0x7f),
message: "ELF magic".to_string(),
children: vec![],
level: 0,
strength_modifier: None,
};
assert_eq!(rule.strength_modifier, None);
}
}