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