launchdarkly_server_sdk_evaluation/
flag_value.rs1use log::warn;
2use serde::{Deserialize, Serialize};
3
4use crate::util::f64_to_i64_safe;
5
6#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
9#[serde(untagged)]
10pub enum FlagValue {
11 Bool(bool),
13 Str(String),
15 Number(f64),
17 Json(serde_json::Value),
19}
20
21impl From<bool> for FlagValue {
22 fn from(b: bool) -> FlagValue {
23 FlagValue::Bool(b)
24 }
25}
26
27impl From<String> for FlagValue {
28 fn from(s: String) -> FlagValue {
29 FlagValue::Str(s)
30 }
31}
32
33impl From<f64> for FlagValue {
34 fn from(f: f64) -> FlagValue {
35 FlagValue::Number(f)
36 }
37}
38
39impl From<i64> for FlagValue {
40 fn from(i: i64) -> FlagValue {
41 FlagValue::Number(i as f64)
42 }
43}
44
45impl From<serde_json::Value> for FlagValue {
46 fn from(v: serde_json::Value) -> Self {
47 use serde_json::Value;
48 match v {
49 Value::Bool(b) => b.into(),
50 Value::Number(n) => {
51 if let Some(f) = n.as_f64() {
52 f.into()
53 } else {
54 warn!("unrepresentable number {}, converting to string", n);
55 FlagValue::Json(format!("{}", n).into())
56 }
57 }
58 Value::String(s) => s.into(),
59 Value::Null | Value::Object(_) | Value::Array(_) => FlagValue::Json(v),
60 }
61 }
62}
63
64impl FlagValue {
65 pub fn as_bool(&self) -> Option<bool> {
68 match self {
69 FlagValue::Bool(b) => Some(*b),
70 _ => {
71 warn!("variation type is not bool but {:?}", self);
72 None
73 }
74 }
75 }
76
77 pub fn as_string(&self) -> Option<String> {
80 match self {
81 FlagValue::Str(s) => Some(s.clone()),
82 _ => {
83 warn!("variation type is not str but {:?}", self);
84 None
85 }
86 }
87 }
88
89 pub fn as_float(&self) -> Option<f64> {
92 match self {
93 FlagValue::Number(f) => Some(*f),
94 _ => {
95 warn!("variation type is not number but {:?}", self);
96 None
97 }
98 }
99 }
100
101 pub fn as_int(&self) -> Option<i64> {
104 match self {
105 FlagValue::Number(f) => f64_to_i64_safe(*f),
106 _ => {
107 warn!("variation type is not number but {:?}", self);
108 None
109 }
110 }
111 }
112
113 pub fn as_json(&self) -> Option<serde_json::Value> {
116 use serde_json::Value;
117 match self {
118 FlagValue::Bool(b) => Some(Value::from(*b)),
119 FlagValue::Str(s) => Some(Value::from(s.as_str())),
120 FlagValue::Number(f) => Some(Value::from(*f)),
121 FlagValue::Json(v) => Some(v.clone()),
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use serde_json::json;
130 use spectral::prelude::*;
131
132 #[test]
133 fn float_bounds() {
134 let test_cases = vec![
135 (1.99, Some(1)),
136 (9007199254740990.0, Some(9007199254740990)),
137 (9007199254740991.0, Some(9007199254740991)),
138 (9007199254740992.0, None),
139 (-1.99, Some(-1)),
140 (-9007199254740990.0, Some(-9007199254740990)),
141 (-9007199254740991.0, Some(-9007199254740991)),
142 (-9007199254740992.0, None),
143 ];
144 for (have, expect) in test_cases {
145 assert_that!(FlagValue::Number(have).as_int()).is_equal_to(expect);
146 }
147 }
148
149 #[test]
150 fn deserialization() {
151 fn test_case(json: &str, expected: FlagValue) {
152 assert_eq!(serde_json::from_str::<FlagValue>(json).unwrap(), expected);
153 }
154
155 test_case("1.0", FlagValue::Number(1.0));
156 test_case("1", FlagValue::Number(1.0));
157 test_case("true", FlagValue::Bool(true));
158 test_case("\"foo\"", FlagValue::Str("foo".to_string()));
159 test_case("{}", FlagValue::Json(json!({})));
160 }
161
162 #[test]
163 fn can_handle_converting_between_types() {
164 let value: FlagValue = true.into();
165 assert_eq!(Some(true), value.as_bool());
166 assert!(value.as_string().is_none());
167 assert!(value.as_float().is_none());
168 assert!(value.as_float().is_none());
169 assert!(value.as_int().is_none());
170
171 let value: FlagValue = String::from("testing").into();
172 assert!(value.as_bool().is_none());
173 assert_eq!(Some(String::from("testing")), value.as_string());
174 assert!(value.as_float().is_none());
175 assert!(value.as_float().is_none());
176 assert!(value.as_int().is_none());
177
178 let value: FlagValue = 1_f64.into();
179 assert!(value.as_bool().is_none());
180 assert!(value.as_string().is_none());
181 assert_eq!(Some(1_f64), value.as_float());
182 assert_eq!(Some(1_i64), value.as_int());
183
184 let value: FlagValue = 1_i64.into();
185 assert!(value.as_bool().is_none());
186 assert!(value.as_string().is_none());
187 assert_eq!(Some(1_f64), value.as_float());
188 assert_eq!(Some(1_i64), value.as_int());
189
190 let value: FlagValue = serde_json::Value::Bool(true).into();
191 assert_eq!(Some(true), value.as_bool());
192 assert_eq!(Some(serde_json::Value::Bool(true)), value.as_json());
193
194 let value: FlagValue = serde_json::Value::String("testing".to_string()).into();
195 assert_eq!(Some(String::from("testing")), value.as_string());
196 assert_eq!(
197 Some(serde_json::Value::String("testing".to_string())),
198 value.as_json()
199 );
200
201 let value: FlagValue = json!(1_f64).into();
202 assert_eq!(Some(1_f64), value.as_float());
203 assert_eq!(Some(json!(1_f64)), value.as_json());
204
205 let value: FlagValue = serde_json::Value::Array(vec![serde_json::Value::Bool(true)]).into();
206 assert_eq!(
207 Some(serde_json::Value::Array(vec![serde_json::Value::Bool(
208 true
209 )])),
210 value.as_json()
211 );
212 }
213}