statsig_rust/evaluation/
dynamic_value.rs1use regex_lite::Regex;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use serde_json::{json, Value as JsonValue};
4use std::collections::HashMap;
5
6#[macro_export]
7macro_rules! dyn_value {
8 ($x:expr) => {
9 DynamicValue::from($x)
10 };
11}
12
13#[derive(Debug, Clone, Default)]
14pub struct DynamicValue {
15 pub null: Option<()>,
16 pub bool_value: Option<bool>,
17 pub int_value: Option<i64>,
18 pub float_value: Option<f64>,
19 pub string_value: Option<String>,
20 pub lowercase_string_value: Option<String>,
21 pub array_value: Option<Vec<DynamicValue>>,
22 pub object_value: Option<HashMap<String, DynamicValue>>,
23 pub regex_value: Option<Regex>,
24 pub json_value: JsonValue,
25}
26
27impl From<String> for DynamicValue {
28 fn from(value: String) -> Self {
29 let json_value = json!(value);
30 let float_value = value.parse().ok();
31 let int_value = value.parse().ok();
32 DynamicValue {
33 float_value,
34 int_value,
35 json_value,
36 lowercase_string_value: Some(value.to_lowercase()),
37 string_value: Some(value),
38 ..Self::default()
39 }
40 }
41}
42
43impl From<&str> for DynamicValue {
44 fn from(value: &str) -> Self {
45 DynamicValue::from(value.to_string())
46 }
47}
48
49impl From<usize> for DynamicValue {
50 fn from(value: usize) -> Self {
51 DynamicValue {
52 json_value: json!(value),
53 int_value: Some(value as i64),
54 float_value: Some(value as f64),
55 string_value: Some(value.to_string()),
56 lowercase_string_value: Some(value.to_string()),
57 ..Self::default()
58 }
59 }
60}
61
62impl From<i64> for DynamicValue {
63 fn from(value: i64) -> Self {
64 DynamicValue {
65 int_value: Some(value),
66 float_value: Some(value as f64),
67 string_value: Some(value.to_string()),
68 lowercase_string_value: Some(value.to_string()),
69 json_value: json!(value),
70 ..Self::default()
71 }
72 }
73}
74
75impl From<i32> for DynamicValue {
76 fn from(value: i32) -> Self {
77 Self::from(i64::from(value))
78 }
79}
80
81impl From<f64> for DynamicValue {
82 fn from(value: f64) -> Self {
83 DynamicValue {
84 int_value: Some(value as i64),
85 float_value: Some(value),
86 string_value: Some(value.to_string()),
87 lowercase_string_value: Some(value.to_string()),
88 json_value: json!(value),
89 ..Self::default()
90 }
91 }
92}
93
94impl From<bool> for DynamicValue {
95 fn from(value: bool) -> Self {
96 DynamicValue {
97 bool_value: Some(value),
98 string_value: Some(value.to_string()),
99 lowercase_string_value: Some(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(b.to_string()),
124 json_value,
125 lowercase_string_value: Some(b.to_string().to_lowercase()),
126 ..DynamicValue::new()
127 },
128 JsonValue::Number(n) => DynamicValue {
129 float_value: n.as_f64(),
130 int_value: n.as_i64(),
131 string_value: Some(json_value.to_string()),
132 lowercase_string_value: Some(json_value.to_string()),
133 json_value,
134 ..DynamicValue::new()
135 },
136 JsonValue::String(s) => DynamicValue::from(s),
137 JsonValue::Array(arr) => DynamicValue {
138 array_value: Some(arr.into_iter().map(DynamicValue::from).collect()),
139 string_value: Some(json_value.to_string()),
140 lowercase_string_value: Some(json_value.to_string().to_lowercase()),
141 json_value,
142 ..DynamicValue::new()
143 },
144 JsonValue::Object(obj) => DynamicValue {
145 object_value: Some(
146 obj.into_iter()
147 .map(|(k, v)| (k, DynamicValue::from(v)))
148 .collect(),
149 ),
150 json_value,
151 ..DynamicValue::new()
152 },
153 }
154 }
155}
156
157impl DynamicValue {
158 #[must_use]
159 pub fn new() -> Self {
160 Self::default()
161 }
162 pub fn from<T: Into<DynamicValue>>(value: T) -> Self {
163 value.into()
164 }
165
166 #[must_use]
167 pub fn for_timestamp_evaluation(timestamp: i64) -> DynamicValue {
168 DynamicValue {
169 int_value: Some(timestamp),
170 ..DynamicValue::default()
171 }
172 }
173
174 pub fn compile_regex(&mut self) {
175 if let Some(value) = &self.string_value {
176 if let Ok(regex) = Regex::new(value) {
177 self.regex_value = Some(regex);
178 }
179 }
180 }
181}
182
183impl PartialEq for DynamicValue {
184 fn eq(&self, other: &Self) -> bool {
185 self.null == other.null
186 && self.bool_value == other.bool_value
187 && self.int_value == other.int_value
188 && self.float_value == other.float_value
189 && self.string_value == other.string_value
190 && self.array_value == other.array_value
191 && self.object_value == other.object_value
192 }
193}
194
195impl Serialize for DynamicValue {
196 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
197 where
198 S: Serializer,
199 {
200 self.json_value.serialize(serializer)
201 }
202}
203
204impl<'de> Deserialize<'de> for DynamicValue {
205 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206 where
207 D: Deserializer<'de>,
208 {
209 let json_value = JsonValue::deserialize(deserializer)?;
210 Ok(DynamicValue::from(json_value))
211 }
212}