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;
5
6use super::dynamic_string::DynamicString;
7
8#[macro_export]
9macro_rules! dyn_value {
10 ($x:expr) => {
11 $crate::DynamicValue::from($x)
12 };
13}
14
15#[derive(Debug, Clone, Default)]
16pub struct DynamicValue {
17 pub null: Option<()>,
18 pub bool_value: Option<bool>,
19 pub int_value: Option<i64>,
20 pub float_value: Option<f64>,
21 pub timestamp_value: Option<i64>,
22 pub string_value: Option<DynamicString>,
23 pub array_value: Option<Vec<DynamicValue>>,
24 pub object_value: Option<HashMap<String, DynamicValue>>,
25 pub json_value: JsonValue,
26}
27
28impl From<String> for DynamicValue {
29 fn from(value: String) -> Self {
30 let json_value = json!(value);
31 let float_value = value.parse().ok();
32 let int_value = value.parse().ok();
33
34 let timestamp_value = Self::try_parse_timestamp(&value);
35
36 DynamicValue {
37 float_value,
38 int_value,
39 json_value,
40 timestamp_value,
41 string_value: Some(DynamicString::from(value)),
42 ..Self::default()
43 }
44 }
45}
46
47impl From<&str> for DynamicValue {
48 fn from(value: &str) -> Self {
49 DynamicValue::from(value.to_string())
50 }
51}
52
53impl From<usize> for DynamicValue {
54 fn from(value: usize) -> Self {
55 DynamicValue {
56 json_value: json!(value),
57 int_value: Some(value as i64),
58 float_value: Some(value as f64),
59 string_value: Some(DynamicString::from(value.to_string())),
60 ..Self::default()
61 }
62 }
63}
64
65impl From<i64> for DynamicValue {
66 fn from(value: i64) -> Self {
67 DynamicValue {
68 int_value: Some(value),
69 float_value: Some(value as f64),
70 string_value: Some(DynamicString::from(value.to_string())),
71 json_value: json!(value),
72 ..Self::default()
73 }
74 }
75}
76
77impl From<i32> for DynamicValue {
78 fn from(value: i32) -> Self {
79 Self::from(i64::from(value))
80 }
81}
82
83impl From<f64> for DynamicValue {
84 fn from(value: f64) -> Self {
85 DynamicValue {
86 int_value: Some(value as i64),
87 float_value: Some(value),
88 string_value: Some(DynamicString::from(value.to_string())),
89 json_value: json!(value),
90 ..Self::default()
91 }
92 }
93}
94
95impl From<bool> for DynamicValue {
96 fn from(value: bool) -> Self {
97 DynamicValue {
98 bool_value: Some(value),
99 string_value: Some(DynamicString::from(value.to_string())),
100 json_value: json!(value),
101 ..Self::default()
102 }
103 }
104}
105
106impl From<Vec<JsonValue>> for DynamicValue {
107 fn from(value: Vec<JsonValue>) -> Self {
108 DynamicValue::from(json!(value))
109 }
110}
111
112impl From<JsonValue> for DynamicValue {
113 fn from(value: JsonValue) -> Self {
114 let json_value = value.clone();
115 match value {
116 JsonValue::Null => DynamicValue {
117 null: Some(()),
118 json_value,
119 ..DynamicValue::new()
120 },
121 JsonValue::Bool(b) => DynamicValue {
122 bool_value: Some(b),
123 string_value: Some(DynamicString::from(b.to_string())),
124 json_value,
125 ..DynamicValue::new()
126 },
127 JsonValue::Number(n) => DynamicValue {
128 float_value: n.as_f64(),
129 int_value: n.as_i64(),
130 string_value: Some(DynamicString::from(json_value.to_string())),
131 json_value,
132 ..DynamicValue::new()
133 },
134 JsonValue::String(s) => DynamicValue::from(s),
135 JsonValue::Array(arr) => DynamicValue {
136 array_value: Some(arr.into_iter().map(DynamicValue::from).collect()),
137 string_value: Some(DynamicString::from(json_value.to_string())),
138 json_value,
139 ..DynamicValue::new()
140 },
141 JsonValue::Object(obj) => DynamicValue {
142 object_value: Some(
143 obj.into_iter()
144 .map(|(k, v)| (k, DynamicValue::from(v)))
145 .collect(),
146 ),
147 json_value,
148 ..DynamicValue::new()
149 },
150 }
151 }
152}
153
154impl DynamicValue {
155 #[must_use]
156 pub fn new() -> Self {
157 Self::default()
158 }
159 pub fn from<T: Into<DynamicValue>>(value: T) -> Self {
160 value.into()
161 }
162
163 #[must_use]
164 pub fn for_timestamp_evaluation(timestamp: i64) -> DynamicValue {
165 DynamicValue {
166 int_value: Some(timestamp),
167 ..DynamicValue::default()
168 }
169 }
170
171 fn try_parse_timestamp(s: &str) -> Option<i64> {
172 if let Ok(ts) = s.parse::<i64>() {
173 return Some(ts);
174 }
175
176 if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
177 return Some(dt.timestamp_millis());
178 }
179
180 if let Ok(ndt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
181 return Some(ndt.and_utc().timestamp_millis());
182 }
183
184 None
185 }
186}
187
188impl PartialEq for DynamicValue {
189 fn eq(&self, other: &Self) -> bool {
190 self.null == other.null
191 && self.bool_value == other.bool_value
192 && self.int_value == other.int_value
193 && self.float_value == other.float_value
194 && self.string_value == other.string_value
195 && self.array_value == other.array_value
196 && self.object_value == other.object_value
197 }
198}
199
200impl Serialize for DynamicValue {
201 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
202 where
203 S: Serializer,
204 {
205 self.json_value.serialize(serializer)
206 }
207}
208
209impl<'de> Deserialize<'de> for DynamicValue {
210 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
211 where
212 D: Deserializer<'de>,
213 {
214 let json_value = JsonValue::deserialize(deserializer)?;
215 Ok(DynamicValue::from(json_value))
216 }
217}