toml_edit/
item.rs

1use std::str::FromStr;
2
3use toml_datetime::*;
4
5use crate::array_of_tables::ArrayOfTables;
6use crate::table::TableLike;
7use crate::{Array, InlineTable, Table, Value};
8
9/// Type representing either a value, a table, an array of tables, or none.
10#[derive(Debug)]
11pub enum Item {
12    /// Type representing none.
13    None,
14    /// Type representing value.
15    Value(Value),
16    /// Type representing table.
17    Table(Table),
18    /// Type representing array of tables.
19    ArrayOfTables(ArrayOfTables),
20}
21
22impl Item {
23    /// Sets `self` to the given item iff `self` is none and
24    /// returns a mutable reference to `self`.
25    pub fn or_insert(&mut self, item: Item) -> &mut Item {
26        if self.is_none() {
27            *self = item
28        }
29        self
30    }
31}
32
33// TODO: This should be generated by macro or derive
34/// Downcasting
35impl Item {
36    /// Text description of value type
37    pub fn type_name(&self) -> &'static str {
38        match self {
39            Item::None => "none",
40            Item::Value(v) => v.type_name(),
41            Item::Table(..) => "table",
42            Item::ArrayOfTables(..) => "array of tables",
43        }
44    }
45
46    /// Index into a TOML array or map. A string index can be used to access a
47    /// value in a map, and a usize index can be used to access an element of an
48    /// array.
49    ///
50    /// Returns `None` if:
51    /// - The type of `self` does not match the type of the
52    ///   index, for example if the index is a string and `self` is an array or a
53    ///   number.
54    /// - The given key does not exist in the map
55    ///   or the given index is not within the bounds of the array.
56    pub fn get<I: crate::index::Index>(&self, index: I) -> Option<&Item> {
57        index.index(self)
58    }
59
60    /// Mutably index into a TOML array or map. A string index can be used to
61    /// access a value in a map, and a usize index can be used to access an
62    /// element of an array.
63    ///
64    /// Returns `None` if:
65    /// - The type of `self` does not match the type of the
66    ///   index, for example if the index is a string and `self` is an array or a
67    ///   number.
68    /// - The given key does not exist in the map
69    ///   or the given index is not within the bounds of the array.
70    pub fn get_mut<I: crate::index::Index>(&mut self, index: I) -> Option<&mut Item> {
71        index.index_mut(self)
72    }
73
74    /// Casts `self` to value.
75    pub fn as_value(&self) -> Option<&Value> {
76        match *self {
77            Item::Value(ref v) => Some(v),
78            _ => None,
79        }
80    }
81    /// Casts `self` to table.
82    pub fn as_table(&self) -> Option<&Table> {
83        match *self {
84            Item::Table(ref t) => Some(t),
85            _ => None,
86        }
87    }
88    /// Casts `self` to array of tables.
89    pub fn as_array_of_tables(&self) -> Option<&ArrayOfTables> {
90        match *self {
91            Item::ArrayOfTables(ref a) => Some(a),
92            _ => None,
93        }
94    }
95    /// Casts `self` to mutable value.
96    pub fn as_value_mut(&mut self) -> Option<&mut Value> {
97        match *self {
98            Item::Value(ref mut v) => Some(v),
99            _ => None,
100        }
101    }
102    /// Casts `self` to mutable table.
103    pub fn as_table_mut(&mut self) -> Option<&mut Table> {
104        match *self {
105            Item::Table(ref mut t) => Some(t),
106            _ => None,
107        }
108    }
109    /// Casts `self` to mutable array of tables.
110    pub fn as_array_of_tables_mut(&mut self) -> Option<&mut ArrayOfTables> {
111        match *self {
112            Item::ArrayOfTables(ref mut a) => Some(a),
113            _ => None,
114        }
115    }
116    /// Casts `self` to value.
117    pub fn into_value(self) -> Result<Value, Self> {
118        match self {
119            Item::None => Err(self),
120            Item::Value(v) => Ok(v),
121            Item::Table(v) => {
122                let v = v.into_inline_table();
123                Ok(Value::InlineTable(v))
124            }
125            Item::ArrayOfTables(v) => {
126                let v = v.into_array();
127                Ok(Value::Array(v))
128            }
129        }
130    }
131    /// In-place convert to a value
132    pub fn make_value(&mut self) {
133        let other = std::mem::take(self);
134        let other = other.into_value().map(Item::Value).unwrap_or(Item::None);
135        *self = other;
136    }
137    /// Casts `self` to table.
138    pub fn into_table(self) -> Result<Table, Self> {
139        match self {
140            Item::Table(t) => Ok(t),
141            Item::Value(Value::InlineTable(t)) => Ok(t.into_table()),
142            _ => Err(self),
143        }
144    }
145    /// Casts `self` to array of tables.
146    pub fn into_array_of_tables(self) -> Result<ArrayOfTables, Self> {
147        match self {
148            Item::ArrayOfTables(a) => Ok(a),
149            Item::Value(Value::Array(a)) => {
150                if a.is_empty() {
151                    Err(Item::Value(Value::Array(a)))
152                } else if a.iter().all(|v| v.is_inline_table()) {
153                    let mut aot = ArrayOfTables::new();
154                    aot.values = a.values;
155                    for value in aot.values.iter_mut() {
156                        value.make_item();
157                    }
158                    Ok(aot)
159                } else {
160                    Err(Item::Value(Value::Array(a)))
161                }
162            }
163            _ => Err(self),
164        }
165    }
166    // Starting private because the name is unclear
167    pub(crate) fn make_item(&mut self) {
168        let other = std::mem::take(self);
169        let other = match other.into_table().map(crate::Item::Table) {
170            Ok(i) => i,
171            Err(i) => i,
172        };
173        let other = match other.into_array_of_tables().map(crate::Item::ArrayOfTables) {
174            Ok(i) => i,
175            Err(i) => i,
176        };
177        *self = other;
178    }
179    /// Returns true iff `self` is a value.
180    pub fn is_value(&self) -> bool {
181        self.as_value().is_some()
182    }
183    /// Returns true iff `self` is a table.
184    pub fn is_table(&self) -> bool {
185        self.as_table().is_some()
186    }
187    /// Returns true iff `self` is an array of tables.
188    pub fn is_array_of_tables(&self) -> bool {
189        self.as_array_of_tables().is_some()
190    }
191    /// Returns true iff `self` is `None`.
192    pub fn is_none(&self) -> bool {
193        matches!(*self, Item::None)
194    }
195
196    // Duplicate Value downcasting API
197
198    /// Casts `self` to integer.
199    pub fn as_integer(&self) -> Option<i64> {
200        self.as_value().and_then(Value::as_integer)
201    }
202
203    /// Returns true iff `self` is an integer.
204    pub fn is_integer(&self) -> bool {
205        self.as_integer().is_some()
206    }
207
208    /// Casts `self` to float.
209    pub fn as_float(&self) -> Option<f64> {
210        self.as_value().and_then(Value::as_float)
211    }
212
213    /// Returns true iff `self` is a float.
214    pub fn is_float(&self) -> bool {
215        self.as_float().is_some()
216    }
217
218    /// Casts `self` to boolean.
219    pub fn as_bool(&self) -> Option<bool> {
220        self.as_value().and_then(Value::as_bool)
221    }
222
223    /// Returns true iff `self` is a boolean.
224    pub fn is_bool(&self) -> bool {
225        self.as_bool().is_some()
226    }
227
228    /// Casts `self` to str.
229    pub fn as_str(&self) -> Option<&str> {
230        self.as_value().and_then(Value::as_str)
231    }
232
233    /// Returns true iff `self` is a string.
234    pub fn is_str(&self) -> bool {
235        self.as_str().is_some()
236    }
237
238    /// Casts `self` to date-time.
239    pub fn as_datetime(&self) -> Option<&Datetime> {
240        self.as_value().and_then(Value::as_datetime)
241    }
242
243    /// Returns true iff `self` is a date-time.
244    pub fn is_datetime(&self) -> bool {
245        self.as_datetime().is_some()
246    }
247
248    /// Casts `self` to array.
249    pub fn as_array(&self) -> Option<&Array> {
250        self.as_value().and_then(Value::as_array)
251    }
252
253    /// Casts `self` to mutable array.
254    pub fn as_array_mut(&mut self) -> Option<&mut Array> {
255        self.as_value_mut().and_then(Value::as_array_mut)
256    }
257
258    /// Returns true iff `self` is an array.
259    pub fn is_array(&self) -> bool {
260        self.as_array().is_some()
261    }
262
263    /// Casts `self` to inline table.
264    pub fn as_inline_table(&self) -> Option<&InlineTable> {
265        self.as_value().and_then(Value::as_inline_table)
266    }
267
268    /// Casts `self` to mutable inline table.
269    pub fn as_inline_table_mut(&mut self) -> Option<&mut InlineTable> {
270        self.as_value_mut().and_then(Value::as_inline_table_mut)
271    }
272
273    /// Returns true iff `self` is an inline table.
274    pub fn is_inline_table(&self) -> bool {
275        self.as_inline_table().is_some()
276    }
277
278    /// Casts `self` to either a table or an inline table.
279    pub fn as_table_like(&self) -> Option<&dyn TableLike> {
280        self.as_table()
281            .map(|t| t as &dyn TableLike)
282            .or_else(|| self.as_inline_table().map(|t| t as &dyn TableLike))
283    }
284
285    /// Casts `self` to either a table or an inline table.
286    pub fn as_table_like_mut(&mut self) -> Option<&mut dyn TableLike> {
287        match self {
288            Item::Table(t) => Some(t as &mut dyn TableLike),
289            Item::Value(Value::InlineTable(t)) => Some(t as &mut dyn TableLike),
290            _ => None,
291        }
292    }
293
294    /// Returns true iff `self` is either a table, or an inline table.
295    pub fn is_table_like(&self) -> bool {
296        self.as_table_like().is_some()
297    }
298
299    /// Returns the location within the original document
300    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
301        match self {
302            Item::None => None,
303            Item::Value(v) => v.span(),
304            Item::Table(v) => v.span(),
305            Item::ArrayOfTables(v) => v.span(),
306        }
307    }
308
309    pub(crate) fn despan(&mut self, input: &str) {
310        match self {
311            Item::None => {}
312            Item::Value(v) => v.despan(input),
313            Item::Table(v) => v.despan(input),
314            Item::ArrayOfTables(v) => v.despan(input),
315        }
316    }
317}
318
319impl Clone for Item {
320    #[inline(never)]
321    fn clone(&self) -> Self {
322        match self {
323            Item::None => Item::None,
324            Item::Value(v) => Item::Value(v.clone()),
325            Item::Table(v) => Item::Table(v.clone()),
326            Item::ArrayOfTables(v) => Item::ArrayOfTables(v.clone()),
327        }
328    }
329}
330
331impl Default for Item {
332    fn default() -> Self {
333        Item::None
334    }
335}
336
337impl FromStr for Item {
338    type Err = crate::TomlError;
339
340    /// Parses a value from a &str
341    fn from_str(s: &str) -> Result<Self, Self::Err> {
342        let value = s.parse::<Value>()?;
343        Ok(Item::Value(value))
344    }
345}
346
347impl std::fmt::Display for Item {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        match &self {
350            Item::None => Ok(()),
351            Item::Value(v) => v.fmt(f),
352            Item::Table(v) => v.fmt(f),
353            Item::ArrayOfTables(v) => v.fmt(f),
354        }
355    }
356}
357
358/// Returns a formatted value.
359///
360/// Since formatting is part of a `Value`, the right hand side of the
361/// assignment needs to be decorated with a space before the value.
362/// The `value` function does just that.
363///
364/// # Examples
365/// ```rust
366/// # use snapbox::assert_eq;
367/// # use toml_edit::*;
368/// let mut table = Table::default();
369/// let mut array = Array::default();
370/// array.push("hello");
371/// array.push("\\, world"); // \ is only allowed in a literal string
372/// table["key1"] = value("value1");
373/// table["key2"] = value(42);
374/// table["key3"] = value(array);
375/// assert_eq(table.to_string(),
376/// r#"key1 = "value1"
377/// key2 = 42
378/// key3 = ["hello", '\, world']
379/// "#);
380/// ```
381pub fn value<V: Into<Value>>(v: V) -> Item {
382    Item::Value(v.into())
383}
384
385/// Returns an empty table.
386pub fn table() -> Item {
387    Item::Table(Table::new())
388}
389
390/// Returns an empty array of tables.
391pub fn array() -> Item {
392    Item::ArrayOfTables(ArrayOfTables::new())
393}