toml_edit/parser/
key.rs
1use std::ops::RangeInclusive;
2
3use winnow::combinator::peek;
4use winnow::combinator::separated1;
5use winnow::token::any;
6use winnow::token::take_while;
7use winnow::trace::trace;
8
9use crate::key::Key;
10use crate::parser::errors::CustomError;
11use crate::parser::prelude::*;
12use crate::parser::strings::{basic_string, literal_string};
13use crate::parser::trivia::{from_utf8_unchecked, ws};
14use crate::repr::{Decor, Repr};
15use crate::InternalString;
16use crate::RawString;
17
18pub(crate) fn key(input: &mut Input<'_>) -> PResult<Vec<Key>> {
21 trace(
22 "dotted-key",
23 separated1(
24 (ws.span(), simple_key, ws.span()).map(|(pre, (raw, key), suffix)| {
25 Key::new(key)
26 .with_repr_unchecked(Repr::new_unchecked(raw))
27 .with_decor(Decor::new(
28 RawString::with_span(pre),
29 RawString::with_span(suffix),
30 ))
31 }),
32 DOT_SEP,
33 )
34 .context(StrContext::Label("key"))
35 .try_map(|k: Vec<_>| {
36 RecursionCheck::check_depth(k.len())?;
38 Ok::<_, CustomError>(k)
39 }),
40 )
41 .parse_next(input)
42}
43
44pub(crate) fn simple_key(input: &mut Input<'_>) -> PResult<(RawString, InternalString)> {
47 trace(
48 "simple-key",
49 dispatch! {peek(any);
50 crate::parser::strings::QUOTATION_MARK => basic_string
51 .map(|s: std::borrow::Cow<'_, str>| s.as_ref().into()),
52 crate::parser::strings::APOSTROPHE => literal_string.map(|s: &str| s.into()),
53 _ => unquoted_key.map(|s: &str| s.into()),
54 }
55 .with_span()
56 .map(|(k, span)| {
57 let raw = RawString::with_span(span);
58 (raw, k)
59 }),
60 )
61 .parse_next(input)
62}
63
64fn unquoted_key<'i>(input: &mut Input<'i>) -> PResult<&'i str> {
66 trace(
67 "unquoted-key",
68 take_while(1.., UNQUOTED_CHAR)
69 .map(|b| unsafe { from_utf8_unchecked(b, "`is_unquoted_char` filters out on-ASCII") }),
70 )
71 .parse_next(input)
72}
73
74pub(crate) fn is_unquoted_char(c: u8) -> bool {
75 use winnow::stream::ContainsToken;
76 UNQUOTED_CHAR.contains_token(c)
77}
78
79const UNQUOTED_CHAR: (
80 RangeInclusive<u8>,
81 RangeInclusive<u8>,
82 RangeInclusive<u8>,
83 u8,
84 u8,
85) = (b'A'..=b'Z', b'a'..=b'z', b'0'..=b'9', b'-', b'_');
86
87const DOT_SEP: u8 = b'.';
89
90#[cfg(test)]
91mod test {
92 use super::*;
93
94 #[test]
95 fn keys() {
96 let cases = [
97 ("a", "a"),
98 (r#""hello\n ""#, "hello\n "),
99 (r#"'hello\n '"#, "hello\\n "),
100 ];
101
102 for (input, expected) in cases {
103 dbg!(input);
104 let parsed = simple_key.parse(new_input(input));
105 assert_eq!(
106 parsed,
107 Ok((RawString::with_span(0..(input.len())), expected.into())),
108 "Parsing {input:?}"
109 );
110 }
111 }
112}