statsig_rust/evaluation/
dynamic_returnable.rs1use std::{
2 collections::HashMap,
3 sync::{Arc, Mutex, Weak},
4};
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use serde_json::{value::RawValue, Value as JsonValue};
8
9use crate::{hashing::djb2, log_e};
10
11const TAG: &str = "DynamicReturnable";
12
13lazy_static::lazy_static! {
14 static ref MEMOIZED_VALUES: Mutex<HashMap<String, Weak<MemoizedValue>>> =
15 Mutex::new(HashMap::new());
16}
17
18#[derive(Clone, PartialEq, Debug)]
19pub struct DynamicReturnable {
20 hash: String,
21 value: Arc<MemoizedValue>,
22}
23
24impl DynamicReturnable {
25 pub fn get_bool(&self) -> Option<bool> {
26 self.value.bool_value
27 }
28
29 pub fn get_json(&self) -> Option<HashMap<String, JsonValue>> {
30 self.value.json_value.clone()
31 }
32
33 fn new(hash: String, value: Arc<MemoizedValue>) -> Self {
34 Self { hash, value }
35 }
36}
37
38impl<'de> Deserialize<'de> for DynamicReturnable {
39 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
40 where
41 D: Deserializer<'de>,
42 {
43 let raw_value: &'de RawValue = Deserialize::deserialize(deserializer)?;
44 let raw_value_str = raw_value.get();
45 let hash = djb2(raw_value_str);
46
47 if let Some(value) = get_memoized_value(&hash) {
48 return Ok(DynamicReturnable { hash, value });
49 }
50
51 let value = MemoizedValue::new(raw_value_str);
52 let weak_value = Arc::downgrade(&value);
53
54 let new_returnable = DynamicReturnable::new(hash.clone(), value);
55 set_memoized_value(&hash, weak_value);
56
57 Ok(new_returnable)
58 }
59}
60
61impl Serialize for DynamicReturnable {
62 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
63 where
64 S: Serializer,
65 {
66 if let Some(bool_value) = self.value.bool_value {
67 return bool_value.serialize(serializer);
68 }
69
70 if let Some(json_value) = &self.value.json_value {
71 return json_value.serialize(serializer);
72 }
73
74 JsonValue::Null.serialize(serializer)
75 }
76}
77
78impl Drop for DynamicReturnable {
79 fn drop(&mut self) {
80 let mut memo = match MEMOIZED_VALUES.lock() {
81 Ok(values) => values,
82 Err(e) => {
83 log_e!(TAG, "Failed to lock memoized values: {}", e);
84 return;
85 }
86 };
87
88 let found = match memo.get(&self.hash) {
89 Some(value) => value,
90 None => return,
91 };
92
93 if found.strong_count() == 1 {
94 memo.remove(&self.hash);
95 }
96 }
97}
98
99#[derive(Debug, Clone, PartialEq)]
100struct MemoizedValue {
101 bool_value: Option<bool>,
102 json_value: Option<HashMap<String, JsonValue>>,
103}
104
105impl MemoizedValue {
106 fn new(raw_json: &str) -> Arc<Self> {
107 let value = match raw_json {
108 "true" | "false" => Self::from_bool(raw_json == "true"),
109 raw_json => Self::from_object_str(raw_json),
110 };
111
112 Arc::new(value)
113 }
114
115 fn from_bool(bool_value: bool) -> Self {
116 Self {
117 bool_value: Some(bool_value),
118 json_value: None,
119 }
120 }
121
122 fn from_object_str(raw_json: &str) -> Self {
123 let json_value = match serde_json::from_str(raw_json) {
124 Ok(json_value) => json_value,
125 Err(e) => {
126 log_e!(TAG, "Failed to parse json: {}", e);
127 None
128 }
129 };
130
131 Self {
132 bool_value: None,
133 json_value,
134 }
135 }
136}
137
138fn get_memoized_value(hash: &str) -> Option<Arc<MemoizedValue>> {
139 let mut memoized_values = match MEMOIZED_VALUES.lock() {
140 Ok(values) => values,
141 Err(e) => {
142 log_e!(TAG, "Failed to lock memoized values: {}", e);
143 return None;
144 }
145 };
146
147 let found = memoized_values.get(hash)?;
148
149 match found.upgrade() {
150 Some(value) => Some(value),
151 None => {
152 memoized_values.remove(hash);
153 None
154 }
155 }
156}
157
158fn set_memoized_value(hash: &str, value: Weak<MemoizedValue>) {
159 match MEMOIZED_VALUES.lock() {
160 Ok(mut values) => {
161 values.insert(hash.to_string(), value);
162 }
163 Err(e) => {
164 log_e!(TAG, "Failed to lock memoized values: {}", e);
165 }
166 };
167}