Skip to main content

statsig_rust/evaluation/
dynamic_returnable.rs

1use std::{borrow::Cow, collections::HashMap, num::NonZeroU64, sync::Arc};
2
3use rkyv::{collections::swiss_table::ArchivedHashMap, string::ArchivedString};
4use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
5use serde_json::{
6    value::{to_raw_value, RawValue},
7    Value as JsonValue,
8};
9
10use crate::{
11    evaluation::rkyv_value::{stable_object_hash, ArchivedRkyvValue, RkyvValue},
12    hashing,
13    interned_values::InternedStore,
14    log_e,
15};
16
17const TAG: &str = "DynamicReturnable";
18
19lazy_static::lazy_static! {
20    static ref EMPTY_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
21        hash: 0,
22        value: DynamicReturnableValue::Null,
23        stable_hash: NonZeroU64::new(hashing::hash_u64_slice(&[0])),
24    };
25
26    static ref TRUE_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
27        hash: 0,
28        value: DynamicReturnableValue::Bool(true),
29        stable_hash: NonZeroU64::new(hashing::hash_u64_slice(&[1, 1])),
30    };
31
32    static ref FALSE_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
33        hash: 0,
34        value: DynamicReturnableValue::Bool(false),
35        stable_hash: NonZeroU64::new(hashing::hash_u64_slice(&[1, 0])),
36    };
37
38}
39
40#[derive(Clone, Debug)]
41pub struct DynamicReturnable {
42    pub hash: u64,
43    pub value: DynamicReturnableValue,
44    stable_hash: Option<NonZeroU64>,
45}
46
47impl DynamicReturnable {
48    pub fn empty() -> Self {
49        EMPTY_DYNAMIC_RETURNABLE.clone()
50    }
51
52    pub fn from_bool(value: bool) -> Self {
53        if value {
54            TRUE_DYNAMIC_RETURNABLE.clone()
55        } else {
56            FALSE_DYNAMIC_RETURNABLE.clone()
57        }
58    }
59
60    pub fn from_map(value: HashMap<String, JsonValue>) -> Self {
61        let raw_value = match to_raw_value(&value) {
62            Ok(raw_value) => raw_value,
63            Err(e) => {
64                log_e!(TAG, "Failed to convert map to raw value: {}", e);
65                return Self::empty();
66            }
67        };
68
69        InternedStore::get_or_intern_returnable(Cow::Owned(raw_value))
70    }
71
72    pub fn get_bool(&self) -> Option<bool> {
73        match self.value {
74            DynamicReturnableValue::Bool(value) => Some(value),
75            _ => None,
76        }
77    }
78
79    pub fn get_json_archived_ref(
80        &self,
81    ) -> Option<&'static ArchivedHashMap<ArchivedString, ArchivedRkyvValue>> {
82        match self.value {
83            DynamicReturnableValue::JsonArchived(v) => Some(v),
84            _ => None,
85        }
86    }
87
88    pub fn get_json_pointer_ref(&self) -> Option<&HashMap<String, RkyvValue>> {
89        match &self.value {
90            DynamicReturnableValue::JsonPointer(v) => Some(v.as_ref()),
91            DynamicReturnableValue::JsonStatic(v) => Some(v),
92            _ => None,
93        }
94    }
95
96    pub fn get_json(&self) -> Option<HashMap<String, JsonValue>> {
97        match &self.value {
98            DynamicReturnableValue::JsonPointer(v) => rkyv_hashmap_to_owned_json(v.as_ref()),
99            DynamicReturnableValue::JsonStatic(v) => rkyv_hashmap_to_owned_json(v),
100            DynamicReturnableValue::JsonArchived(v) => archived_hashmap_to_owned(v).map_or_else(
101                |e| {
102                    log_e!(TAG, "Failed to convert archived json. Error: {}", e);
103                    None
104                },
105                Some,
106            ),
107            _ => None,
108        }
109    }
110
111    pub fn get_hash(&self) -> u64 {
112        self.hash
113    }
114
115    pub(crate) fn get_stable_hash(&self) -> u64 {
116        if let Some(hash) = self.stable_hash {
117            return hash.get();
118        }
119
120        if matches!(&self.value, DynamicReturnableValue::JsonArchived(_)) {
121            if let Some(hash) = InternedStore::get_mmap_returnable_stable_hash(self.hash) {
122                return hash;
123            }
124        }
125
126        stable_hash(&self.value)
127    }
128
129    pub(crate) fn from_interned_value(hash: u64, value: DynamicReturnableValue) -> Self {
130        Self {
131            hash,
132            stable_hash: NonZeroU64::new(stable_hash(&value)),
133            value,
134        }
135    }
136
137    pub(crate) fn from_archived_value(
138        hash: u64,
139        value: &'static ArchivedHashMap<ArchivedString, ArchivedRkyvValue>,
140    ) -> Self {
141        Self {
142            hash,
143            value: DynamicReturnableValue::JsonArchived(value),
144            stable_hash: None,
145        }
146    }
147
148    #[cfg(test)]
149    pub(crate) fn has_inline_stable_hash(&self) -> bool {
150        self.stable_hash.is_some()
151    }
152}
153
154impl PartialEq for DynamicReturnable {
155    fn eq(&self, other: &Self) -> bool {
156        self.hash == other.hash && self.value == other.value
157    }
158}
159
160fn stable_hash(value: &DynamicReturnableValue) -> u64 {
161    match value {
162        DynamicReturnableValue::Null => hashing::hash_u64_slice(&[0]),
163        DynamicReturnableValue::Bool(value) => hashing::hash_u64_slice(&[1, u64::from(*value)]),
164        DynamicReturnableValue::JsonPointer(value) => {
165            stable_object_hash(value.iter().map(|(key, value)| (key.as_str(), value)))
166        }
167        DynamicReturnableValue::JsonStatic(value) => {
168            stable_object_hash(value.iter().map(|(key, value)| (key.as_str(), value)))
169        }
170        DynamicReturnableValue::JsonArchived(value) => archived_returnable_stable_hash(value),
171    }
172}
173
174pub(crate) fn archived_returnable_stable_hash(
175    value: &ArchivedHashMap<ArchivedString, ArchivedRkyvValue>,
176) -> u64 {
177    stable_object_hash(value.iter().map(|(key, value)| (key.as_str(), value)))
178}
179
180impl<'de> Deserialize<'de> for DynamicReturnable {
181    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182    where
183        D: Deserializer<'de>,
184    {
185        let raw_value_ref: Cow<RawValue> = Deserialize::deserialize(deserializer)?;
186        Ok(InternedStore::get_or_intern_returnable(raw_value_ref))
187    }
188}
189
190impl Serialize for DynamicReturnable {
191    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
192    where
193        S: Serializer,
194    {
195        match &self.value {
196            DynamicReturnableValue::JsonPointer(raw) => raw.serialize(serializer),
197            DynamicReturnableValue::JsonStatic(raw) => raw.serialize(serializer),
198            DynamicReturnableValue::JsonArchived(raw) => {
199                let mut map = serializer.serialize_map(Some(raw.len()))?;
200                for (key, value) in raw.iter() {
201                    map.serialize_entry(key.as_str(), value)?;
202                }
203                map.end()
204            }
205            DynamicReturnableValue::Null => serializer.serialize_none(),
206            DynamicReturnableValue::Bool(value) => serializer.serialize_bool(*value),
207        }
208    }
209}
210
211// ------------------------------------------------------------------------------- [ DynamicReturnableValue ]
212
213#[derive(Clone, Debug)]
214pub enum DynamicReturnableValue {
215    Null,
216    Bool(bool),
217    JsonPointer(Arc<HashMap<String, RkyvValue>>),
218    JsonStatic(&'static HashMap<String, RkyvValue>),
219    JsonArchived(&'static ArchivedHashMap<ArchivedString, ArchivedRkyvValue>),
220}
221
222impl PartialEq for DynamicReturnableValue {
223    fn eq(&self, other: &Self) -> bool {
224        match (self, other) {
225            (DynamicReturnableValue::Null, DynamicReturnableValue::Null) => return true,
226            (DynamicReturnableValue::Bool(a), DynamicReturnableValue::Bool(b)) => return *a == *b,
227            _ => {}
228        };
229
230        if let DynamicReturnableValue::JsonPointer(a) = self {
231            match other {
232                DynamicReturnableValue::JsonPointer(b) => return a.as_ref() == b.as_ref(),
233                DynamicReturnableValue::JsonStatic(b) => return a.as_ref() == *b,
234                DynamicReturnableValue::JsonArchived(b) => return eq_check(b, a.as_ref()),
235                _ => return false,
236            }
237        }
238
239        if let DynamicReturnableValue::JsonStatic(a) = self {
240            match other {
241                DynamicReturnableValue::JsonPointer(b) => return *a == b.as_ref(),
242                DynamicReturnableValue::JsonStatic(b) => return a == b,
243                DynamicReturnableValue::JsonArchived(b) => return eq_check(b, a),
244                _ => return false,
245            }
246        }
247
248        if let DynamicReturnableValue::JsonArchived(a) = self {
249            match other {
250                DynamicReturnableValue::JsonPointer(b) => return eq_check(a, b.as_ref()),
251                DynamicReturnableValue::JsonStatic(b) => return eq_check(a, b),
252                DynamicReturnableValue::JsonArchived(b) => return a == b,
253                _ => return false,
254            }
255        }
256
257        false
258    }
259}
260
261// ------------------------------------------------------------------------------- [ Rkyv Helper ]
262
263fn eq_check(
264    left: &ArchivedHashMap<ArchivedString, ArchivedRkyvValue>,
265    right: &HashMap<String, RkyvValue>,
266) -> bool {
267    for (key, value) in left.iter() {
268        match right.get_key_value(key.as_str()) {
269            Some((left_key, left_value)) => {
270                if left_key != key {
271                    return false;
272                }
273                if left_value != value {
274                    return false;
275                }
276            }
277            None => return false,
278        };
279    }
280    true
281}
282
283fn rkyv_hashmap_to_owned_json(
284    raw: &HashMap<String, RkyvValue>,
285) -> Option<HashMap<String, JsonValue>> {
286    match serde_json::to_value(raw) {
287        Ok(JsonValue::Object(o)) => Some(o.into_iter().collect()),
288        Ok(_) => None,
289        Err(e) => {
290            log_e!(TAG, "Failed to convert json. Error: {}", e);
291            None
292        }
293    }
294}
295
296fn archived_hashmap_to_owned(
297    raw: &'static ArchivedHashMap<ArchivedString, ArchivedRkyvValue>,
298) -> Result<HashMap<String, JsonValue>, serde_json::Error> {
299    let mut taken: HashMap<String, JsonValue> = HashMap::new();
300    for (key, value) in raw.iter() {
301        taken.insert(key.as_str().to_string(), serde_json::to_value(value)?);
302    }
303
304    Ok(taken)
305}