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