esp_config/generate/
value.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use super::Error;
6
7/// Supported configuration value types.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum Value {
10    /// Booleans.
11    Bool(bool),
12    /// Integers.
13    Integer(i128),
14    /// Strings.
15    String(String),
16}
17
18impl Serialize for Value {
19    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
20    where
21        S: serde::Serializer,
22    {
23        match self {
24            Value::String(s) => serializer.serialize_str(&format!("\"{s}\"")),
25            Value::Integer(n) => serializer.serialize_str(&format!("{n}")),
26            Value::Bool(b) => serializer.serialize_str(&format!("{b}")),
27        }
28    }
29}
30
31impl<'de> Deserialize<'de> for Value {
32    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
33    where
34        D: serde::Deserializer<'de>,
35    {
36        struct ValueVisitor;
37
38        impl serde::de::Visitor<'_> for ValueVisitor {
39            type Value = String;
40
41            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42                formatter.write_str("a String representing the Value")
43            }
44
45            fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
46            where
47                E: serde::de::Error,
48            {
49                Ok(v)
50            }
51
52            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
53            where
54                E: serde::de::Error,
55            {
56                Ok(v.to_string())
57            }
58        }
59
60        let str_repr = deserializer.deserialize_string(ValueVisitor)?;
61        let str_repr = str_repr.as_str();
62
63        if let Some(remaining) = str_repr.strip_prefix("\"") {
64            let s = &remaining[..remaining.len() - 1];
65            return Ok(Value::String(s.to_string()));
66        }
67
68        if str_repr == "true" {
69            return Ok(Value::Bool(true));
70        }
71
72        if str_repr == "false" {
73            return Ok(Value::Bool(false));
74        }
75
76        Ok(Value::Integer(str_repr.parse().map_err(
77            |e: core::num::ParseIntError| serde::de::Error::custom(e.to_string()),
78        )?))
79    }
80}
81
82// TODO: Do we want to handle negative values for non-decimal values?
83impl Value {
84    /// Try to parse the given String
85    pub fn parse_in_place(&mut self, s: &str) -> Result<(), Error> {
86        *self = match self {
87            Value::Bool(_) => match s {
88                "true" => Value::Bool(true),
89                "false" => Value::Bool(false),
90                _ => {
91                    return Err(Error::parse(format!(
92                        "Expected 'true' or 'false', found: '{s}'"
93                    )));
94                }
95            },
96            Value::Integer(_) => {
97                let inner = match s.as_bytes() {
98                    [b'0', b'x', ..] => i128::from_str_radix(&s[2..], 16),
99                    [b'0', b'o', ..] => i128::from_str_radix(&s[2..], 8),
100                    [b'0', b'b', ..] => i128::from_str_radix(&s[2..], 2),
101                    _ => s.parse(),
102                }
103                .map_err(|_| Error::parse(format!("Expected valid intger value, found: '{s}'")))?;
104
105                Value::Integer(inner)
106            }
107            Value::String(_) => Value::String(s.into()),
108        };
109
110        Ok(())
111    }
112
113    /// Convert the value to a [bool].
114    pub fn as_bool(&self) -> bool {
115        match self {
116            Value::Bool(value) => *value,
117            _ => panic!("attempted to convert non-bool value to a bool"),
118        }
119    }
120
121    /// Convert the value to an [i128].
122    pub fn as_integer(&self) -> i128 {
123        match self {
124            Value::Integer(value) => *value,
125            _ => panic!("attempted to convert non-integer value to an integer"),
126        }
127    }
128
129    /// Convert the value to a [String].
130    pub fn as_string(&self) -> String {
131        match self {
132            Value::String(value) => value.to_owned(),
133            _ => panic!("attempted to convert non-string value to a string"),
134        }
135    }
136
137    /// Is the value a bool?
138    pub fn is_bool(&self) -> bool {
139        matches!(self, Value::Bool(_))
140    }
141
142    /// Is the value an integer?
143    pub fn is_integer(&self) -> bool {
144        matches!(self, Value::Integer(_))
145    }
146
147    /// Is the value a string?
148    pub fn is_string(&self) -> bool {
149        matches!(self, Value::String(_))
150    }
151}
152
153impl fmt::Display for Value {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        match self {
156            Value::Bool(b) => write!(f, "{b}"),
157            Value::Integer(i) => write!(f, "{i}"),
158            Value::String(s) => write!(f, "{s}"),
159        }
160    }
161}
162
163impl From<bool> for Value {
164    fn from(value: bool) -> Self {
165        Value::Bool(value)
166    }
167}
168
169impl From<i128> for Value {
170    fn from(value: i128) -> Self {
171        Value::Integer(value)
172    }
173}
174
175impl From<&str> for Value {
176    fn from(value: &str) -> Self {
177        Value::String(value.to_string())
178    }
179}
180
181impl From<String> for Value {
182    fn from(value: String) -> Self {
183        Value::String(value)
184    }
185}
186
187#[cfg(test)]
188mod test {
189    use super::*;
190
191    #[test]
192    fn deserialization_number() {
193        assert_eq!(
194            serde_yaml::from_str::<Value>("128").unwrap(),
195            Value::Integer(128)
196        );
197        assert_eq!(
198            serde_yaml::from_str::<Value>(&format!("{}", i128::MAX)).unwrap(),
199            Value::Integer(i128::MAX)
200        );
201        assert_eq!(
202            serde_yaml::from_str::<Value>(&format!("{}", i128::MIN)).unwrap(),
203            Value::Integer(i128::MIN)
204        );
205    }
206
207    #[test]
208    fn deserialization_string() {
209        let yml = "'\"Hello\"'";
210        let value: Value = serde_yaml::from_str(yml).unwrap();
211        assert_eq!(value, Value::String("Hello".to_string()));
212    }
213}