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);