Skip to main content

statsig_rust/evaluation/
evaluator_value.rs

1use chrono::{DateTime, NaiveDateTime};
2use fancy_regex::Regex as FancyRegex;
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use serde_json::{
5    value::{to_raw_value, RawValue},
6    Value as JsonValue, Value,
7};
8use std::{borrow::Cow, collections::HashMap, sync::Arc};
9
10use crate::{
11    impl_interned_value, interned_string::InternedString, interned_value_store::FromRawValue,
12    log_e, unwrap_or_return, DynamicValue,
13};
14
15use super::dynamic_string::DynamicString;
16
17lazy_static::lazy_static! {
18    pub(crate) static ref EMPTY_EVALUATOR_VALUE: EvaluatorValue = EvaluatorValue {
19        hash: 0,
20        inner: Arc::new(MemoizedEvaluatorValue::new(EvaluatorValueType::Null)),
21    };
22}
23
24const TAG: &str = "EvaluatorValue";
25
26#[derive(Clone, Debug)]
27pub struct EvaluatorValue {
28    pub hash: u64,
29    pub inner: Arc<MemoizedEvaluatorValue>,
30}
31
32impl_interned_value!(EvaluatorValue, MemoizedEvaluatorValue);
33
34impl EvaluatorValue {
35    pub fn empty() -> &'static Self {
36        &EMPTY_EVALUATOR_VALUE
37    }
38
39    pub fn from_json_value(value: Value) -> Self {
40        let raw_value = match to_raw_value(&value) {
41            Ok(raw_value) => raw_value,
42            Err(e) => {
43                log_e!(TAG, "Failed to convert map to raw value: {}", e);
44                return Self::empty().clone();
45            }
46        };
47
48        let (hash, value) = EvaluatorValue::get_or_create_memoized(Cow::Owned(raw_value));
49        Self { hash, inner: value }
50    }
51
52    pub fn compile_regex(&mut self) {
53        let mut_inner = Arc::make_mut(&mut self.inner);
54        mut_inner.compile_regex();
55    }
56}
57
58impl<'de> Deserialize<'de> for EvaluatorValue {
59    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60    where
61        D: Deserializer<'de>,
62    {
63        let raw_value_ref: Box<RawValue> = Deserialize::deserialize(deserializer)?;
64        let (hash, value) = EvaluatorValue::get_or_create_memoized(Cow::Owned(raw_value_ref));
65        Ok(EvaluatorValue { hash, inner: value })
66    }
67}
68
69impl Serialize for EvaluatorValue {
70    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
71    where
72        S: Serializer,
73    {
74        self.inner.serialize(serializer)
75    }
76}
77
78impl PartialEq for EvaluatorValue {
79    fn eq(&self, other: &Self) -> bool {
80        self.inner == other.inner
81    }
82}
83
84#[derive(Debug, PartialEq, Clone)]
85pub enum EvaluatorValueType {
86    Null,
87
88    Bool,
89    Number,
90    String,
91    Array,
92    Object,
93}
94
95#[derive(Debug, Clone)]
96pub struct MemoizedEvaluatorValue {
97    pub value_type: EvaluatorValueType,
98    pub bool_value: Option<bool>,
99    pub float_value: Option<f64>,
100    pub string_value: Option<DynamicString>,
101    pub regex_value: Option<FancyRegex>,
102    pub timestamp_value: Option<i64>,
103    // { lower_case_str: (index, str) } -- Keyed by lowercase string so we can lookup with O(1)
104    pub array_value: Option<HashMap<InternedString, (usize, InternedString)>>,
105    pub object_value: Option<HashMap<InternedString, DynamicString>>,
106}
107
108impl FromRawValue for MemoizedEvaluatorValue {
109    fn from_raw_value(raw_value: Cow<'_, RawValue>) -> Self {
110        match serde_json::from_str(raw_value.get()) {
111            Ok(value) => value,
112            Err(e) => {
113                log_e!(
114                    TAG,
115                    "Failed to convert raw value to MemoizedEvaluatorValue: {}",
116                    e
117                );
118                Self::null()
119            }
120        }
121    }
122}
123
124impl MemoizedEvaluatorValue {
125    pub fn new(value_type: EvaluatorValueType) -> Self {
126        Self {
127            value_type,
128            bool_value: None,
129            float_value: None,
130            string_value: None,
131            regex_value: None,
132            timestamp_value: None,
133            array_value: None,
134            object_value: None,
135        }
136    }
137
138    pub fn null() -> Self {
139        Self::new(EvaluatorValueType::Null)
140    }
141
142    pub fn compile_regex(&mut self) {
143        let str_value = match &self.string_value {
144            Some(dyn_str) => &dyn_str.value,
145            None => return,
146        };
147
148        if let Ok(regex) = FancyRegex::new(str_value) {
149            self.regex_value = Some(regex);
150        }
151    }
152
153    pub fn is_equal_to_dynamic_value(&self, other: &DynamicValue) -> bool {
154        match self.value_type {
155            EvaluatorValueType::Null => other.json_value == Value::Null,
156            EvaluatorValueType::Bool => self.bool_value == other.bool_value,
157            EvaluatorValueType::Number => self.float_value == other.float_value,
158            EvaluatorValueType::String => self.string_value == other.string_value,
159            EvaluatorValueType::Array => {
160                let self_keyed_arr = match &self.array_value {
161                    Some(map) => map,
162                    None => return other.array_value.is_none(),
163                };
164
165                let other_arr = match &other.array_value {
166                    Some(arr) => arr,
167                    None => return false,
168                };
169
170                if self_keyed_arr.len() != other_arr.len() {
171                    return false;
172                }
173
174                for (i, self_value) in self_keyed_arr.values() {
175                    let other_dyn_str = unwrap_or_return!(&other_arr[*i].string_value, false);
176                    if *self_value != other_dyn_str.value {
177                        return false;
178                    }
179                }
180
181                true
182            }
183            EvaluatorValueType::Object => {
184                let self_obj = match &self.object_value {
185                    Some(map) => map,
186                    None => return other.object_value.is_none(),
187                };
188
189                let other_obj = match &other.object_value {
190                    Some(arr) => arr,
191                    None => return false,
192                };
193
194                if self_obj.len() != other_obj.len() {
195                    return false;
196                }
197
198                for (k, v) in self_obj {
199                    let other_dyn_val = unwrap_or_return!(other_obj.get(k.as_str()), false);
200                    let other_str_val = unwrap_or_return!(&other_dyn_val.string_value, false);
201                    if other_str_val.value != v.value {
202                        return false;
203                    }
204                }
205
206                true
207            }
208        }
209    }
210}
211
212// Used during evaluation:
213// - ua_parser
214impl From<String> for MemoizedEvaluatorValue {
215    fn from(value: String) -> Self {
216        MemoizedEvaluatorValue {
217            timestamp_value: try_parse_timestamp(&value),
218            float_value: value.parse::<f64>().ok(),
219            string_value: Some(DynamicString::from(value)),
220            ..MemoizedEvaluatorValue::new(EvaluatorValueType::String)
221        }
222    }
223}
224
225// Used during Deserialization
226impl From<JsonValue> for MemoizedEvaluatorValue {
227    fn from(value: JsonValue) -> Self {
228        match value {
229            JsonValue::Null => MemoizedEvaluatorValue::new(EvaluatorValueType::Null),
230
231            JsonValue::Bool(b) => MemoizedEvaluatorValue {
232                bool_value: Some(b),
233                ..MemoizedEvaluatorValue::new(EvaluatorValueType::Bool)
234            },
235
236            JsonValue::Number(n) => MemoizedEvaluatorValue {
237                float_value: n.as_f64(),
238                ..MemoizedEvaluatorValue::new(EvaluatorValueType::Number)
239            },
240
241            JsonValue::String(s) => MemoizedEvaluatorValue::from(s),
242
243            JsonValue::Array(arr) => {
244                let keyed_array: HashMap<InternedString, (usize, InternedString)> = arr
245                    .into_iter()
246                    .enumerate()
247                    .map(|(idx, val)| {
248                        let str_value = match val.as_str() {
249                            Some(s) => s.to_string(), // Value is a String
250                            None => val.to_string(),  // Value was not a String, but can be made one
251                        };
252
253                        let interned_lowercased_str =
254                            InternedString::from_string(str_value.to_lowercase());
255                        let interned_str = InternedString::from_string(str_value);
256
257                        (interned_lowercased_str, (idx, interned_str))
258                    })
259                    .collect();
260
261                MemoizedEvaluatorValue {
262                    array_value: Some(keyed_array),
263                    ..MemoizedEvaluatorValue::new(EvaluatorValueType::Array)
264                }
265            }
266
267            JsonValue::Object(obj) => MemoizedEvaluatorValue {
268                object_value: Some(
269                    obj.into_iter()
270                        .map(|(k, v)| (InternedString::from_string(k), DynamicString::from(v)))
271                        .collect(),
272                ),
273                ..MemoizedEvaluatorValue::new(EvaluatorValueType::Object)
274            },
275        }
276    }
277}
278
279impl Serialize for MemoizedEvaluatorValue {
280    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
281    where
282        S: Serializer,
283    {
284        match &self.value_type {
285            EvaluatorValueType::Null => JsonValue::Null.serialize(serializer),
286            EvaluatorValueType::Bool => self.bool_value.serialize(serializer),
287            EvaluatorValueType::Number => self.float_value.serialize(serializer),
288            EvaluatorValueType::String => self.string_value.serialize(serializer),
289            EvaluatorValueType::Array => {
290                let array_map = match &self.array_value {
291                    Some(a) => a,
292                    None => return JsonValue::Null.serialize(serializer),
293                };
294
295                let mut result = vec![String::new(); array_map.len()];
296
297                for (idx, val) in array_map.values() {
298                    result[*idx] = val.unperformant_to_string();
299                }
300
301                result.serialize(serializer)
302            }
303            EvaluatorValueType::Object => self.object_value.serialize(serializer),
304        }
305    }
306}
307
308impl<'de> Deserialize<'de> for MemoizedEvaluatorValue {
309    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
310    where
311        D: Deserializer<'de>,
312    {
313        let json_value = JsonValue::deserialize(deserializer)?;
314        Ok(MemoizedEvaluatorValue::from(json_value))
315    }
316}
317
318impl PartialEq for MemoizedEvaluatorValue {
319    fn eq(&self, other: &Self) -> bool {
320        self.value_type == other.value_type
321            && self.bool_value == other.bool_value
322            && self.float_value == other.float_value
323            && self.string_value == other.string_value
324            && self.array_value == other.array_value
325            && self.object_value == other.object_value
326    }
327}
328
329fn try_parse_timestamp(s: &str) -> Option<i64> {
330    if let Ok(ts) = s.parse::<i64>() {
331        return Some(ts);
332    }
333
334    if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
335        return Some(dt.timestamp_millis());
336    }
337
338    if let Ok(ndt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
339        return Some(ndt.and_utc().timestamp_millis());
340    }
341
342    None
343}
344
345#[macro_export]
346macro_rules! test_only_make_eval_value {
347    ($x:expr) => {
348        $crate::evaluation::evaluator_value::MemoizedEvaluatorValue::from(serde_json::json!($x))
349    };
350}