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