Skip to main content

toml/de/parser/
devalue.rs

1//! Definition of a TOML [value][DeValue] for deserialization
2
3use alloc::borrow::Cow;
4use core::mem::discriminant;
5use core::ops;
6
7use serde_spanned::Spanned;
8use toml_datetime::Datetime;
9
10use crate::alloc_prelude::*;
11use crate::de::DeArray;
12use crate::de::DeTable;
13
14/// Type representing a TOML string, payload of the `DeValue::String` variant
15pub type DeString<'i> = Cow<'i, str>;
16
17/// Represents a TOML integer
18#[derive(Clone, Debug)]
19pub struct DeInteger<'i> {
20    pub(crate) inner: DeString<'i>,
21    pub(crate) radix: u32,
22}
23
24impl DeInteger<'_> {
25    pub(crate) fn to_u64(&self) -> Option<u64> {
26        u64::from_str_radix(self.inner.as_ref(), self.radix).ok()
27    }
28    pub(crate) fn to_i64(&self) -> Option<i64> {
29        i64::from_str_radix(self.inner.as_ref(), self.radix).ok()
30    }
31    pub(crate) fn to_u128(&self) -> Option<u128> {
32        u128::from_str_radix(self.inner.as_ref(), self.radix).ok()
33    }
34    pub(crate) fn to_i128(&self) -> Option<i128> {
35        i128::from_str_radix(self.inner.as_ref(), self.radix).ok()
36    }
37
38    /// [`from_str_radix`][i64::from_str_radix]-compatible representation of an integer
39    ///
40    /// Requires [`DeInteger::radix`] to interpret
41    ///
42    /// See [`Display`][std::fmt::Display] for a representation that includes the radix
43    pub fn as_str(&self) -> &str {
44        self.inner.as_ref()
45    }
46
47    /// Numeric base of [`DeInteger::as_str`]
48    pub fn radix(&self) -> u32 {
49        self.radix
50    }
51
52    /// Ensure no data is borrowed
53    pub fn make_owned(&mut self) {
54        let owned = core::mem::take(&mut self.inner);
55        self.inner = Cow::Owned(owned.into_owned());
56    }
57}
58
59impl Default for DeInteger<'_> {
60    fn default() -> Self {
61        Self {
62            inner: DeString::Borrowed("0"),
63            radix: 10,
64        }
65    }
66}
67
68impl core::fmt::Display for DeInteger<'_> {
69    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70        match self.radix {
71            2 => "0b".fmt(formatter)?,
72            8 => "0o".fmt(formatter)?,
73            10 => {}
74            16 => "0x".fmt(formatter)?,
75            _ => {
76                unreachable!(
77                    "we should only ever have 2, 8, 10, and 16 radix, not {}",
78                    self.radix
79                )
80            }
81        }
82        self.as_str().fmt(formatter)?;
83        Ok(())
84    }
85}
86
87/// Represents a TOML integer
88#[derive(Clone, Debug)]
89pub struct DeFloat<'i> {
90    pub(crate) inner: DeString<'i>,
91}
92
93impl DeFloat<'_> {
94    pub(crate) fn to_f64(&self) -> Option<f64> {
95        let f: f64 = self.inner.as_ref().parse().ok()?;
96        if f.is_infinite() && !self.as_str().contains("inf") {
97            None
98        } else {
99            Some(f)
100        }
101    }
102
103    /// [`FromStr`][std::str::FromStr]-compatible representation of a float
104    pub fn as_str(&self) -> &str {
105        self.inner.as_ref()
106    }
107
108    /// Ensure no data is borrowed
109    pub fn make_owned(&mut self) {
110        let owned = core::mem::take(&mut self.inner);
111        self.inner = Cow::Owned(owned.into_owned());
112    }
113}
114
115impl Default for DeFloat<'_> {
116    fn default() -> Self {
117        Self {
118            inner: DeString::Borrowed("0.0"),
119        }
120    }
121}
122
123impl core::fmt::Display for DeFloat<'_> {
124    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
125        self.as_str().fmt(formatter)?;
126        Ok(())
127    }
128}
129
130/// Representation of a TOML value.
131#[derive(Clone, Debug)]
132pub enum DeValue<'i> {
133    /// Represents a TOML string
134    String(DeString<'i>),
135    /// Represents a TOML integer
136    Integer(DeInteger<'i>),
137    /// Represents a TOML float
138    Float(DeFloat<'i>),
139    /// Represents a TOML boolean
140    Boolean(bool),
141    /// Represents a TOML datetime
142    Datetime(Datetime),
143    /// Represents a TOML array
144    Array(DeArray<'i>),
145    /// Represents a TOML table
146    Table(DeTable<'i>),
147}
148
149impl<'i> DeValue<'i> {
150    /// Parse a TOML value
151    pub fn parse(input: &'i str) -> Result<Spanned<Self>, crate::de::Error> {
152        let source = toml_parser::Source::new(input);
153        let mut errors = crate::de::error::TomlSink::<Option<_>>::new(source);
154        let value = crate::de::parser::parse_value(source, &mut errors);
155        if let Some(err) = errors.into_inner() {
156            Err(err)
157        } else {
158            Ok(value)
159        }
160    }
161
162    /// Parse a TOML value, with best effort recovery on error
163    pub fn parse_recoverable(input: &'i str) -> (Spanned<Self>, Vec<crate::de::Error>) {
164        let source = toml_parser::Source::new(input);
165        let mut errors = crate::de::error::TomlSink::<Vec<_>>::new(source);
166        let value = crate::de::parser::parse_value(source, &mut errors);
167        (value, errors.into_inner())
168    }
169
170    /// Ensure no data is borrowed
171    pub fn make_owned(&mut self) {
172        match self {
173            DeValue::String(v) => {
174                let owned = core::mem::take(v);
175                *v = Cow::Owned(owned.into_owned());
176            }
177            DeValue::Integer(v) => {
178                v.make_owned();
179            }
180            DeValue::Float(v) => {
181                v.make_owned();
182            }
183            DeValue::Boolean(..) | DeValue::Datetime(..) => {}
184            DeValue::Array(v) => {
185                for e in v.iter_mut() {
186                    e.get_mut().make_owned();
187                }
188            }
189            DeValue::Table(v) => v.make_owned(),
190        }
191    }
192
193    /// Index into a TOML array or map. A string index can be used to access a
194    /// value in a map, and a usize index can be used to access an element of an
195    /// array.
196    ///
197    /// Returns `None` if the type of `self` does not match the type of the
198    /// index, for example if the index is a string and `self` is an array or a
199    /// number. Also returns `None` if the given key does not exist in the map
200    /// or the given index is not within the bounds of the array.
201    pub fn get<I: Index>(&self, index: I) -> Option<&Spanned<Self>> {
202        index.index(self)
203    }
204
205    /// Extracts the integer value if it is an integer.
206    pub fn as_integer(&self) -> Option<&DeInteger<'i>> {
207        match self {
208            DeValue::Integer(i) => Some(i),
209            _ => None,
210        }
211    }
212
213    /// Tests whether this value is an integer.
214    pub fn is_integer(&self) -> bool {
215        self.as_integer().is_some()
216    }
217
218    /// Extracts the float value if it is a float.
219    pub fn as_float(&self) -> Option<&DeFloat<'i>> {
220        match self {
221            DeValue::Float(f) => Some(f),
222            _ => None,
223        }
224    }
225
226    /// Tests whether this value is a float.
227    pub fn is_float(&self) -> bool {
228        self.as_float().is_some()
229    }
230
231    /// Extracts the boolean value if it is a boolean.
232    pub fn as_bool(&self) -> Option<bool> {
233        match *self {
234            DeValue::Boolean(b) => Some(b),
235            _ => None,
236        }
237    }
238
239    /// Tests whether this value is a boolean.
240    pub fn is_bool(&self) -> bool {
241        self.as_bool().is_some()
242    }
243
244    /// Extracts the string of this value if it is a string.
245    pub fn as_str(&self) -> Option<&str> {
246        match *self {
247            DeValue::String(ref s) => Some(&**s),
248            _ => None,
249        }
250    }
251
252    /// Tests if this value is a string.
253    pub fn is_str(&self) -> bool {
254        self.as_str().is_some()
255    }
256
257    /// Extracts the datetime value if it is a datetime.
258    ///
259    /// Note that a parsed TOML value will only contain ISO 8601 dates. An
260    /// example date is:
261    ///
262    /// ```notrust
263    /// 1979-05-27T07:32:00Z
264    /// ```
265    pub fn as_datetime(&self) -> Option<&Datetime> {
266        match *self {
267            DeValue::Datetime(ref s) => Some(s),
268            _ => None,
269        }
270    }
271
272    /// Tests whether this value is a datetime.
273    pub fn is_datetime(&self) -> bool {
274        self.as_datetime().is_some()
275    }
276
277    /// Extracts the array value if it is an array.
278    pub fn as_array(&self) -> Option<&DeArray<'i>> {
279        match *self {
280            DeValue::Array(ref s) => Some(s),
281            _ => None,
282        }
283    }
284
285    pub(crate) fn as_array_mut(&mut self) -> Option<&mut DeArray<'i>> {
286        match self {
287            DeValue::Array(s) => Some(s),
288            _ => None,
289        }
290    }
291
292    /// Tests whether this value is an array.
293    pub fn is_array(&self) -> bool {
294        self.as_array().is_some()
295    }
296
297    /// Extracts the table value if it is a table.
298    pub fn as_table(&self) -> Option<&DeTable<'i>> {
299        match *self {
300            DeValue::Table(ref s) => Some(s),
301            _ => None,
302        }
303    }
304
305    pub(crate) fn as_table_mut(&mut self) -> Option<&mut DeTable<'i>> {
306        match self {
307            DeValue::Table(s) => Some(s),
308            _ => None,
309        }
310    }
311
312    /// Tests whether this value is a table.
313    pub fn is_table(&self) -> bool {
314        self.as_table().is_some()
315    }
316
317    /// Tests whether this and another value have the same type.
318    pub fn same_type(&self, other: &DeValue<'_>) -> bool {
319        discriminant(self) == discriminant(other)
320    }
321
322    /// Returns a human-readable representation of the type of this value.
323    pub fn type_str(&self) -> &'static str {
324        match *self {
325            DeValue::String(..) => "string",
326            DeValue::Integer(..) => "integer",
327            DeValue::Float(..) => "float",
328            DeValue::Boolean(..) => "boolean",
329            DeValue::Datetime(..) => "datetime",
330            DeValue::Array(..) => "array",
331            DeValue::Table(..) => "table",
332        }
333    }
334}
335
336impl<I> ops::Index<I> for DeValue<'_>
337where
338    I: Index,
339{
340    type Output = Spanned<Self>;
341
342    fn index(&self, index: I) -> &Spanned<Self> {
343        self.get(index).expect("index not found")
344    }
345}
346
347/// Types that can be used to index a `toml::Value`
348///
349/// Currently this is implemented for `usize` to index arrays and `str` to index
350/// tables.
351///
352/// This trait is sealed and not intended for implementation outside of the
353/// `toml` crate.
354pub trait Index: Sealed {
355    #[doc(hidden)]
356    fn index<'r, 'i>(&self, val: &'r DeValue<'i>) -> Option<&'r Spanned<DeValue<'i>>>;
357}
358
359/// An implementation detail that should not be implemented, this will change in
360/// the future and break code otherwise.
361#[doc(hidden)]
362pub trait Sealed {}
363impl Sealed for usize {}
364impl Sealed for str {}
365impl Sealed for String {}
366impl<T: Sealed + ?Sized> Sealed for &T {}
367
368impl Index for usize {
369    fn index<'r, 'i>(&self, val: &'r DeValue<'i>) -> Option<&'r Spanned<DeValue<'i>>> {
370        match *val {
371            DeValue::Array(ref a) => a.get(*self),
372            _ => None,
373        }
374    }
375}
376
377impl Index for str {
378    fn index<'r, 'i>(&self, val: &'r DeValue<'i>) -> Option<&'r Spanned<DeValue<'i>>> {
379        match *val {
380            DeValue::Table(ref a) => a.get(self),
381            _ => None,
382        }
383    }
384}
385
386impl Index for String {
387    fn index<'r, 'i>(&self, val: &'r DeValue<'i>) -> Option<&'r Spanned<DeValue<'i>>> {
388        self[..].index(val)
389    }
390}
391
392impl<T> Index for &T
393where
394    T: Index + ?Sized,
395{
396    fn index<'r, 'i>(&self, val: &'r DeValue<'i>) -> Option<&'r Spanned<DeValue<'i>>> {
397        (**self).index(val)
398    }
399}