statsig_rust/evaluation/
evaluator_value.rs

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