1use std::cmp::Ordering;
4
5use stix_model::StixValue;
6use stix_pattern::ast::Literal;
7
8fn literal_str(lit: &Literal) -> Option<&str> {
10 match lit {
11 Literal::String(s) | Literal::Timestamp(s) | Literal::Binary(s) | Literal::Hex(s) => {
12 Some(s)
13 }
14 _ => None,
15 }
16}
17
18fn literal_f64(lit: &Literal) -> Option<f64> {
20 match lit {
21 Literal::Integer(n) => Some(*n as f64),
22 Literal::Float(f) => Some(*f),
23 _ => None,
24 }
25}
26
27pub fn value_eq_literal(value: &StixValue, lit: &Literal) -> bool {
33 match (value, lit) {
34 (StixValue::Bool(b), Literal::Boolean(l)) => b == l,
35 _ => {
36 if let (Some(v), Some(l)) = (value.as_str(), literal_str(lit)) {
37 return v == l;
38 }
39 if let (Some(v), Some(l)) = (value.as_f64(), literal_f64(lit)) {
40 return v == l;
41 }
42 false
43 }
44 }
45}
46
47pub fn value_cmp_literal(value: &StixValue, lit: &Literal) -> Option<Ordering> {
52 if let (Some(v), Some(l)) = (value.as_f64(), literal_f64(lit)) {
53 return v.partial_cmp(&l);
54 }
55 if let (Some(v), Some(l)) = (value.as_str(), literal_str(lit)) {
56 return Some(v.cmp(l));
57 }
58 None
59}
60
61pub fn value_in_set(value: &StixValue, set: &[Literal]) -> bool {
63 set.iter().any(|lit| value_eq_literal(value, lit))
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use stix_model::StixValue;
70 use stix_pattern::ast::Literal;
71
72 #[test]
73 fn string_equality() {
74 assert!(value_eq_literal(
75 &StixValue::String("a".into()),
76 &Literal::String("a".into())
77 ));
78 assert!(!value_eq_literal(
79 &StixValue::String("a".into()),
80 &Literal::String("b".into())
81 ));
82 }
83
84 #[test]
85 fn numeric_equality_crosses_int_float() {
86 assert!(value_eq_literal(
87 &StixValue::Integer(3),
88 &Literal::Integer(3)
89 ));
90 assert!(value_eq_literal(
91 &StixValue::Integer(3),
92 &Literal::Float(3.0)
93 ));
94 assert!(value_eq_literal(
95 &StixValue::Float(3.0),
96 &Literal::Integer(3)
97 ));
98 assert!(!value_eq_literal(
99 &StixValue::Integer(3),
100 &Literal::Integer(4)
101 ));
102 }
103
104 #[test]
105 fn typed_literals_compare_as_strings() {
106 assert!(value_eq_literal(
107 &StixValue::String("2020-01-01T00:00:00Z".into()),
108 &Literal::Timestamp("2020-01-01T00:00:00Z".into())
109 ));
110 assert!(value_eq_literal(
111 &StixValue::String("cafe".into()),
112 &Literal::Hex("cafe".into())
113 ));
114 }
115
116 #[test]
117 fn ordering() {
118 use std::cmp::Ordering;
119 assert_eq!(
120 value_cmp_literal(&StixValue::Integer(2), &Literal::Integer(5)),
121 Some(Ordering::Less)
122 );
123 assert_eq!(
124 value_cmp_literal(&StixValue::String("b".into()), &Literal::String("a".into())),
125 Some(Ordering::Greater)
126 );
127 assert_eq!(
128 value_cmp_literal(&StixValue::Bool(true), &Literal::Integer(1)),
129 None
130 );
131 }
132
133 #[test]
134 fn membership() {
135 let set = vec![Literal::Integer(1), Literal::Integer(2)];
136 assert!(value_in_set(&StixValue::Integer(2), &set));
137 assert!(!value_in_set(&StixValue::Integer(3), &set));
138 }
139}