1 use std::ops::RangeInclusive;
2
3 use winnow::combinator::peek;
4 use winnow::combinator::separated1;
5 use winnow::token::any;
6 use winnow::token::take_while;
7 use winnow::trace::trace;
8
9 use crate::key::Key;
10 use crate::parser::error::CustomError;
11 use crate::parser::prelude::*;
12 use crate::parser::strings::{basic_string, literal_string};
13 use crate::parser::trivia::{from_utf8_unchecked, ws};
14 use crate::repr::{Decor, Repr};
15 use crate::InternalString;
16 use crate::RawString;
17
18 // key = simple-key / dotted-key
19 // dotted-key = simple-key 1*( dot-sep simple-key )
key(input: &mut Input<'_>) -> PResult<Vec<Key>>20 pub(crate) fn key(input: &mut Input<'_>) -> PResult<Vec<Key>> {
21 let mut key_path = 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_dotted_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 // Inserting the key will require recursion down the line
37 RecursionCheck::check_depth(k.len())?;
38 Ok::<_, CustomError>(k)
39 }),
40 )
41 .parse_next(input)?;
42
43 let mut leaf_decor = Decor::new("", "");
44 {
45 let first_dotted_decor = key_path
46 .first_mut()
47 .expect("always at least one key")
48 .dotted_decor_mut();
49 if let Some(prefix) = first_dotted_decor.prefix().cloned() {
50 leaf_decor.set_prefix(prefix);
51 first_dotted_decor.set_prefix("");
52 }
53 }
54 let last_key = &mut key_path.last_mut().expect("always at least one key");
55 {
56 let last_dotted_decor = last_key.dotted_decor_mut();
57 if let Some(suffix) = last_dotted_decor.suffix().cloned() {
58 leaf_decor.set_suffix(suffix);
59 last_dotted_decor.set_suffix("");
60 }
61 }
62
63 *last_key.leaf_decor_mut() = leaf_decor;
64
65 Ok(key_path)
66 }
67
68 // simple-key = quoted-key / unquoted-key
69 // quoted-key = basic-string / literal-string
simple_key(input: &mut Input<'_>) -> PResult<(RawString, InternalString)>70 pub(crate) fn simple_key(input: &mut Input<'_>) -> PResult<(RawString, InternalString)> {
71 trace(
72 "simple-key",
73 dispatch! {peek(any);
74 crate::parser::strings::QUOTATION_MARK => basic_string
75 .map(|s: std::borrow::Cow<'_, str>| s.as_ref().into()),
76 crate::parser::strings::APOSTROPHE => literal_string.map(|s: &str| s.into()),
77 _ => unquoted_key.map(|s: &str| s.into()),
78 }
79 .with_span()
80 .map(|(k, span)| {
81 let raw = RawString::with_span(span);
82 (raw, k)
83 }),
84 )
85 .parse_next(input)
86 }
87
88 // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _
unquoted_key<'i>(input: &mut Input<'i>) -> PResult<&'i str>89 fn unquoted_key<'i>(input: &mut Input<'i>) -> PResult<&'i str> {
90 trace(
91 "unquoted-key",
92 take_while(1.., UNQUOTED_CHAR)
93 .map(|b| unsafe { from_utf8_unchecked(b, "`is_unquoted_char` filters out on-ASCII") }),
94 )
95 .parse_next(input)
96 }
97
is_unquoted_char(c: u8) -> bool98 pub(crate) fn is_unquoted_char(c: u8) -> bool {
99 use winnow::stream::ContainsToken;
100 UNQUOTED_CHAR.contains_token(c)
101 }
102
103 const UNQUOTED_CHAR: (
104 RangeInclusive<u8>,
105 RangeInclusive<u8>,
106 RangeInclusive<u8>,
107 u8,
108 u8,
109 ) = (b'A'..=b'Z', b'a'..=b'z', b'0'..=b'9', b'-', b'_');
110
111 // dot-sep = ws %x2E ws ; . Period
112 const DOT_SEP: u8 = b'.';
113
114 #[cfg(test)]
115 #[cfg(feature = "parse")]
116 #[cfg(feature = "display")]
117 mod test {
118 use super::*;
119
120 #[test]
keys()121 fn keys() {
122 let cases = [
123 ("a", "a"),
124 (r#""hello\n ""#, "hello\n "),
125 (r"'hello\n '", "hello\\n "),
126 ];
127
128 for (input, expected) in cases {
129 dbg!(input);
130 let parsed = simple_key.parse(new_input(input));
131 assert_eq!(
132 parsed,
133 Ok((RawString::with_span(0..(input.len())), expected.into())),
134 "Parsing {input:?}"
135 );
136 }
137 }
138 }
139