Skip to main content

statsig_rust/evaluation/
dynamic_returnable.rs

1use std::{borrow::Cow, collections::HashMap, sync::Arc};
2
3use rkyv::{collections::swiss_table::ArchivedHashMap, string::ArchivedString};
4use serde::{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::{ArchivedRkyvValue, RkyvValue},
12    interned_values::InternedStore,
13    log_e,
14};
15
16const TAG: &str = "DynamicReturnable";
17
18lazy_static::lazy_static! {
19    static ref EMPTY_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
20        hash: 0,
21        value: DynamicReturnableValue::Null,
22    };
23
24    static ref TRUE_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
25        hash: 0,
26        value: DynamicReturnableValue::Bool(true),
27    };
28
29    static ref FALSE_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
30        hash: 0,
31        value: DynamicReturnableValue::Bool(false),
32    };
33
34}
35
36#[derive(Clone, PartialEq, Debug)]
37pub struct DynamicReturnable {
38    pub hash: u64,
39    pub value: DynamicReturnableValue,
40}
41
42impl DynamicReturnable {
43    pub fn empty() -> Self {
44        EMPTY_DYNAMIC_RETURNABLE.clone()
45    }
46
47    pub fn from_bool(value: bool) -> Self {
48        if value {
49            TRUE_DYNAMIC_RETURNABLE.clone()
50        } else {
51            FALSE_DYNAMIC_RETURNABLE.clone()
52        }
53    }
54
55    pub fn from_map(value: HashMap<String, JsonValue>) -> Self {
56        let raw_value = match to_raw_value(&value) {
57            Ok(raw_value) => raw_value,
58            Err(e) => {
59                log_e!(TAG, "Failed to convert map to raw value: {}", e);
60                return Self::empty();
61            }
62        };
63
64        InternedStore::get_or_intern_returnable(Cow::Owned(raw_value))
65    }
66
67    pub fn get_bool(&self) -> Option<bool> {
68        match self.value {
69            DynamicReturnableValue::Bool(value) => Some(value),
70            _ => None,
71        }
72    }
73
74    pub fn get_json_archived_ref(
75        &self,
76    ) -> Option<&'static ArchivedHashMap<ArchivedString, ArchivedRkyvValue>> {
77        match self.value {
78            DynamicReturnableValue::JsonArchived(v) => Some(v),
79            _ => None,
80        }
81    }
82
83    pub fn get_json_pointer_ref(&self) -> Option<&HashMap<String, RkyvValue>> {
84        match &self.value {
85            DynamicReturnableValue::JsonPointer(v) => Some(v.as_ref()),
86            DynamicReturnableValue::JsonStatic(v) => Some(v),
87            _ => None,
88        }
89    }
90
91    pub fn get_json(&self) -> Option<HashMap<String, JsonValue>> {
92        match &self.value {
93            DynamicReturnableValue::JsonPointer(v) => rkyv_hashmap_to_owned_json(v.as_ref()),
94            DynamicReturnableValue::JsonStatic(v) => rkyv_hashmap_to_owned_json(v),
95            DynamicReturnableValue::JsonArchived(v) => archived_hashmap_to_owned(v).map_or_else(
96                |e| {
97                    log_e!(TAG, "Failed to convert archived json. Error: {}", e);
98                    None
99                },
100                Some,
101            ),
102            _ => None,
103        }
104    }
105
106    pub fn get_hash(&self) -> u64 {
107        self.hash
108    }
109}
110
111impl<'de> Deserialize<'de> for DynamicReturnable {
112    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
113    where
114        D: Deserializer<'de>,
115    {
116        let raw_value_ref: Cow<RawValue> = Deserialize::deserialize(deserializer)?;
117        Ok(InternedStore::get_or_intern_returnable(raw_value_ref))
118    }
119}
120
121impl Serialize for DynamicReturnable {
122    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
123    where
124        S: Serializer,
125    {
126        match &self.value {
127            DynamicReturnableValue::JsonPointer(raw) => raw.serialize(serializer),
128            DynamicReturnableValue::JsonStatic(raw) => raw.serialize(serializer),
129            DynamicReturnableValue::JsonArchived(raw) => {
130                let owned = archived_hashmap_to_owned(raw).map_err(serde::ser::Error::custom)?;
131                owned.serialize(serializer)
132            }
133            DynamicReturnableValue::Null => serializer.serialize_none(),
134            DynamicReturnableValue::Bool(value) => serializer.serialize_bool(*value),
135        }
136    }
137}
138
139impl Drop for DynamicReturnable {
140    fn drop(&mut self) {
141        self.value = DynamicReturnableValue::Null;
142        InternedStore::release_returnable(self.hash);
143    }
144}
145
146// ------------------------------------------------------------------------------- [ DynamicReturnableValue ]
147
148#[derive(Clone, Debug)]
149pub enum DynamicReturnableValue {
150    Null,
151    Bool(bool),
152    JsonPointer(Arc<HashMap<String, RkyvValue>>),
153    JsonStatic(&'static HashMap<String, RkyvValue>),
154    JsonArchived(&'static ArchivedHashMap<ArchivedString, ArchivedRkyvValue>),
155}
156
157impl PartialEq for DynamicReturnableValue {
158    fn eq(&self, other: &Self) -> bool {
159        match (self, other) {
160            (DynamicReturnableValue::Null, DynamicReturnableValue::Null) => return true,
161            (DynamicReturnableValue::Bool(a), DynamicReturnableValue::Bool(b)) => return *a == *b,
162            _ => {}
163        };
164
165        if let DynamicReturnableValue::JsonPointer(a) = self {
166            match other {
167                DynamicReturnableValue::JsonPointer(b) => return a.as_ref() == b.as_ref(),
168                DynamicReturnableValue::JsonStatic(b) => return a.as_ref() == *b,
169                DynamicReturnableValue::JsonArchived(b) => return eq_check(b, a.as_ref()),
170                _ => return false,
171            }
172        }
173
174        if let DynamicReturnableValue::JsonStatic(a) = self {
175            match other {
176                DynamicReturnableValue::JsonPointer(b) => return *a == b.as_ref(),
177                DynamicReturnableValue::JsonStatic(b) => return a == b,
178                DynamicReturnableValue::JsonArchived(b) => return eq_check(b, a),
179                _ => return false,
180            }
181        }
182
183        if let DynamicReturnableValue::JsonArchived(a) = self {
184            match other {
185                DynamicReturnableValue::JsonPointer(b) => return eq_check(a, b.as_ref()),
186                DynamicReturnableValue::JsonStatic(b) => return eq_check(a, b),
187                DynamicReturnableValue::JsonArchived(b) => return a == b,
188                _ => return false,
189            }
190        }
191
192        false
193    }
194}
195
196// ------------------------------------------------------------------------------- [ Rkyv Helper ]
197
198fn eq_check(
199    left: &ArchivedHashMap<ArchivedString, ArchivedRkyvValue>,
200    right: &HashMap<String, RkyvValue>,
201) -> bool {
202    for (key, value) in left.iter() {
203        match right.get_key_value(key.as_str()) {
204            Some((left_key, left_value)) => {
205                if left_key != key {
206                    return false;
207                }
208                if left_value != value {
209                    return false;
210                }
211            }
212            None => return false,
213        };
214    }
215    true
216}
217
218fn rkyv_hashmap_to_owned_json(
219    raw: &HashMap<String, RkyvValue>,
220) -> Option<HashMap<String, JsonValue>> {
221    match serde_json::to_value(raw) {
222        Ok(JsonValue::Object(o)) => Some(o.into_iter().collect()),
223        Ok(_) => None,
224        Err(e) => {
225            log_e!(TAG, "Failed to convert json. Error: {}", e);
226            None
227        }
228    }
229}
230
231fn archived_hashmap_to_owned(
232    raw: &'static ArchivedHashMap<ArchivedString, ArchivedRkyvValue>,
233) -> Result<HashMap<String, JsonValue>, serde_json::Error> {
234    let mut taken: HashMap<String, JsonValue> = HashMap::new();
235    for (key, value) in raw.iter() {
236        taken.insert(key.as_str().to_string(), serde_json::to_value(value)?);
237    }
238
239    Ok(taken)
240}