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