hcl/value/
mod.rs

1//! The Value enum, a loosely typed way of representing any valid HCL value.
2
3pub(crate) mod de;
4mod from;
5mod ser;
6
7use std::fmt;
8
9use serde::{de::DeserializeOwned, ser::Serialize};
10
11use self::{de::ValueDeserializer, ser::ValueSerializer};
12use crate::{format, Number, Result};
13
14/// The map type used for HCL objects.
15pub type Map<K, V> = indexmap::IndexMap<K, V>;
16
17/// Represents any valid HCL value.
18#[derive(Debug, PartialEq, Eq, Clone, Default)]
19pub enum Value {
20    /// Represents a HCL null value.
21    #[default]
22    Null,
23    /// Represents a HCL boolean.
24    Bool(bool),
25    /// Represents a HCL number, either integer or float.
26    Number(Number),
27    /// Represents a HCL string.
28    String(String),
29    /// Represents a HCL array.
30    Array(Vec<Value>),
31    /// Represents a HCL object.
32    Object(Map<String, Value>),
33}
34
35impl Value {
36    /// If the `Value` is an Array, returns the associated vector. Returns None
37    /// otherwise.
38    pub fn as_array(&self) -> Option<&Vec<Value>> {
39        match self {
40            Self::Array(array) => Some(array),
41            _ => None,
42        }
43    }
44
45    /// If the `Value` is an Array, returns the associated mutable vector.
46    /// Returns None otherwise.
47    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
48        match self {
49            Self::Array(array) => Some(array),
50            _ => None,
51        }
52    }
53
54    /// If the `Value` is a Boolean, represent it as bool if possible. Returns
55    /// None otherwise.
56    pub fn as_bool(&self) -> Option<bool> {
57        match *self {
58            Self::Bool(b) => Some(b),
59            _ => None,
60        }
61    }
62
63    /// If the `Value` is a Number, represent it as f64 if possible. Returns
64    /// None otherwise.
65    pub fn as_f64(&self) -> Option<f64> {
66        self.as_number().and_then(Number::as_f64)
67    }
68
69    /// If the `Value` is a Number, represent it as i64 if possible. Returns
70    /// None otherwise.
71    pub fn as_i64(&self) -> Option<i64> {
72        self.as_number().and_then(Number::as_i64)
73    }
74
75    /// If the `Value` is a Null, returns (). Returns None otherwise.
76    pub fn as_null(&self) -> Option<()> {
77        match self {
78            Self::Null => Some(()),
79            _ => None,
80        }
81    }
82
83    /// If the `Value` is a Number, returns the associated Number. Returns None
84    /// otherwise.
85    pub fn as_number(&self) -> Option<&Number> {
86        match self {
87            Self::Number(num) => Some(num),
88            _ => None,
89        }
90    }
91
92    /// If the `Value` is an Object, returns the associated Map. Returns None
93    /// otherwise.
94    pub fn as_object(&self) -> Option<&Map<String, Value>> {
95        match self {
96            Self::Object(object) => Some(object),
97            _ => None,
98        }
99    }
100
101    /// If the `Value` is an Object, returns the associated mutable Map.
102    /// Returns None otherwise.
103    pub fn as_object_mut(&mut self) -> Option<&mut Map<String, Value>> {
104        match self {
105            Self::Object(object) => Some(object),
106            _ => None,
107        }
108    }
109
110    /// If the `Value` is a String, returns the associated str. Returns None
111    /// otherwise.
112    pub fn as_str(&self) -> Option<&str> {
113        match self {
114            Self::String(s) => Some(s),
115            _ => None,
116        }
117    }
118
119    /// If the `Value` is a Number, represent it as u64 if possible. Returns
120    /// None otherwise.
121    pub fn as_u64(&self) -> Option<u64> {
122        self.as_number().and_then(Number::as_u64)
123    }
124
125    /// Returns true if the `Value` is an Array. Returns false otherwise.
126    ///
127    /// For any Value on which `is_array` returns true, `as_array` and
128    /// `as_array_mut` are guaranteed to return the vector representing the
129    /// array.
130    pub fn is_array(&self) -> bool {
131        self.as_array().is_some()
132    }
133
134    /// Returns true if the `Value` is a Boolean. Returns false otherwise.
135    ///
136    /// For any Value on which `is_boolean` returns true, `as_bool` is
137    /// guaranteed to return the boolean value.
138    pub fn is_boolean(&self) -> bool {
139        self.as_bool().is_some()
140    }
141
142    /// Returns true if the `Value` is a number that can be represented by f64.
143    ///
144    /// For any Value on which `is_f64` returns true, `as_f64` is guaranteed to
145    /// return the floating point value.
146    pub fn is_f64(&self) -> bool {
147        self.as_number().is_some_and(Number::is_f64)
148    }
149
150    /// Returns true if the `Value` is an integer between `i64::MIN` and
151    /// `i64::MAX`.
152    ///
153    /// For any Value on which `is_i64` returns true, `as_i64` is guaranteed to
154    /// return the integer value.
155    pub fn is_i64(&self) -> bool {
156        self.as_number().is_some_and(Number::is_i64)
157    }
158
159    /// Returns true if the `Value` is a Number. Returns false otherwise.
160    pub fn is_number(&self) -> bool {
161        self.as_number().is_some()
162    }
163
164    /// Returns true if the `Value` is a Null. Returns false otherwise.
165    ///
166    /// For any Value on which `is_null` returns true, `as_null` is guaranteed
167    /// to return `Some(())`.
168    pub fn is_null(&self) -> bool {
169        self.as_null().is_some()
170    }
171
172    /// Returns true if the `Value` is an Object. Returns false otherwise.
173    ///
174    /// For any Value on which `is_object` returns true, `as_object` and
175    /// `as_object_mut` are guaranteed to return the map representation of the
176    /// object.
177    pub fn is_object(&self) -> bool {
178        self.as_object().is_some()
179    }
180
181    /// Returns true if the `Value` is a String. Returns false otherwise.
182    ///
183    /// For any Value on which `is_string` returns true, `as_str` is guaranteed
184    /// to return the string slice.
185    pub fn is_string(&self) -> bool {
186        self.as_str().is_some()
187    }
188
189    /// Returns true if the `Value` is an integer between zero and `u64::MAX`.
190    ///
191    /// For any Value on which `is_u64` returns true, `as_u64` is guaranteed to
192    /// return the integer value.
193    pub fn is_u64(&self) -> bool {
194        self.as_number().is_some_and(Number::is_u64)
195    }
196
197    /// Takes the value out of the `Value`, leaving a `Null` in its place.
198    pub fn take(&mut self) -> Value {
199        std::mem::replace(self, Value::Null)
200    }
201}
202
203impl fmt::Display for Value {
204    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205        // Formatting a `Value` as string cannot fail.
206        let formatted = format::to_string(self).expect("a Value failed to format unexpectedly");
207        f.write_str(&formatted)
208    }
209}
210
211/// Convert a `T` into `hcl::Value` which is an enum that can represent any valid HCL value.
212///
213/// # Example
214///
215/// ```
216/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
217/// use hcl::{Map, Value};
218/// use serde::Serialize;
219///
220/// #[derive(Debug, Serialize)]
221/// struct Custom {
222///     foo: String,
223///     bar: u64,
224/// }
225///
226/// let custom = Custom {
227///     foo: "baz".into(),
228///     bar: 42,
229/// };
230///
231/// let expected = Value::Object({
232///     let mut object = Map::new();
233///     object.insert("foo".into(), "baz".into());
234///     object.insert("bar".into(), 42u64.into());
235///     object
236/// });
237///
238/// let value = hcl::to_value(&custom)?;
239///
240/// assert_eq!(value, expected);
241/// #     Ok(())
242/// # }
243/// ```
244///
245/// # Errors
246///
247/// This conversion can fail if `T`'s implementation of `Serialize` decides to fail, or if `T`
248/// contains a map with non-string keys.
249pub fn to_value<T>(value: T) -> Result<Value>
250where
251    T: Serialize,
252{
253    value.serialize(ValueSerializer)
254}
255
256/// Convert a `hcl::Value` into a type `T` that implements `serde::Deserialize`.
257///
258/// # Example
259///
260/// ```
261/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
262/// use hcl::{Map, Value};
263/// use serde::Deserialize;
264///
265/// #[derive(Debug, Deserialize, PartialEq)]
266/// struct Custom {
267///     foo: String,
268///     bar: u64,
269/// }
270///
271/// let value = Value::Object({
272///     let mut object = Map::new();
273///     object.insert("foo".into(), "baz".into());
274///     object.insert("bar".into(), 42u64.into());
275///     object
276/// });
277///
278///
279/// let expected = Custom {
280///     foo: "baz".into(),
281///     bar: 42,
282/// };
283///
284/// let custom: Custom = hcl::from_value(value)?;
285///
286/// assert_eq!(custom, expected);
287/// #     Ok(())
288/// # }
289/// ```
290///
291/// # Errors
292///
293/// This conversion can fail if `T`'s implementation of [`serde::Deserialize`] decides to fail.
294pub fn from_value<T>(value: Value) -> Result<T>
295where
296    T: DeserializeOwned,
297{
298    T::deserialize(ValueDeserializer::new(value))
299}