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