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}