toml_edit/
key.rs

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