open_feature/evaluation/
value.rs

1use std::collections::HashMap;
2
3/// Hold a value in the evaluation result of supported types.
4#[derive(Clone, PartialEq, Debug)]
5#[allow(missing_docs)]
6pub enum Value {
7    Bool(bool),
8    Int(i64),
9    Float(f64),
10    String(String),
11    Array(Vec<Value>),
12    Struct(StructValue),
13}
14
15/// Supported types of values.
16/// [spec](https://openfeature.dev/specification/types).
17#[derive(Clone, PartialEq, Debug)]
18#[allow(missing_docs)]
19pub enum Type {
20    Bool,
21    Int,
22    Float,
23    String,
24    Array,
25    Struct,
26}
27
28/// Represent a structure value as defined in the
29/// [spec](https://openfeature.dev/specification/types#structure).
30#[derive(Clone, Default, PartialEq, Debug)]
31pub struct StructValue {
32    /// The fields of struct as key-value pairs.
33    pub fields: HashMap<String, Value>,
34}
35
36impl Value {
37    /// Return `true` if this is a bool value.
38    pub fn is_bool(&self) -> bool {
39        matches!(self, Self::Bool(_))
40    }
41
42    /// Try to convert `self` to bool.
43    pub fn as_bool(&self) -> Option<bool> {
44        match self {
45            Self::Bool(value) => Some(*value),
46            _ => None,
47        }
48    }
49
50    /// Return `true` if this is an int value.
51    pub fn is_i64(&self) -> bool {
52        matches!(self, Self::Int(_))
53    }
54
55    /// Try to convert `self` to int.
56    pub fn as_i64(&self) -> Option<i64> {
57        match self {
58            Self::Int(value) => Some(*value),
59            _ => None,
60        }
61    }
62
63    /// Return `true` if this is a float value.
64    pub fn is_f64(&self) -> bool {
65        matches!(self, Self::Float(_))
66    }
67
68    /// Try to convert `self` to float.
69    pub fn as_f64(&self) -> Option<f64> {
70        match self {
71            Self::Float(value) => Some(*value),
72            _ => None,
73        }
74    }
75
76    /// Return `true` if this is a string value.
77    pub fn is_str(&self) -> bool {
78        matches!(self, Self::String(_))
79    }
80
81    /// Try to convert `self` to str.
82    pub fn as_str(&self) -> Option<&str> {
83        match self {
84            Self::String(value) => Some(value),
85            _ => None,
86        }
87    }
88
89    /// Return `true` if this is an array.
90    pub fn is_array(&self) -> bool {
91        matches!(self, Self::Array(_))
92    }
93
94    /// Try to convert `self` to vector.
95    pub fn as_array(&self) -> Option<&Vec<Value>> {
96        match self {
97            Self::Array(value) => Some(value),
98            _ => None,
99        }
100    }
101
102    /// Return `true` if this is a struct.
103    pub fn is_struct(&self) -> bool {
104        matches!(self, Self::Struct(_))
105    }
106
107    /// Try to convert `self` to [`StructValue`].
108    pub fn as_struct(&self) -> Option<&StructValue> {
109        match self {
110            Self::Struct(value) => Some(value),
111            _ => None,
112        }
113    }
114
115    /// Return the type of the value.
116    pub fn get_type(&self) -> Type {
117        match self {
118            Self::Bool(_) => Type::Bool,
119            Self::Int(_) => Type::Int,
120            Self::Float(_) => Type::Float,
121            Self::String(_) => Type::String,
122            Self::Array(_) => Type::Array,
123            Self::Struct(_) => Type::Struct,
124        }
125    }
126}
127
128impl From<bool> for Value {
129    fn from(value: bool) -> Self {
130        Self::Bool(value)
131    }
132}
133
134impl From<i8> for Value {
135    fn from(value: i8) -> Self {
136        Self::Int(value.into())
137    }
138}
139
140impl From<i16> for Value {
141    fn from(value: i16) -> Self {
142        Self::Int(value.into())
143    }
144}
145
146impl From<i32> for Value {
147    fn from(value: i32) -> Self {
148        Self::Int(value.into())
149    }
150}
151
152impl From<i64> for Value {
153    fn from(value: i64) -> Self {
154        Self::Int(value)
155    }
156}
157
158impl From<u8> for Value {
159    fn from(value: u8) -> Self {
160        Self::Int(value.into())
161    }
162}
163
164impl From<u16> for Value {
165    fn from(value: u16) -> Self {
166        Self::Int(value.into())
167    }
168}
169
170impl From<u32> for Value {
171    fn from(value: u32) -> Self {
172        Self::Int(value.into())
173    }
174}
175
176impl From<f32> for Value {
177    fn from(value: f32) -> Self {
178        Self::Float(value.into())
179    }
180}
181
182impl From<f64> for Value {
183    fn from(value: f64) -> Self {
184        Self::Float(value)
185    }
186}
187
188impl From<String> for Value {
189    fn from(value: String) -> Self {
190        Self::String(value)
191    }
192}
193
194impl From<&str> for Value {
195    fn from(value: &str) -> Self {
196        Self::String(value.to_string())
197    }
198}
199
200impl<T> From<Vec<T>> for Value
201where
202    T: Into<Value>,
203{
204    fn from(value: Vec<T>) -> Self {
205        Self::Array(value.into_iter().map(Into::into).collect())
206    }
207}
208
209impl From<StructValue> for Value {
210    fn from(value: StructValue) -> Self {
211        Self::Struct(value)
212    }
213}
214
215impl StructValue {
216    /// Append given `key` and `value` to `self` and return it.
217    #[must_use]
218    pub fn with_field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
219        self.add_field(key, value);
220        self
221    }
222
223    /// Append given `key` and `value` to `self` in place.
224    pub fn add_field(&mut self, key: impl Into<String>, value: impl Into<Value>) {
225        self.fields.insert(key.into(), value.into());
226    }
227}
228
229impl std::fmt::Display for Type {
230    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231        match self {
232            Self::Bool => write!(f, "bool"),
233            Self::Int => write!(f, "int"),
234            Self::Float => write!(f, "float"),
235            Self::String => write!(f, "string"),
236            Self::Array => write!(f, "array"),
237            Self::Struct => write!(f, "struct"),
238        }
239    }
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245
246    #[test]
247    fn build_value() {
248        let alex = StructValue::default()
249            .with_field("is_male", false)
250            .with_field("id", 100)
251            .with_field("grade", 97.5)
252            .with_field("name", "Alex")
253            .with_field("friends", vec!["Bob", "Carl"])
254            .with_field(
255                "other",
256                StructValue::default().with_field("description", "A student"),
257            );
258
259        let is_male = alex.fields.get("is_male").unwrap();
260        assert!(is_male.is_bool());
261        assert_eq!(false, is_male.as_bool().unwrap());
262
263        let id = alex.fields.get("id").unwrap();
264        assert!(id.is_i64());
265        assert_eq!(100, id.as_i64().unwrap());
266
267        let grade = alex.fields.get("grade").unwrap();
268        assert!(grade.is_f64());
269        assert_eq!(97.5, grade.as_f64().unwrap());
270
271        let name = alex.fields.get("name").unwrap();
272        assert!(name.is_str());
273        assert_eq!("Alex", alex.fields.get("name").unwrap().as_str().unwrap());
274
275        let friends = alex.fields.get("friends").unwrap();
276        assert!(friends.is_array());
277        assert_eq!(
278            vec![
279                Value::String("Bob".to_string()),
280                Value::String("Carl".to_string())
281            ],
282            *friends.as_array().unwrap()
283        );
284
285        let other = alex.fields.get("other").unwrap();
286        assert!(other.is_struct());
287        assert_eq!(
288            "A student",
289            other
290                .as_struct()
291                .unwrap()
292                .fields
293                .get("description")
294                .unwrap()
295                .as_str()
296                .unwrap()
297        );
298    }
299}