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, Default)]
11pub 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
23impl Item {
24 /// Sets `self` to the given item iff `self` is none and
25 /// returns a mutable reference to `self`.
26 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
36impl Item {
37 /// Text description of value type
38 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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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
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.
181 pub fn is_value(&self) -> bool {
182 self.as_value().is_some()
183 }
184 /// Returns true iff `self` is a table.
185 pub fn is_table(&self) -> bool {
186 self.as_table().is_some()
187 }
188 /// Returns true iff `self` is an array of tables.
189 pub fn is_array_of_tables(&self) -> bool {
190 self.as_array_of_tables().is_some()
191 }
192 /// Returns true iff `self` is `None`.
193 pub fn is_none(&self) -> bool {
194 matches!(*self, Item::None)
195 }
196
197 // Duplicate Value downcasting API
198
199 /// Casts `self` to integer.
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.
205 pub fn is_integer(&self) -> bool {
206 self.as_integer().is_some()
207 }
208
209 /// Casts `self` to float.
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.
215 pub fn is_float(&self) -> bool {
216 self.as_float().is_some()
217 }
218
219 /// Casts `self` to boolean.
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.
225 pub fn is_bool(&self) -> bool {
226 self.as_bool().is_some()
227 }
228
229 /// Casts `self` to 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.
235 pub fn is_str(&self) -> bool {
236 self.as_str().is_some()
237 }
238
239 /// Casts `self` to date-time.
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.
245 pub fn is_datetime(&self) -> bool {
246 self.as_datetime().is_some()
247 }
248
249 /// Casts `self` to 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.
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.
260 pub fn is_array(&self) -> bool {
261 self.as_array().is_some()
262 }
263
264 /// Casts `self` to inline table.
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.
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.
275 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.
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.
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.
296 pub fn is_table_like(&self) -> bool {
297 self.as_table_like().is_some()
298 }
299
300 /// Returns the location within the original document
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
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
320impl Clone for Item {
321 #[inline(never)]
322 fn clone(&self) -> Self {
323 match self {
324 Item::None => Item::None,
325 Item::Value(v: &Value) => Item::Value(v.clone()),
326 Item::Table(v: &Table) => Item::Table(v.clone()),
327 Item::ArrayOfTables(v: &ArrayOfTables) => Item::ArrayOfTables(v.clone()),
328 }
329 }
330}
331
332impl FromStr for Item {
333 type Err = crate::TomlError;
334
335 /// Parses a value from a &str
336 fn from_str(s: &str) -> Result<Self, Self::Err> {
337 let value: Value = s.parse::<Value>()?;
338 Ok(Item::Value(value))
339 }
340}
341
342impl std::fmt::Display for Item {
343 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
344 match &self {
345 Item::None => Ok(()),
346 Item::Value(v: &Value) => v.fmt(f),
347 Item::Table(v: &Table) => v.fmt(f),
348 Item::ArrayOfTables(v: &ArrayOfTables) => v.fmt(f),
349 }
350 }
351}
352
353/// Returns a formatted value.
354///
355/// Since formatting is part of a `Value`, the right hand side of the
356/// assignment needs to be decorated with a space before the value.
357/// The `value` function does just that.
358///
359/// # Examples
360/// ```rust
361/// # use snapbox::assert_eq;
362/// # use toml_edit::*;
363/// let mut table = Table::default();
364/// let mut array = Array::default();
365/// array.push("hello");
366/// array.push("\\, world"); // \ is only allowed in a literal string
367/// table["key1"] = value("value1");
368/// table["key2"] = value(42);
369/// table["key3"] = value(array);
370/// assert_eq(table.to_string(),
371/// r#"key1 = "value1"
372/// key2 = 42
373/// key3 = ["hello", '\, world']
374/// "#);
375/// ```
376pub fn value<V: Into<Value>>(v: V) -> Item {
377 Item::Value(v.into())
378}
379
380/// Returns an empty table.
381pub fn table() -> Item {
382 Item::Table(Table::new())
383}
384
385/// Returns an empty array of tables.
386pub fn array() -> Item {
387 Item::ArrayOfTables(ArrayOfTables::new())
388}
389