statsig_rust/evaluation/
dynamic_value.rs1use chrono::{DateTime, NaiveDateTime};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use serde_json::{json, Value as JsonValue};
4use std::{collections::HashMap, fmt::Debug};
5
6use crate::hashing::ahash_str;
7
8use super::dynamic_string::DynamicString;
9
10#[macro_export]
11macro_rules! dyn_value {
12 ($x:expr) => {{
13 $crate::DynamicValue::from_json_value($x)
14 }};
15}
16
17#[derive(Debug, Clone, Default)]
18pub struct DynamicValue {
19 pub null: Option<()>,
20 pub bool_value: Option<bool>,
21 pub int_value: Option<i64>,
22 pub float_value: Option<f64>,
23 pub timestamp_value: Option<i64>,
24 pub string_value: Option<DynamicString>,
25 pub array_value: Option<Vec<DynamicValue>>,
26 pub object_value: Option<HashMap<String, DynamicValue>>,
27 pub json_value: JsonValue,
28 pub hash_value: u64,
29}
30
31impl DynamicValue {
32 #[must_use]
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 #[must_use]
38 pub fn from_json_value(value: impl Serialize) -> Self {
39 Self::from(json!(value))
40 }
41
42 #[must_use]
43 pub fn for_timestamp_evaluation(timestamp: i64) -> DynamicValue {
44 DynamicValue {
45 int_value: Some(timestamp),
46 ..DynamicValue::default()
47 }
48 }
49
50 fn try_parse_timestamp(s: &str) -> Option<i64> {
51 if let Ok(ts) = s.parse::<i64>() {
53 return Some(ts);
54 }
55
56 if s.len() < 8 || s.len() > 20 || (!s.contains('-') && !s.contains('T') && !s.contains(':'))
58 {
59 return None;
60 }
61
62 if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
64 return Some(dt.timestamp_millis());
65 }
66
67 if let Ok(ndt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
69 return Some(ndt.and_utc().timestamp_millis());
70 }
71
72 None
73 }
74}
75
76impl PartialEq for DynamicValue {
77 fn eq(&self, other: &Self) -> bool {
78 self.null == other.null
79 && self.bool_value == other.bool_value
80 && self.int_value == other.int_value
81 && self.float_value == other.float_value
82 && self.string_value == other.string_value
83 && self.array_value == other.array_value
84 && self.object_value == other.object_value
85 }
86}
87
88impl Serialize for DynamicValue {
91 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
92 where
93 S: Serializer,
94 {
95 self.json_value.serialize(serializer)
96 }
97}
98
99impl<'de> Deserialize<'de> for DynamicValue {
100 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
101 where
102 D: Deserializer<'de>,
103 {
104 let json_value = JsonValue::deserialize(deserializer)?;
105 Ok(DynamicValue::from(json_value))
106 }
107}
108
109impl From<JsonValue> for DynamicValue {
112 fn from(json_value: JsonValue) -> Self {
113 let mut stringified_json_value = None;
115 let hash_value = if let JsonValue::String(s) = &json_value {
116 ahash_str(s)
117 } else {
118 let actual = json_value.to_string();
119 let hash = ahash_str(&actual);
120 stringified_json_value = Some(actual);
121 hash
122 };
123
124 match &json_value {
125 JsonValue::Null => DynamicValue {
126 null: Some(()),
127 json_value,
128 hash_value,
129 ..DynamicValue::new()
130 },
131
132 JsonValue::Bool(b) => DynamicValue {
133 bool_value: Some(*b),
134 string_value: Some(DynamicString::from(b.to_string())),
135 json_value,
136 hash_value,
137 ..DynamicValue::new()
138 },
139
140 JsonValue::Number(n) => {
141 let mut float_value = n.as_f64();
142 let mut int_value = n.as_i64();
143 if let (Some(f), None) = (float_value, int_value) {
144 let iv = f as i64;
145 if iv as f64 == f {
146 int_value = Some(iv);
147 }
148 } else if let (None, Some(i)) = (float_value, int_value) {
149 let fv = i as f64;
150 if fv as i64 == i {
151 float_value = Some(fv)
152 }
153 }
154
155 let string_value = float_value
156 .map(|f| f.to_string())
157 .or_else(|| int_value.map(|i| i.to_string()))
158 .or(stringified_json_value);
159
160 DynamicValue {
161 float_value,
162 int_value,
163 string_value: string_value.map(DynamicString::from),
164 json_value,
165 hash_value,
166 ..DynamicValue::new()
167 }
168 }
169
170 JsonValue::String(s) => {
171 let timestamp_value = Self::try_parse_timestamp(s);
172 let float_value = s.parse().ok();
173 let int_value = s.parse().ok();
174 DynamicValue {
175 string_value: Some(DynamicString::from(s.clone())),
176 json_value,
177 timestamp_value,
178 int_value,
179 float_value,
180 hash_value,
181 ..DynamicValue::new()
182 }
183 }
184
185 JsonValue::Array(arr) => DynamicValue {
186 array_value: Some(arr.iter().map(|v| DynamicValue::from(v.clone())).collect()),
187 string_value: Some(DynamicString::from(
188 stringified_json_value.unwrap_or(json_value.to_string()),
189 )),
190 json_value,
191 hash_value,
192 ..DynamicValue::new()
193 },
194
195 JsonValue::Object(obj) => DynamicValue {
196 object_value: Some(
197 obj.into_iter()
198 .map(|(k, v)| (k.clone(), DynamicValue::from(v.clone())))
199 .collect(),
200 ),
201 json_value,
202 hash_value,
203 ..DynamicValue::new()
204 },
205 }
206 }
207}
208
209impl From<String> for DynamicValue {
210 fn from(value: String) -> Self {
211 Self::from(json!(value))
212 }
213}
214
215impl From<&str> for DynamicValue {
216 fn from(value: &str) -> Self {
217 Self::from(json!(value))
218 }
219}
220
221impl From<usize> for DynamicValue {
222 fn from(value: usize) -> Self {
223 Self::from(json!(value))
224 }
225}
226
227impl From<i64> for DynamicValue {
228 fn from(value: i64) -> Self {
229 Self::from(json!(value))
230 }
231}
232
233impl From<i32> for DynamicValue {
234 fn from(value: i32) -> Self {
235 Self::from(json!(value))
236 }
237}
238
239impl From<f64> for DynamicValue {
240 fn from(value: f64) -> Self {
241 Self::from(json!(value))
242 }
243}
244
245impl From<bool> for DynamicValue {
246 fn from(value: bool) -> Self {
247 Self::from(json!(value))
248 }
249}
250
251impl From<Vec<JsonValue>> for DynamicValue {
252 fn from(value: Vec<JsonValue>) -> Self {
253 DynamicValue::from(json!(value))
254 }
255}