statsig_rust/evaluation/
dynamic_returnable.rs1use crate::{hashing::djb2, log_e};
2use parking_lot::Mutex;
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use serde_json::{
5 value::{to_raw_value, RawValue},
6 Value as JsonValue,
7};
8use std::{
9 collections::HashMap,
10 sync::{Arc, Weak},
11 time::Duration,
12};
13
14const TAG: &str = "DynamicReturnable";
15
16lazy_static::lazy_static! {
17 pub(crate) static ref MEMOIZED_VALUES: Mutex<HashMap<String, Weak<MemoizedValue>>> =
18 Mutex::new(HashMap::new());
19
20 static ref EMPTY_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
21 hash: "".to_string(),
22 value: Arc::new(MemoizedValue {
23 raw_value: None,
24 bool_value: None,
25 json_value: None,
26 }),
27 };
28}
29
30#[derive(Clone, PartialEq, Debug)]
31pub struct DynamicReturnable {
32 hash: String,
33 value: Arc<MemoizedValue>,
34}
35
36impl DynamicReturnable {
37 pub fn empty() -> Self {
38 EMPTY_DYNAMIC_RETURNABLE.clone()
39 }
40
41 pub fn from_map(value: HashMap<String, JsonValue>) -> Self {
42 let raw_value = match to_raw_value(&value) {
43 Ok(raw_value) => raw_value,
44 Err(e) => {
45 log_e!(TAG, "Failed to convert map to raw value: {}", e);
46 return Self::empty();
47 }
48 };
49
50 let hash = djb2(raw_value.get());
51 let value = Arc::new(MemoizedValue {
52 raw_value: Some(raw_value),
53 bool_value: None,
54 json_value: Some(value.clone()),
55 });
56
57 Self::new(hash.to_string(), value)
58 }
59
60 pub fn get_bool(&self) -> Option<bool> {
61 self.value.bool_value
62 }
63
64 pub fn get_json(&self) -> Option<HashMap<String, JsonValue>> {
65 self.value.json_value.clone()
66 }
67
68 pub fn get_json_ref(&self) -> Option<&HashMap<String, JsonValue>> {
69 self.value.json_value.as_ref()
70 }
71
72 fn new(hash: String, value: Arc<MemoizedValue>) -> Self {
73 let weak_value = Arc::downgrade(&value);
74 set_memoized_value(&hash, weak_value);
75
76 Self { hash, value }
77 }
78}
79
80impl<'de> Deserialize<'de> for DynamicReturnable {
81 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
82 where
83 D: Deserializer<'de>,
84 {
85 let raw_value_ref: &'de RawValue = Deserialize::deserialize(deserializer)?;
86
87 let raw_value_str = raw_value_ref.get();
88 let hash = djb2(raw_value_str);
89
90 if let Some(value) = get_memoized_value(&hash) {
91 return Ok(DynamicReturnable { hash, value });
92 }
93
94 let raw_value = raw_value_ref.to_owned();
95 let value = MemoizedValue::new(raw_value);
96
97 let new_returnable = DynamicReturnable::new(hash.clone(), value);
98
99 Ok(new_returnable)
100 }
101}
102
103impl Serialize for DynamicReturnable {
104 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
105 where
106 S: Serializer,
107 {
108 if let Some(bool_value) = self.value.bool_value {
109 return bool_value.serialize(serializer);
110 }
111
112 if let Some(raw_value) = &self.value.raw_value {
113 return raw_value.serialize(serializer);
114 }
115
116 if let Some(json_value) = &self.value.json_value {
117 return json_value.serialize(serializer);
118 }
119
120 JsonValue::Null.serialize(serializer)
121 }
122}
123
124impl Drop for DynamicReturnable {
125 fn drop(&mut self) {
126 let mut memo = match MEMOIZED_VALUES.try_lock_for(Duration::from_secs(5)) {
127 Some(values) => values,
128 None => {
129 log_e!(
130 TAG,
131 "Failed to lock memoized values: Failed to lock MEMOIZED_VALUES"
132 );
133 return;
134 }
135 };
136
137 let found = match memo.get(&self.hash) {
138 Some(value) => value,
139 None => return,
140 };
141
142 if found.strong_count() == 1 {
143 memo.remove(&self.hash);
144 }
145 }
146}
147
148#[derive(Debug, Clone)]
149pub(crate) struct MemoizedValue {
150 pub(crate) raw_value: Option<Box<RawValue>>,
151 pub(crate) bool_value: Option<bool>,
152 pub(crate) json_value: Option<HashMap<String, JsonValue>>,
153}
154
155impl PartialEq for MemoizedValue {
156 fn eq(&self, other: &Self) -> bool {
157 self.raw_value.as_ref().map(|v| v.get()) == other.raw_value.as_ref().map(|v| v.get())
158 && self.bool_value == other.bool_value
159 && self.json_value == other.json_value
160 }
161}
162
163impl MemoizedValue {
164 fn new(raw_value: Box<RawValue>) -> Arc<Self> {
165 let value = match raw_value.get() {
166 "true" => Self::from_bool(true),
167 "false" => Self::from_bool(false),
168 _ => Self::from_raw_value(raw_value),
169 };
170
171 Arc::new(value)
172 }
173
174 fn from_bool(bool_value: bool) -> Self {
175 Self {
176 raw_value: None,
177 bool_value: Some(bool_value),
178 json_value: None,
179 }
180 }
181
182 fn from_raw_value(raw_value: Box<RawValue>) -> Self {
183 let json_value = match serde_json::from_str(raw_value.get()) {
184 Ok(json_value) => json_value,
185 Err(e) => {
186 log_e!(TAG, "Failed to parse json: {}", e);
187 None
188 }
189 };
190
191 Self {
192 raw_value: Some(raw_value),
193 bool_value: None,
194 json_value,
195 }
196 }
197}
198
199fn get_memoized_value(hash: &str) -> Option<Arc<MemoizedValue>> {
200 let mut memoized_values = match MEMOIZED_VALUES.try_lock_for(Duration::from_secs(5)) {
201 Some(values) => values,
202 None => {
203 log_e!(
204 TAG,
205 "Failed to lock memoized values: Failed to lock MEMOIZED_VALUES"
206 );
207 return None;
208 }
209 };
210
211 let found = memoized_values.get(hash)?;
212
213 match found.upgrade() {
214 Some(value) => Some(value),
215 None => {
216 memoized_values.remove(hash);
217 None
218 }
219 }
220}
221
222fn set_memoized_value(hash: &str, value: Weak<MemoizedValue>) {
223 match MEMOIZED_VALUES.try_lock_for(Duration::from_secs(5)) {
224 Some(mut values) => {
225 values.insert(hash.to_string(), value);
226 }
227 None => {
228 log_e!(
229 TAG,
230 "Failed to lock memoized values: Failed to lock MEMOIZED_VALUES"
231 );
232 }
233 };
234}