toml_span/
value.rs

1//! Contains the [`Value`] and [`ValueInner`] containers into which all toml
2//! contents can be deserialized into and either used directly or fed into
3//! [`crate::Deserialize`] or your own constructs to deserialize into your own
4//! types
5
6use crate::{Error, ErrorKind, Span};
7use std::{borrow::Cow, fmt};
8
9/// A deserialized [`ValueInner`] with accompanying [`Span`] information for where
10/// it was located in the toml document
11pub struct Value<'de> {
12    value: Option<ValueInner<'de>>,
13    /// The location of the value in the toml document
14    pub span: Span,
15}
16
17impl<'de> Value<'de> {
18    /// Creates a new [`Value`] with an empty [`Span`]
19    #[inline]
20    pub fn new(value: ValueInner<'de>) -> Self {
21        Self::with_span(value, Span::default())
22    }
23
24    /// Creates a new [`Value`] with the specified [`Span`]
25    #[inline]
26    pub fn with_span(value: ValueInner<'de>, span: Span) -> Self {
27        Self {
28            value: Some(value),
29            span,
30        }
31    }
32
33    /// Takes the inner [`ValueInner`]
34    ///
35    /// This panics if the inner value has already been taken.
36    ///
37    /// Typically paired with [`Self::set`]
38    #[inline]
39    pub fn take(&mut self) -> ValueInner<'de> {
40        self.value.take().expect("the value has already been taken")
41    }
42
43    /// Sets the inner [`ValueInner`]
44    ///
45    /// This is typically done when the value is taken with [`Self::take`],
46    /// processed, and returned
47    #[inline]
48    pub fn set(&mut self, value: ValueInner<'de>) {
49        self.value = Some(value);
50    }
51
52    /// Returns true if the value is a table and is non-empty
53    #[inline]
54    pub fn has_keys(&self) -> bool {
55        self.value.as_ref().map_or(false, |val| {
56            if let ValueInner::Table(table) = val {
57                !table.is_empty()
58            } else {
59                false
60            }
61        })
62    }
63
64    /// Returns true if the value is a table and has the specified key
65    #[inline]
66    pub fn has_key(&self, key: &str) -> bool {
67        self.value.as_ref().map_or(false, |val| {
68            if let ValueInner::Table(table) = val {
69                table.contains_key(key)
70            } else {
71                false
72            }
73        })
74    }
75
76    /// Takes the value as a string, returning an error with either a default
77    /// or user supplied message
78    #[inline]
79    pub fn take_string(&mut self, msg: Option<&'static str>) -> Result<Cow<'de, str>, Error> {
80        match self.take() {
81            ValueInner::String(s) => Ok(s),
82            other => Err(Error {
83                kind: ErrorKind::Wanted {
84                    expected: msg.unwrap_or("a string"),
85                    found: other.type_str(),
86                },
87                span: self.span,
88                line_info: None,
89            }),
90        }
91    }
92
93    /// Returns a borrowed string if this is a [`ValueInner::String`]
94    #[inline]
95    pub fn as_str(&self) -> Option<&str> {
96        self.value.as_ref().and_then(|v| v.as_str())
97    }
98
99    /// Returns a borrowed table if this is a [`ValueInner::Table`]
100    #[inline]
101    pub fn as_table(&self) -> Option<&Table<'de>> {
102        self.value.as_ref().and_then(|v| v.as_table())
103    }
104
105    /// Returns a borrowed array if this is a [`ValueInner::Array`]
106    #[inline]
107    pub fn as_array(&self) -> Option<&Array<'de>> {
108        self.value.as_ref().and_then(|v| v.as_array())
109    }
110
111    /// Returns an `i64` if this is a [`ValueInner::Integer`]
112    #[inline]
113    pub fn as_integer(&self) -> Option<i64> {
114        self.value.as_ref().and_then(|v| v.as_integer())
115    }
116
117    /// Returns an `f64` if this is a [`ValueInner::Float`]
118    #[inline]
119    pub fn as_float(&self) -> Option<f64> {
120        self.value.as_ref().and_then(|v| v.as_float())
121    }
122
123    /// Returns a `bool` if this is a [`ValueInner::Boolean`]
124    #[inline]
125    pub fn as_bool(&self) -> Option<bool> {
126        self.value.as_ref().and_then(|v| v.as_bool())
127    }
128
129    /// Uses JSON pointer-like syntax to lookup a specific [`Value`]
130    ///
131    /// The basic format is:
132    ///
133    /// - The path starts with `/`
134    /// - Each segment is separated by a `/`
135    /// - Each segment is either a key name, or an integer array index
136    ///
137    /// ```rust
138    /// let data = "[x]\ny = ['z', 'zz']";
139    /// let value = toml_span::parse(data).unwrap();
140    /// assert_eq!(value.pointer("/x/y/1").unwrap().as_str().unwrap(), "zz");
141    /// assert!(value.pointer("/a/b/c").is_none());
142    /// ```
143    ///
144    /// Note that this is JSON pointer**-like** because `/` is not supported in
145    /// key names because I don't see the point. If you want this it is easy to
146    /// implement.
147    pub fn pointer(&self, pointer: &str) -> Option<&Self> {
148        if pointer.is_empty() {
149            return Some(self);
150        } else if !pointer.starts_with('/') {
151            return None;
152        }
153
154        pointer
155            .split('/')
156            .skip(1)
157            // Don't support / or ~ in key names unless someone actually opens
158            // an issue about it
159            //.map(|x| x.replace("~1", "/").replace("~0", "~"))
160            .try_fold(self, move |target, token| {
161                (match &target.value {
162                    Some(ValueInner::Table(tab)) => tab.get(token),
163                    Some(ValueInner::Array(list)) => parse_index(token).and_then(|x| list.get(x)),
164                    _ => None,
165                })
166                .filter(|v| v.value.is_some())
167            })
168    }
169
170    /// The `mut` version of [`Self::pointer`]
171    pub fn pointer_mut(&mut self, pointer: &'de str) -> Option<&mut Self> {
172        if pointer.is_empty() {
173            return Some(self);
174        } else if !pointer.starts_with('/') {
175            return None;
176        }
177
178        pointer
179            .split('/')
180            .skip(1)
181            // Don't support / or ~ in key names unless someone actually opens
182            // an issue about it
183            //.map(|x| x.replace("~1", "/").replace("~0", "~"))
184            .try_fold(self, |target, token| {
185                (match &mut target.value {
186                    Some(ValueInner::Table(tab)) => tab.get_mut(token),
187                    Some(ValueInner::Array(list)) => {
188                        parse_index(token).and_then(|x| list.get_mut(x))
189                    }
190                    _ => None,
191                })
192                .filter(|v| v.value.is_some())
193            })
194    }
195}
196
197fn parse_index(s: &str) -> Option<usize> {
198    if s.starts_with('+') || (s.starts_with('0') && s.len() != 1) {
199        return None;
200    }
201    s.parse().ok()
202}
203
204impl<'de> AsRef<ValueInner<'de>> for Value<'de> {
205    fn as_ref(&self) -> &ValueInner<'de> {
206        self.value
207            .as_ref()
208            .expect("the value has already been taken")
209    }
210}
211
212impl fmt::Debug for Value<'_> {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        write!(f, "{:?}", self.value)
215    }
216}
217
218/// A toml table key
219#[derive(Clone)]
220pub struct Key<'de> {
221    /// The key itself, in most cases it will be borrowed, but may be owned
222    /// if escape characters are present in the original source
223    pub name: Cow<'de, str>,
224    /// The span for the key in the original document
225    pub span: Span,
226}
227
228impl std::borrow::Borrow<str> for Key<'_> {
229    fn borrow(&self) -> &str {
230        self.name.as_ref()
231    }
232}
233
234impl fmt::Debug for Key<'_> {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        f.write_str(&self.name)
237    }
238}
239
240impl fmt::Display for Key<'_> {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        f.write_str(&self.name)
243    }
244}
245
246impl Ord for Key<'_> {
247    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
248        self.name.cmp(&other.name)
249    }
250}
251
252impl PartialOrd for Key<'_> {
253    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
254        Some(self.cmp(other))
255    }
256}
257
258impl PartialEq for Key<'_> {
259    fn eq(&self, other: &Self) -> bool {
260        self.name.eq(&other.name)
261    }
262}
263
264impl Eq for Key<'_> {}
265
266/// A toml table, always represented as a sorted map.
267///
268/// The original key ordering can be obtained by ordering the keys by their span
269pub type Table<'de> = std::collections::BTreeMap<Key<'de>, Value<'de>>;
270/// A toml array
271pub type Array<'de> = Vec<Value<'de>>;
272
273/// The core value types that toml can deserialize to
274///
275/// Note that this library does not support datetime values that are part of the
276/// toml spec since I have no need of them, but could be added
277#[derive(Debug)]
278pub enum ValueInner<'de> {
279    /// A string.
280    ///
281    /// This will be borrowed from the original toml source unless it contains
282    /// escape characters
283    String(Cow<'de, str>),
284    /// An integer
285    Integer(i64),
286    /// A float
287    Float(f64),
288    /// A boolean
289    Boolean(bool),
290    /// An array
291    Array(Array<'de>),
292    /// A table
293    Table(Table<'de>),
294}
295
296impl<'de> ValueInner<'de> {
297    /// Gets the type of the value as a string
298    pub fn type_str(&self) -> &'static str {
299        match self {
300            Self::String(..) => "string",
301            Self::Integer(..) => "integer",
302            Self::Float(..) => "float",
303            Self::Boolean(..) => "boolean",
304            Self::Array(..) => "array",
305            Self::Table(..) => "table",
306        }
307    }
308
309    /// Returns a borrowed string if this is a [`Self::String`]
310    #[inline]
311    pub fn as_str(&self) -> Option<&str> {
312        if let Self::String(s) = self {
313            Some(s.as_ref())
314        } else {
315            None
316        }
317    }
318
319    /// Returns a borrowed table if this is a [`Self::Table`]
320    #[inline]
321    pub fn as_table(&self) -> Option<&Table<'de>> {
322        if let ValueInner::Table(t) = self {
323            Some(t)
324        } else {
325            None
326        }
327    }
328
329    /// Returns a borrowed array if this is a [`Self::Array`]
330    #[inline]
331    pub fn as_array(&self) -> Option<&Array<'de>> {
332        if let ValueInner::Array(a) = self {
333            Some(a)
334        } else {
335            None
336        }
337    }
338
339    /// Returns an `i64` if this is a [`Self::Integer`]
340    #[inline]
341    pub fn as_integer(&self) -> Option<i64> {
342        if let ValueInner::Integer(i) = self {
343            Some(*i)
344        } else {
345            None
346        }
347    }
348
349    /// Returns an `f64` if this is a [`Self::Float`]
350    #[inline]
351    pub fn as_float(&self) -> Option<f64> {
352        if let ValueInner::Float(f) = self {
353            Some(*f)
354        } else {
355            None
356        }
357    }
358
359    /// Returns a `bool` if this is a [`Self::Boolean`]
360    #[inline]
361    pub fn as_bool(&self) -> Option<bool> {
362        if let ValueInner::Boolean(b) = self {
363            Some(*b)
364        } else {
365            None
366        }
367    }
368}