configcat/
value.rs

1use serde::{Deserialize, Serialize};
2use std::fmt::{Display, Formatter};
3
4/// Represents the value of a feature flag or setting.
5///
6/// # Examples
7///
8/// ```rust
9/// use configcat::Value;
10///
11/// let bool_val = Value::Bool(true);
12/// let int_val = Value::Int(42);
13/// ```
14#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
15#[serde(untagged)]
16pub enum Value {
17    /// A bool feature flag's value.
18    Bool(bool),
19    /// A whole number setting's value.
20    Int(i64),
21    /// A decimal number setting's value.
22    Float(f64),
23    /// A text setting's value.
24    String(String),
25}
26
27impl Value {
28    /// Reads the value as `bool`. Returns [`None`] if it's not a [`Value::Bool`].
29    ///
30    /// # Examples
31    ///
32    /// ```rust
33    /// use configcat::Value;
34    ///
35    /// let value = Value::Bool(true);
36    /// assert!(value.as_bool().unwrap());
37    /// ```
38    pub fn as_bool(&self) -> Option<bool> {
39        if let Value::Bool(val) = self {
40            return Some(*val);
41        }
42        None
43    }
44
45    /// Reads the value as `i64`. Returns [`None`] if it's not a [`Value::Int`].
46    ///
47    /// # Examples
48    ///
49    /// ```rust
50    /// use configcat::Value;
51    ///
52    /// let value = Value::Int(42);
53    /// assert_eq!(value.as_int().unwrap(), 42);
54    /// ```
55    pub fn as_int(&self) -> Option<i64> {
56        if let Value::Int(val) = self {
57            return Some(*val);
58        }
59        None
60    }
61
62    /// Reads the value as `f64`. Returns [`None`] if it's not a [`Value::Float`].
63    ///
64    /// # Examples
65    ///
66    /// ```rust
67    /// use configcat::Value;
68    ///
69    /// let value = Value::Float(3.14);
70    /// assert_eq!(value.as_float().unwrap(), 3.14);
71    /// ```
72    pub fn as_float(&self) -> Option<f64> {
73        if let Value::Float(val) = self {
74            return Some(*val);
75        }
76        None
77    }
78
79    /// Reads the value as [`String`]. Returns [`None`] if it's not a [`Value::String`].
80    ///
81    /// # Examples
82    ///
83    /// ```rust
84    /// use configcat::Value;
85    ///
86    /// let value = Value::String("foo".to_owned());
87    /// assert_eq!(value.as_str().unwrap(), "foo".to_owned());
88    /// ```
89    pub fn as_str(&self) -> Option<String> {
90        if let Value::String(val) = self {
91            return Some(val.clone());
92        }
93        None
94    }
95}
96
97impl Display for Value {
98    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99        match self {
100            Value::Bool(val) => write!(f, "{val}"),
101            Value::Int(val) => write!(f, "{val}"),
102            Value::Float(val) => write!(f, "{val}"),
103            Value::String(val) => f.write_str(val),
104        }
105    }
106}
107
108pub trait OptionalValueDisplay {
109    fn to_str(&self) -> String;
110}
111
112impl OptionalValueDisplay for Option<&Value> {
113    fn to_str(&self) -> String {
114        match self {
115            None => "none".to_owned(),
116            Some(value) => format!("{value}"),
117        }
118    }
119}
120
121/// Represents a primitive type that can describe the value of a feature flag or setting.
122pub trait ValuePrimitive: Into<Value> {
123    /// Reads the primitive value from a [`Value`].
124    fn from_value(value: &Value) -> Option<Self>;
125}
126
127macro_rules! primitive_impl {
128    ($ob:ident $to:ident $as_m:ident $t:ty) => (
129        from_val_to_enum!($ob $to $t);
130
131        impl ValuePrimitive for $t {
132            fn from_value(value: &Value) -> Option<Self> {
133                value.$as_m()
134            }
135        }
136    )
137}
138
139primitive_impl!(Value String as_str String);
140primitive_impl!(Value Float as_float f64);
141primitive_impl!(Value Int as_int i64);
142primitive_impl!(Value Bool as_bool bool);
143from_val_to_enum_into!(Value String &str);