toml_edit/
key.rs

1use std::borrow::Cow;
2use std::str::FromStr;
3
4use crate::repr::{Decor, Repr};
5use crate::InternalString;
6
7/// Key as part of a Key/Value Pair or a table header.
8///
9/// # Examples
10///
11/// ```notrust
12/// [dependencies."nom"]
13/// version = "5.0"
14/// 'literal key' = "nonsense"
15/// "basic string key" = 42
16/// ```
17///
18/// There are 3 types of keys:
19///
20/// 1. Bare keys (`version` and `dependencies`)
21///
22/// 2. Basic quoted keys (`"basic string key"` and `"nom"`)
23///
24/// 3. Literal quoted keys (`'literal key'`)
25///
26/// For details see [toml spec](https://github.com/toml-lang/toml/#keyvalue-pair).
27///
28/// To parse a key use `FromStr` trait implementation: `"string".parse::<Key>()`.
29#[derive(Debug)]
30pub struct Key {
31    key: InternalString,
32    pub(crate) repr: Option<Repr>,
33    pub(crate) leaf_decor: Decor,
34    pub(crate) dotted_decor: Decor,
35}
36
37impl Key {
38    /// Create a new table key
39    pub fn new(key: impl Into<InternalString>) -> Self {
40        Self {
41            key: key.into(),
42            repr: None,
43            leaf_decor: Default::default(),
44            dotted_decor: Default::default(),
45        }
46    }
47
48    /// Parse a TOML key expression
49    ///
50    /// Unlike `"".parse<Key>()`, this supports dotted keys.
51    #[cfg(feature = "parse")]
52    pub fn parse(repr: &str) -> Result<Vec<Self>, crate::TomlError> {
53        Self::try_parse_path(repr)
54    }
55
56    pub(crate) fn with_repr_unchecked(mut self, repr: Repr) -> Self {
57        self.repr = Some(repr);
58        self
59    }
60
61    /// While creating the `Key`, add `Decor` to it
62    #[deprecated(since = "0.21.1", note = "Replaced with `with_leaf_decor`")]
63    pub fn with_decor(self, decor: Decor) -> Self {
64        self.with_leaf_decor(decor)
65    }
66
67    /// While creating the `Key`, add `Decor` to it for the line entry
68    pub fn with_leaf_decor(mut self, decor: Decor) -> Self {
69        self.leaf_decor = decor;
70        self
71    }
72
73    /// While creating the `Key`, add `Decor` to it for between dots
74    pub fn with_dotted_decor(mut self, decor: Decor) -> Self {
75        self.dotted_decor = decor;
76        self
77    }
78
79    /// Access a mutable proxy for the `Key`.
80    pub fn as_mut(&mut self) -> KeyMut<'_> {
81        KeyMut { key: self }
82    }
83
84    /// Returns the parsed key value.
85    pub fn get(&self) -> &str {
86        &self.key
87    }
88
89    /// Returns key raw representation, if available.
90    pub fn as_repr(&self) -> Option<&Repr> {
91        self.repr.as_ref()
92    }
93
94    /// Returns the default raw representation.
95    #[cfg(feature = "display")]
96    pub fn default_repr(&self) -> Repr {
97        to_key_repr(&self.key)
98    }
99
100    /// Returns a raw representation.
101    #[cfg(feature = "display")]
102    pub fn display_repr(&self) -> Cow<'_, str> {
103        self.as_repr()
104            .and_then(|r| r.as_raw().as_str())
105            .map(Cow::Borrowed)
106            .unwrap_or_else(|| {
107                Cow::Owned(self.default_repr().as_raw().as_str().unwrap().to_owned())
108            })
109    }
110
111    /// Returns the surrounding whitespace
112    #[deprecated(
113        since = "0.21.1",
114        note = "Replaced with `dotted_decor_mut`, `leaf_decor_mut"
115    )]
116    pub fn decor_mut(&mut self) -> &mut Decor {
117        self.leaf_decor_mut()
118    }
119
120    /// Returns the surrounding whitespace for the line entry
121    pub fn leaf_decor_mut(&mut self) -> &mut Decor {
122        &mut self.leaf_decor
123    }
124
125    /// Returns the surrounding whitespace for between dots
126    pub fn dotted_decor_mut(&mut self) -> &mut Decor {
127        &mut self.dotted_decor
128    }
129
130    /// Returns the surrounding whitespace
131    #[deprecated(since = "0.21.1", note = "Replaced with `dotted_decor`, `leaf_decor")]
132    pub fn decor(&self) -> &Decor {
133        self.leaf_decor()
134    }
135
136    /// Returns the surrounding whitespace for the line entry
137    pub fn leaf_decor(&self) -> &Decor {
138        &self.leaf_decor
139    }
140
141    /// Returns the surrounding whitespace for between dots
142    pub fn dotted_decor(&self) -> &Decor {
143        &self.dotted_decor
144    }
145
146    /// The location within the original document
147    ///
148    /// This generally requires an [`ImDocument`][crate::ImDocument].
149    pub fn span(&self) -> Option<std::ops::Range<usize>> {
150        self.repr.as_ref().and_then(|r| r.span())
151    }
152
153    pub(crate) fn despan(&mut self, input: &str) {
154        self.leaf_decor.despan(input);
155        self.dotted_decor.despan(input);
156        if let Some(repr) = &mut self.repr {
157            repr.despan(input);
158        }
159    }
160
161    /// Auto formats the key.
162    pub fn fmt(&mut self) {
163        self.repr = None;
164        self.leaf_decor.clear();
165        self.dotted_decor.clear();
166    }
167
168    #[cfg(feature = "parse")]
169    fn try_parse_simple(s: &str) -> Result<Key, crate::TomlError> {
170        let mut key = crate::parser::parse_key(s)?;
171        key.despan(s);
172        Ok(key)
173    }
174
175    #[cfg(feature = "parse")]
176    fn try_parse_path(s: &str) -> Result<Vec<Key>, crate::TomlError> {
177        let mut keys = crate::parser::parse_key_path(s)?;
178        for key in &mut keys {
179            key.despan(s);
180        }
181        Ok(keys)
182    }
183}
184
185impl Clone for Key {
186    #[inline(never)]
187    fn clone(&self) -> Self {
188        Self {
189            key: self.key.clone(),
190            repr: self.repr.clone(),
191            leaf_decor: self.leaf_decor.clone(),
192            dotted_decor: self.dotted_decor.clone(),
193        }
194    }
195}
196
197impl std::ops::Deref for Key {
198    type Target = str;
199
200    fn deref(&self) -> &Self::Target {
201        self.get()
202    }
203}
204
205impl std::borrow::Borrow<str> for Key {
206    #[inline]
207    fn borrow(&self) -> &str {
208        self.get()
209    }
210}
211
212impl std::hash::Hash for Key {
213    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
214        self.get().hash(state);
215    }
216}
217
218impl Ord for Key {
219    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
220        self.get().cmp(other.get())
221    }
222}
223
224impl PartialOrd for Key {
225    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
226        Some(self.cmp(other))
227    }
228}
229
230impl Eq for Key {}
231
232impl PartialEq for Key {
233    #[inline]
234    fn eq(&self, other: &Key) -> bool {
235        PartialEq::eq(self.get(), other.get())
236    }
237}
238
239impl PartialEq<str> for Key {
240    #[inline]
241    fn eq(&self, other: &str) -> bool {
242        PartialEq::eq(self.get(), other)
243    }
244}
245
246impl PartialEq<&str> for Key {
247    #[inline]
248    fn eq(&self, other: &&str) -> bool {
249        PartialEq::eq(self.get(), *other)
250    }
251}
252
253impl PartialEq<String> for Key {
254    #[inline]
255    fn eq(&self, other: &String) -> bool {
256        PartialEq::eq(self.get(), other.as_str())
257    }
258}
259
260#[cfg(feature = "display")]
261impl std::fmt::Display for Key {
262    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263        crate::encode::encode_key(self, f, None)
264    }
265}
266
267#[cfg(feature = "parse")]
268impl FromStr for Key {
269    type Err = crate::TomlError;
270
271    /// Tries to parse a key from a &str,
272    /// if fails, tries as basic quoted key (surrounds with "")
273    /// and then literal quoted key (surrounds with '')
274    fn from_str(s: &str) -> Result<Self, Self::Err> {
275        Key::try_parse_simple(s)
276    }
277}
278
279#[cfg(feature = "display")]
280fn to_key_repr(key: &str) -> Repr {
281    #[cfg(feature = "parse")]
282    {
283        if key
284            .as_bytes()
285            .iter()
286            .copied()
287            .all(crate::parser::key::is_unquoted_char)
288            && !key.is_empty()
289        {
290            Repr::new_unchecked(key)
291        } else {
292            crate::encode::to_string_repr(
293                key,
294                Some(crate::encode::StringStyle::OnelineSingle),
295                None,
296            )
297        }
298    }
299    #[cfg(not(feature = "parse"))]
300    {
301        crate::encode::to_string_repr(key, Some(crate::encode::StringStyle::OnelineSingle), None)
302    }
303}
304
305impl<'b> From<&'b str> for Key {
306    fn from(s: &'b str) -> Self {
307        Key::new(s)
308    }
309}
310
311impl<'b> From<&'b String> for Key {
312    fn from(s: &'b String) -> Self {
313        Key::new(s)
314    }
315}
316
317impl From<String> for Key {
318    fn from(s: String) -> Self {
319        Key::new(s)
320    }
321}
322
323impl From<InternalString> for Key {
324    fn from(s: InternalString) -> Self {
325        Key::new(s)
326    }
327}
328
329#[doc(hidden)]
330impl From<Key> for InternalString {
331    fn from(key: Key) -> InternalString {
332        key.key
333    }
334}
335
336/// A mutable reference to a `Key`'s formatting
337#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
338pub struct KeyMut<'k> {
339    key: &'k mut Key,
340}
341
342impl KeyMut<'_> {
343    /// Returns the parsed key value.
344    pub fn get(&self) -> &str {
345        self.key.get()
346    }
347
348    /// Returns the raw representation, if available.
349    pub fn as_repr(&self) -> Option<&Repr> {
350        self.key.as_repr()
351    }
352
353    /// Returns the default raw representation.
354    #[cfg(feature = "display")]
355    pub fn default_repr(&self) -> Repr {
356        self.key.default_repr()
357    }
358
359    /// Returns a raw representation.
360    #[cfg(feature = "display")]
361    pub fn display_repr(&self) -> Cow<'_, str> {
362        self.key.display_repr()
363    }
364
365    /// Returns the surrounding whitespace
366    #[deprecated(
367        since = "0.21.1",
368        note = "Replaced with `dotted_decor_mut`, `leaf_decor_mut"
369    )]
370    pub fn decor_mut(&mut self) -> &mut Decor {
371        #![allow(deprecated)]
372        self.key.decor_mut()
373    }
374
375    /// Returns the surrounding whitespace for the line entry
376    pub fn leaf_decor_mut(&mut self) -> &mut Decor {
377        self.key.leaf_decor_mut()
378    }
379
380    /// Returns the surrounding whitespace for between dots
381    pub fn dotted_decor_mut(&mut self) -> &mut Decor {
382        self.key.dotted_decor_mut()
383    }
384
385    /// Returns the surrounding whitespace
386    #[deprecated(since = "0.21.1", note = "Replaced with `dotted_decor`, `leaf_decor")]
387    pub fn decor(&self) -> &Decor {
388        #![allow(deprecated)]
389        self.key.decor()
390    }
391
392    /// Returns the surrounding whitespace for the line entry
393    pub fn leaf_decor(&self) -> &Decor {
394        self.key.leaf_decor()
395    }
396
397    /// Returns the surrounding whitespace for between dots
398    pub fn dotted_decor(&self) -> &Decor {
399        self.key.dotted_decor()
400    }
401
402    /// Auto formats the key.
403    pub fn fmt(&mut self) {
404        self.key.fmt();
405    }
406}
407
408impl std::ops::Deref for KeyMut<'_> {
409    type Target = str;
410
411    fn deref(&self) -> &Self::Target {
412        self.get()
413    }
414}
415
416impl PartialEq<str> for KeyMut<'_> {
417    #[inline]
418    fn eq(&self, other: &str) -> bool {
419        PartialEq::eq(self.get(), other)
420    }
421}
422
423impl<'s> PartialEq<&'s str> for KeyMut<'s> {
424    #[inline]
425    fn eq(&self, other: &&str) -> bool {
426        PartialEq::eq(self.get(), *other)
427    }
428}
429
430impl PartialEq<String> for KeyMut<'_> {
431    #[inline]
432    fn eq(&self, other: &String) -> bool {
433        PartialEq::eq(self.get(), other.as_str())
434    }
435}
436
437#[cfg(feature = "display")]
438impl std::fmt::Display for KeyMut<'_> {
439    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
440        std::fmt::Display::fmt(&self.key, f)
441    }
442}
443
444#[test]
445#[cfg(feature = "parse")]
446#[cfg(feature = "display")]
447fn string_roundtrip() {
448    Key::new("hello").to_string().parse::<Key>().unwrap();
449}