statsig_rust/evaluation/
dynamic_value.rs

1use 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}