Skip to main content

statsig_rust/evaluation/
evaluator_value.rs

1use fancy_regex::Regex as FancyRegex;
2use serde::{
3    ser::{SerializeMap, SerializeSeq},
4    Deserialize, Deserializer, Serialize, Serializer,
5};
6use serde_json::{
7    value::{to_raw_value, RawValue},
8    Value as JsonValue, Value,
9};
10use std::{borrow::Cow, collections::HashMap, sync::Arc};
11
12use crate::{
13    evaluation::evaluation_data::InternedStrRef,
14    hashing,
15    interned_string::InternedString,
16    interned_values::{
17        mmap_data_v2::{ArchivedMmapEvaluatorValue, ArchivedMmapEvaluatorValueType},
18        InternedStore,
19    },
20    log_e,
21    value_parsing::try_parse_timestamp,
22};
23use crate::{user::user_value::UserValueRef, DynamicValue};
24
25use super::dynamic_string::DynamicString;
26
27lazy_static::lazy_static! {
28    pub(crate) static ref EMPTY_EVALUATOR_VALUE: EvaluatorValue = EvaluatorValue {
29        hash: 0,
30        inner: EvaluatorValueInner::Pointer(Arc::new(MemoizedEvaluatorValue::new(EvaluatorValueType::Null))),
31    };
32}
33
34const TAG: &str = "EvaluatorValue";
35
36#[derive(Clone)]
37#[non_exhaustive]
38pub enum EvaluatorValueInner {
39    Pointer(Arc<MemoizedEvaluatorValue>),
40    Static(&'static MemoizedEvaluatorValue),
41    Mmap(MmapEvaluatorValueHandle),
42}
43
44#[derive(Clone, Copy)]
45pub struct MmapEvaluatorValueHandle {
46    value: &'static ArchivedMmapEvaluatorValue,
47    regex: Option<&'static FancyRegex>,
48}
49
50impl MmapEvaluatorValueHandle {
51    pub(crate) fn new(
52        value: &'static ArchivedMmapEvaluatorValue,
53        regex: Option<&'static FancyRegex>,
54    ) -> Self {
55        Self { value, regex }
56    }
57}
58
59impl std::fmt::Debug for EvaluatorValueInner {
60    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        match self {
62            Self::Pointer(value) => formatter.debug_tuple("Pointer").field(value).finish(),
63            Self::Static(value) => formatter.debug_tuple("Static").field(value).finish(),
64            Self::Mmap(_) => formatter.write_str("Mmap"),
65        }
66    }
67}
68
69#[derive(Clone, Debug)]
70pub struct EvaluatorValue {
71    pub hash: u64,
72    pub inner: EvaluatorValueInner,
73}
74
75impl EvaluatorValue {
76    pub fn empty() -> &'static Self {
77        &EMPTY_EVALUATOR_VALUE
78    }
79
80    pub fn from_json_value(value: Value) -> Self {
81        let raw_value = match to_raw_value(&value) {
82            Ok(raw_value) => raw_value,
83            Err(e) => {
84                log_e!(TAG, "Failed to convert map to raw value: {}", e);
85                return Self::empty().clone();
86            }
87        };
88
89        InternedStore::get_or_intern_evaluator_value(Cow::Owned(raw_value))
90    }
91
92    pub fn compile_regex(&mut self) {
93        match &mut self.inner {
94            EvaluatorValueInner::Pointer(inner) => {
95                if inner.regex_value.is_some() {
96                    return;
97                }
98
99                let mut_inner = Arc::make_mut(inner);
100                mut_inner.compile_regex();
101                InternedStore::replace_evaluator_value(self.hash, inner.clone());
102            }
103            EvaluatorValueInner::Static(inner) => {
104                if inner.regex_value.is_some() {
105                    return;
106                }
107
108                // static values are immutable and should already be compiled during `InternedStore::preload(..)`
109                log_e!(TAG, "Cannot compile regex for static EvaluatorValue");
110            }
111            EvaluatorValueInner::Mmap(handle) => {
112                if handle.regex.is_some() {
113                    return;
114                }
115
116                let handle = *handle;
117                let mut materialized = InternedStore::materialize_mmap_evaluator_value_owned(
118                    handle.value,
119                    handle.regex,
120                );
121                materialized.compile_regex();
122                let inner = Arc::new(materialized);
123                InternedStore::replace_mmap_evaluator_value(self.hash, inner.clone());
124                self.inner = EvaluatorValueInner::Pointer(inner);
125            }
126        }
127    }
128}
129
130impl AsRef<MemoizedEvaluatorValue> for EvaluatorValue {
131    fn as_ref(&self) -> &MemoizedEvaluatorValue {
132        match &self.inner {
133            EvaluatorValueInner::Pointer(inner) => inner,
134            EvaluatorValueInner::Static(inner) => inner,
135            EvaluatorValueInner::Mmap(handle) => InternedStore::materialize_mmap_evaluator_value(
136                self.hash,
137                handle.value,
138                handle.regex,
139            ),
140        }
141    }
142}
143
144#[derive(Clone, Copy)]
145pub(crate) enum EvaluatorValueRef<'a> {
146    Owned(&'a MemoizedEvaluatorValue),
147    Mmap(&'a ArchivedMmapEvaluatorValue, Option<&'a FancyRegex>),
148}
149
150impl<'a> From<&'a MemoizedEvaluatorValue> for EvaluatorValueRef<'a> {
151    fn from(value: &'a MemoizedEvaluatorValue) -> Self {
152        Self::Owned(value)
153    }
154}
155
156impl EvaluatorValue {
157    pub(crate) fn as_value_ref(&self) -> EvaluatorValueRef<'_> {
158        match &self.inner {
159            EvaluatorValueInner::Pointer(value) => EvaluatorValueRef::Owned(value),
160            EvaluatorValueInner::Static(value) => EvaluatorValueRef::Owned(value),
161            EvaluatorValueInner::Mmap(handle) => {
162                EvaluatorValueRef::Mmap(handle.value, handle.regex)
163            }
164        }
165    }
166}
167
168impl<'a> EvaluatorValueRef<'a> {
169    pub(crate) fn bool_value(self) -> Option<bool> {
170        match self {
171            Self::Owned(value) => value.bool_value,
172            Self::Mmap(value, _) => value.bool_value.as_ref().copied(),
173        }
174    }
175
176    pub(crate) fn float_value(self) -> Option<f64> {
177        match self {
178            Self::Owned(value) => value.float_value,
179            Self::Mmap(value, _) => value.float_value.as_ref().map(|value| value.to_native()),
180        }
181    }
182
183    pub(crate) fn string_value(self) -> Option<&'a str> {
184        match self {
185            Self::Owned(value) => value
186                .string_value
187                .as_ref()
188                .map(|value| value.value.as_str()),
189            Self::Mmap(value, _) => {
190                let hash = value.string_value.as_ref().map(|hash| hash.to_native())?;
191                InternedStore::get_mmap_string(hash)
192            }
193        }
194    }
195
196    pub(crate) fn interned_string_value(self) -> Option<InternedStrRef<'a>> {
197        match self {
198            Self::Owned(value) => value
199                .string_value
200                .as_ref()
201                .map(|value| (&value.value).into()),
202            Self::Mmap(value, _) => value
203                .string_value
204                .as_ref()
205                .map(|hash| InternedStrRef::from_mmap(hash.to_native())),
206        }
207    }
208
209    pub(crate) fn regex_value(self) -> Option<&'a FancyRegex> {
210        match self {
211            Self::Owned(value) => value.regex_value.as_ref(),
212            Self::Mmap(_, regex) => regex,
213        }
214    }
215
216    pub(crate) fn timestamp_value(self) -> Option<i64> {
217        match self {
218            Self::Owned(value) => value.timestamp_value,
219            Self::Mmap(value, _) => value
220                .timestamp_value
221                .as_ref()
222                .map(|value| value.to_native()),
223        }
224    }
225
226    pub(crate) fn array_len(self) -> Option<usize> {
227        match self {
228            Self::Owned(value) => value.array_value.as_ref().map(HashMap::len),
229            Self::Mmap(value, _) => value.array_value.as_ref().map(|value| value.len()),
230        }
231    }
232
233    pub(crate) fn object_len(self) -> Option<usize> {
234        match self {
235            Self::Owned(value) => value.object_value.as_ref().map(HashMap::len),
236            Self::Mmap(value, _) => value.object_value.as_ref().map(|value| value.len()),
237        }
238    }
239
240    pub(crate) fn array_contains_key(self, key: &InternedString) -> bool {
241        match self {
242            Self::Owned(value) => value
243                .array_value
244                .as_ref()
245                .is_some_and(|array| array.contains_key(key)),
246            Self::Mmap(value, _) => value.array_value.as_ref().is_some_and(|array| {
247                array.contains_key(&rkyv::primitive::ArchivedU64::from_native(key.hash))
248            }),
249        }
250    }
251
252    pub(crate) fn array_contains_lowercase(self, key: &str) -> bool {
253        match self {
254            Self::Owned(value) => value
255                .array_value
256                .as_ref()
257                .is_some_and(|array| array.keys().any(|candidate| candidate.as_str() == key)),
258            Self::Mmap(value, _) => value.array_value.as_ref().is_some_and(|array| {
259                let hash = hashing::hash_one(key.as_bytes());
260                array.contains_key(&rkyv::primitive::ArchivedU64::from_native(hash))
261            }),
262        }
263    }
264
265    pub(crate) fn object_contains_key(self, key: &InternedString) -> bool {
266        match self {
267            Self::Owned(value) => value
268                .object_value
269                .as_ref()
270                .is_some_and(|object| object.contains_key(key)),
271            Self::Mmap(value, _) => value.object_value.as_ref().is_some_and(|object| {
272                object.contains_key(&rkyv::primitive::ArchivedU64::from_native(key.hash))
273            }),
274        }
275    }
276
277    pub(crate) fn object_contains_key_str(self, key: &str) -> bool {
278        match self {
279            Self::Owned(value) => value
280                .object_value
281                .as_ref()
282                .is_some_and(|object| object.keys().any(|candidate| candidate.as_str() == key)),
283            Self::Mmap(value, _) => value.object_value.as_ref().is_some_and(|object| {
284                let hash = hashing::hash_one(key.as_bytes());
285                object.contains_key(&rkyv::primitive::ArchivedU64::from_native(hash))
286            }),
287        }
288    }
289
290    pub(crate) fn any_array_entry(
291        self,
292        mut predicate: impl FnMut(&str, usize, &str) -> bool,
293    ) -> bool {
294        match self {
295            Self::Owned(value) => value.array_value.as_ref().is_some_and(|array| {
296                array.iter().any(|(lowercase, (index, original))| {
297                    predicate(lowercase.as_str(), *index, original.as_str())
298                })
299            }),
300            Self::Mmap(value, _) => value.array_value.as_ref().is_some_and(|array| {
301                array.iter().any(|(lowercase, entry)| {
302                    let Some(lowercase) = InternedStore::get_mmap_string(lowercase.to_native())
303                    else {
304                        return false;
305                    };
306                    let Some(original) = InternedStore::get_mmap_string(entry.1.to_native()) else {
307                        return false;
308                    };
309                    predicate(lowercase, entry.0.to_native() as usize, original)
310                })
311            }),
312        }
313    }
314
315    fn all_array_entries(self, mut predicate: impl FnMut(&str, usize, &str) -> bool) -> bool {
316        match self {
317            Self::Owned(value) => value.array_value.as_ref().is_some_and(|array| {
318                array.iter().all(|(lowercase, (index, original))| {
319                    predicate(lowercase.as_str(), *index, original.as_str())
320                })
321            }),
322            Self::Mmap(value, _) => value.array_value.as_ref().is_some_and(|array| {
323                array.iter().all(|(lowercase, entry)| {
324                    let Some(lowercase) = InternedStore::get_mmap_string(lowercase.to_native())
325                    else {
326                        return false;
327                    };
328                    let Some(original) = InternedStore::get_mmap_string(entry.1.to_native()) else {
329                        return false;
330                    };
331                    predicate(lowercase, entry.0.to_native() as usize, original)
332                })
333            }),
334        }
335    }
336
337    fn all_object_entries(self, mut predicate: impl FnMut(&str, &str) -> bool) -> bool {
338        match self {
339            Self::Owned(value) => value.object_value.as_ref().is_some_and(|object| {
340                object
341                    .iter()
342                    .all(|(key, value)| predicate(key.as_str(), value.value.as_str()))
343            }),
344            Self::Mmap(value, _) => value.object_value.as_ref().is_some_and(|object| {
345                object.iter().all(|(key, value)| {
346                    let Some(key) = InternedStore::get_mmap_string(key.to_native()) else {
347                        return false;
348                    };
349                    let Some(value) = InternedStore::get_mmap_string(value.to_native()) else {
350                        return false;
351                    };
352                    predicate(key, value)
353                })
354            }),
355        }
356    }
357
358    fn value_type(self) -> EvaluatorValueType {
359        match self {
360            Self::Owned(value) => value.value_type,
361            Self::Mmap(value, _) => match value.value_type {
362                ArchivedMmapEvaluatorValueType::Null => EvaluatorValueType::Null,
363                ArchivedMmapEvaluatorValueType::Bool => EvaluatorValueType::Bool,
364                ArchivedMmapEvaluatorValueType::Number => EvaluatorValueType::Number,
365                ArchivedMmapEvaluatorValueType::String => EvaluatorValueType::String,
366                ArchivedMmapEvaluatorValueType::Array => EvaluatorValueType::Array,
367                ArchivedMmapEvaluatorValueType::Object => EvaluatorValueType::Object,
368            },
369        }
370    }
371
372    pub(crate) fn is_equal_to_user_value(self, other: UserValueRef<'_>) -> bool {
373        match self.value_type() {
374            EvaluatorValueType::Null => other.is_null(),
375            EvaluatorValueType::Bool => self.bool_value() == other.bool_value(),
376            EvaluatorValueType::Number => self.float_value() == other.float_value(),
377            EvaluatorValueType::String => self.string_value() == other.string_value(),
378            EvaluatorValueType::Array => {
379                let Some(len) = self.array_len() else {
380                    return other.array_len().is_none();
381                };
382                if other.array_len() != Some(len) {
383                    return false;
384                }
385                self.all_array_entries(|_, index, expected| {
386                    other.array_item(index).and_then(UserValueRef::string_value) == Some(expected)
387                })
388            }
389            EvaluatorValueType::Object => {
390                let Some(len) = self.object_len() else {
391                    return other.object_len().is_none();
392                };
393                if other.object_len() != Some(len) {
394                    return false;
395                }
396                self.all_object_entries(|key, expected| {
397                    other.object_get(key).and_then(UserValueRef::string_value) == Some(expected)
398                })
399            }
400        }
401    }
402
403    fn array_entry(self, lowercase: &str) -> Option<(usize, &'a str)> {
404        match self {
405            Self::Owned(value) => {
406                value
407                    .array_value
408                    .as_ref()?
409                    .iter()
410                    .find_map(|(candidate, (index, original))| {
411                        (candidate.as_str() == lowercase).then_some((*index, original.as_str()))
412                    })
413            }
414            Self::Mmap(value, _) => {
415                let hash = hashing::hash_one(lowercase.as_bytes());
416                let entry = value
417                    .array_value
418                    .as_ref()?
419                    .get(&rkyv::primitive::ArchivedU64::from_native(hash))?;
420                Some((
421                    entry.0.to_native() as usize,
422                    InternedStore::get_mmap_string(entry.1.to_native())?,
423                ))
424            }
425        }
426    }
427
428    fn object_entry(self, key: &str) -> Option<&'a str> {
429        match self {
430            Self::Owned(value) => {
431                value
432                    .object_value
433                    .as_ref()?
434                    .iter()
435                    .find_map(|(candidate, value)| {
436                        (candidate.as_str() == key).then_some(value.value.as_str())
437                    })
438            }
439            Self::Mmap(value, _) => {
440                let hash = hashing::hash_one(key.as_bytes());
441                let value = value
442                    .object_value
443                    .as_ref()?
444                    .get(&rkyv::primitive::ArchivedU64::from_native(hash))?;
445                InternedStore::get_mmap_string(value.to_native())
446            }
447        }
448    }
449
450    fn is_equal_to_evaluator_value(self, other: EvaluatorValueRef<'_>) -> bool {
451        if let (EvaluatorValueRef::Owned(left), EvaluatorValueRef::Owned(right)) = (self, other) {
452            return left == right;
453        }
454        let iterate_other = matches!(
455            (self, other),
456            (EvaluatorValueRef::Mmap(_, _), EvaluatorValueRef::Owned(_))
457        );
458
459        if self.value_type() != other.value_type() {
460            return false;
461        }
462
463        match self.value_type() {
464            EvaluatorValueType::Null => true,
465            EvaluatorValueType::Bool => self.bool_value() == other.bool_value(),
466            EvaluatorValueType::Number => self.float_value() == other.float_value(),
467            EvaluatorValueType::String => self.string_value() == other.string_value(),
468            EvaluatorValueType::Array => match (self.array_len(), other.array_len()) {
469                (None, None) => true,
470                (Some(left), Some(right)) if left == right => {
471                    if iterate_other {
472                        other.all_array_entries(|lowercase, index, original| {
473                            self.array_entry(lowercase) == Some((index, original))
474                        })
475                    } else {
476                        self.all_array_entries(|lowercase, index, original| {
477                            other.array_entry(lowercase) == Some((index, original))
478                        })
479                    }
480                }
481                _ => false,
482            },
483            EvaluatorValueType::Object => match (self.object_len(), other.object_len()) {
484                (None, None) => true,
485                (Some(left), Some(right)) if left == right => {
486                    if iterate_other {
487                        other.all_object_entries(|key, value| self.object_entry(key) == Some(value))
488                    } else {
489                        self.all_object_entries(|key, value| other.object_entry(key) == Some(value))
490                    }
491                }
492                _ => false,
493            },
494        }
495    }
496}
497
498struct SerializedDynamicString<'a>(&'a str);
499
500impl Serialize for SerializedDynamicString<'_> {
501    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
502    where
503        S: Serializer,
504    {
505        match self.0.parse::<bool>() {
506            Ok(value) => serializer.serialize_bool(value),
507            Err(_) => serializer.serialize_str(self.0),
508        }
509    }
510}
511
512impl Serialize for EvaluatorValueRef<'_> {
513    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
514    where
515        S: Serializer,
516    {
517        let value = match *self {
518            Self::Owned(value) => return value.serialize(serializer),
519            Self::Mmap(value, _) => value,
520        };
521
522        match value.value_type {
523            ArchivedMmapEvaluatorValueType::Null => serializer.serialize_unit(),
524            ArchivedMmapEvaluatorValueType::Bool => {
525                value.bool_value.as_ref().copied().serialize(serializer)
526            }
527            ArchivedMmapEvaluatorValueType::Number => value
528                .float_value
529                .as_ref()
530                .map(|value| value.to_native())
531                .serialize(serializer),
532            ArchivedMmapEvaluatorValueType::String => {
533                let Some(hash) = value.string_value.as_ref() else {
534                    return serializer.serialize_none();
535                };
536                let raw = mmap_string_for_serialization::<S>(hash.to_native())?;
537                SerializedDynamicString(raw).serialize(serializer)
538            }
539            ArchivedMmapEvaluatorValueType::Array => {
540                let Some(array) = value.array_value.as_ref() else {
541                    return serializer.serialize_none();
542                };
543                let mut entries = Vec::with_capacity(array.len());
544                for (_, entry) in array.iter() {
545                    let raw = mmap_string_for_serialization::<S>(entry.1.to_native())?;
546                    entries.push((entry.0.to_native(), raw));
547                }
548                entries.sort_unstable_by_key(|(index, _)| *index);
549
550                let mut sequence = serializer.serialize_seq(Some(entries.len()))?;
551                for (_, raw) in entries {
552                    sequence.serialize_element(raw)?;
553                }
554                sequence.end()
555            }
556            ArchivedMmapEvaluatorValueType::Object => {
557                let Some(object) = value.object_value.as_ref() else {
558                    return serializer.serialize_none();
559                };
560                let mut map = serializer.serialize_map(Some(object.len()))?;
561                for (key, value) in object.iter() {
562                    let key = mmap_string_for_serialization::<S>(key.to_native())?;
563                    let value = mmap_string_for_serialization::<S>(value.to_native())?;
564                    map.serialize_entry(key, &SerializedDynamicString(value))?;
565                }
566                map.end()
567            }
568        }
569    }
570}
571
572fn mmap_string_for_serialization<S: Serializer>(hash: u64) -> Result<&'static str, S::Error> {
573    InternedStore::get_mmap_string(hash).ok_or_else(|| {
574        serde::ser::Error::custom(format!(
575            "Interned mmap evaluator value references missing string hash {hash}"
576        ))
577    })
578}
579
580impl<'de> Deserialize<'de> for EvaluatorValue {
581    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
582    where
583        D: Deserializer<'de>,
584    {
585        let raw_value_ref: Box<RawValue> = Deserialize::deserialize(deserializer)?;
586        Ok(InternedStore::get_or_intern_evaluator_value(Cow::Owned(
587            raw_value_ref,
588        )))
589    }
590}
591
592impl Serialize for EvaluatorValue {
593    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
594    where
595        S: Serializer,
596    {
597        self.as_value_ref().serialize(serializer)
598    }
599}
600
601impl PartialEq for EvaluatorValue {
602    fn eq(&self, other: &Self) -> bool {
603        self.as_value_ref()
604            .is_equal_to_evaluator_value(other.as_value_ref())
605    }
606}
607
608// ------------------------------------------------------------------------------- [ MemoizedEvaluatorValue ]
609
610#[derive(Debug, PartialEq, Eq, Clone, Copy)]
611pub enum EvaluatorValueType {
612    Null,
613
614    Bool,
615    Number,
616    String,
617    Array,
618    Object,
619}
620
621#[derive(Debug, Clone)]
622pub struct MemoizedEvaluatorValue {
623    pub value_type: EvaluatorValueType,
624    pub bool_value: Option<bool>,
625    pub float_value: Option<f64>,
626    pub string_value: Option<DynamicString>,
627    pub regex_value: Option<FancyRegex>,
628    pub timestamp_value: Option<i64>,
629    pub object_value: Option<HashMap<InternedString, DynamicString>>,
630
631    // - Note on Array Value ------------------------------------------------------------
632    // - Keyed by lowercase string so we can lookup with O(1) during evaluation.
633    // - Format is `{ lower_case_str: (index, str) }` i.e: ["Apple", "Banana"] becomes { "apple": (0, "Apple"), "banana": (1, "Banana") }
634    // - The index is what position in the array it is, currently this is only used to serialzie back to the original JSON.
635    // ----------------------------------------------------------------------------------
636    pub array_value: Option<HashMap<InternedString, (usize, InternedString)>>,
637}
638
639impl MemoizedEvaluatorValue {
640    pub fn from_raw_value(raw_value: Cow<'_, RawValue>) -> Self {
641        match serde_json::from_str(raw_value.get()) {
642            Ok(value) => value,
643            Err(e) => {
644                log_e!(
645                    TAG,
646                    "Failed to convert raw value to MemoizedEvaluatorValue: {}",
647                    e
648                );
649                Self::null()
650            }
651        }
652    }
653}
654
655impl MemoizedEvaluatorValue {
656    pub fn new(value_type: EvaluatorValueType) -> Self {
657        Self {
658            value_type,
659            bool_value: None,
660            float_value: None,
661            string_value: None,
662            regex_value: None,
663            timestamp_value: None,
664            array_value: None,
665            object_value: None,
666        }
667    }
668
669    pub fn null() -> Self {
670        Self::new(EvaluatorValueType::Null)
671    }
672
673    pub fn compile_regex(&mut self) {
674        let str_value = match &self.string_value {
675            Some(dyn_str) => &dyn_str.value,
676            None => return,
677        };
678
679        if let Ok(regex) = FancyRegex::new(str_value) {
680            self.regex_value = Some(regex);
681        }
682    }
683
684    pub fn is_equal_to_user_value(&self, other: UserValueRef<'_>) -> bool {
685        EvaluatorValueRef::from(self).is_equal_to_user_value(other)
686    }
687
688    pub fn is_equal_to_dynamic_value(&self, other: &DynamicValue) -> bool {
689        self.is_equal_to_user_value(UserValueRef::Dynamic(other))
690    }
691}
692
693// Used during evaluation:
694// - ua_parser
695impl From<String> for MemoizedEvaluatorValue {
696    fn from(value: String) -> Self {
697        let int_value = value.parse::<i64>().ok();
698        MemoizedEvaluatorValue {
699            timestamp_value: try_parse_timestamp(&value, int_value),
700            float_value: value.parse::<f64>().ok(),
701            string_value: Some(DynamicString::from(value)),
702            ..MemoizedEvaluatorValue::new(EvaluatorValueType::String)
703        }
704    }
705}
706
707// Used during Deserialization
708impl From<JsonValue> for MemoizedEvaluatorValue {
709    fn from(value: JsonValue) -> Self {
710        match value {
711            JsonValue::Null => MemoizedEvaluatorValue::new(EvaluatorValueType::Null),
712
713            JsonValue::Bool(b) => MemoizedEvaluatorValue {
714                bool_value: Some(b),
715                ..MemoizedEvaluatorValue::new(EvaluatorValueType::Bool)
716            },
717
718            JsonValue::Number(n) => MemoizedEvaluatorValue {
719                float_value: n.as_f64(),
720                ..MemoizedEvaluatorValue::new(EvaluatorValueType::Number)
721            },
722
723            JsonValue::String(s) => MemoizedEvaluatorValue::from(s),
724
725            JsonValue::Array(arr) => {
726                let keyed_array: HashMap<InternedString, (usize, InternedString)> = arr
727                    .into_iter()
728                    .enumerate()
729                    .map(|(idx, val)| {
730                        let str_value = match val.as_str() {
731                            Some(s) => s.to_string(), // Value is a String
732                            None => val.to_string(),  // Value was not a String, but can be made one
733                        };
734
735                        let interned_lowercased_str =
736                            InternedString::from_string(str_value.to_lowercase());
737                        let interned_str = InternedString::from_string(str_value);
738
739                        (interned_lowercased_str, (idx, interned_str))
740                    })
741                    .collect();
742
743                MemoizedEvaluatorValue {
744                    array_value: Some(keyed_array),
745                    ..MemoizedEvaluatorValue::new(EvaluatorValueType::Array)
746                }
747            }
748
749            JsonValue::Object(obj) => MemoizedEvaluatorValue {
750                object_value: Some(
751                    obj.into_iter()
752                        .map(|(k, v)| (InternedString::from_string(k), DynamicString::from(v)))
753                        .collect(),
754                ),
755                ..MemoizedEvaluatorValue::new(EvaluatorValueType::Object)
756            },
757        }
758    }
759}
760
761impl Serialize for MemoizedEvaluatorValue {
762    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
763    where
764        S: Serializer,
765    {
766        match &self.value_type {
767            EvaluatorValueType::Null => JsonValue::Null.serialize(serializer),
768            EvaluatorValueType::Bool => self.bool_value.serialize(serializer),
769            EvaluatorValueType::Number => self.float_value.serialize(serializer),
770            EvaluatorValueType::String => self.string_value.serialize(serializer),
771            EvaluatorValueType::Array => {
772                let array_map = match &self.array_value {
773                    Some(a) => a,
774                    None => return JsonValue::Null.serialize(serializer),
775                };
776
777                let mut entries: Vec<(usize, String)> = array_map
778                    .values()
779                    .map(|(idx, val)| (*idx, val.unperformant_to_string()))
780                    .collect();
781                entries.sort_by_key(|(idx, _)| *idx);
782                let result: Vec<String> = entries.into_iter().map(|(_, val)| val).collect();
783
784                result.serialize(serializer)
785            }
786            EvaluatorValueType::Object => self.object_value.serialize(serializer),
787        }
788    }
789}
790
791impl<'de> Deserialize<'de> for MemoizedEvaluatorValue {
792    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
793    where
794        D: Deserializer<'de>,
795    {
796        let json_value = JsonValue::deserialize(deserializer)?;
797        Ok(MemoizedEvaluatorValue::from(json_value))
798    }
799}
800
801impl PartialEq for MemoizedEvaluatorValue {
802    fn eq(&self, other: &Self) -> bool {
803        self.value_type == other.value_type
804            && self.bool_value == other.bool_value
805            && self.float_value == other.float_value
806            && self.string_value == other.string_value
807            && self.array_value == other.array_value
808            && self.object_value == other.object_value
809    }
810}
811
812#[macro_export]
813macro_rules! test_only_make_eval_value {
814    ($x:expr) => {
815        $crate::evaluation::evaluator_value::MemoizedEvaluatorValue::from(serde_json::json!($x))
816    };
817}
818
819#[cfg(test)]
820mod tests {
821    use super::MemoizedEvaluatorValue;
822    use serde_json::json;
823
824    #[test]
825    fn serialize_array_with_case_collision_is_safe() {
826        let value = MemoizedEvaluatorValue::from(json!(["A", "a"]));
827        let serialized = serde_json::to_value(&value).expect("serialize MemoizedEvaluatorValue");
828        assert_eq!(serialized, json!(["a"]));
829    }
830}