• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use winnow::combinator::cut_err;
2 use winnow::combinator::delimited;
3 use winnow::combinator::separated;
4 use winnow::combinator::trace;
5 use winnow::token::one_of;
6 
7 use crate::key::Key;
8 use crate::parser::error::CustomError;
9 use crate::parser::key::key;
10 use crate::parser::prelude::*;
11 use crate::parser::trivia::ws;
12 use crate::parser::value::value;
13 use crate::table::TableKeyValue;
14 use crate::{InlineTable, InternalString, Item, RawString, Value};
15 
16 use indexmap::map::Entry;
17 
18 // ;; Inline Table
19 
20 // inline-table = inline-table-open inline-table-keyvals inline-table-close
inline_table<'i>(input: &mut Input<'i>) -> PResult<InlineTable>21 pub(crate) fn inline_table<'i>(input: &mut Input<'i>) -> PResult<InlineTable> {
22     trace("inline-table", move |input: &mut Input<'i>| {
23         delimited(
24             INLINE_TABLE_OPEN,
25             cut_err(inline_table_keyvals.try_map(|(kv, p)| table_from_pairs(kv, p))),
26             cut_err(INLINE_TABLE_CLOSE)
27                 .context(StrContext::Label("inline table"))
28                 .context(StrContext::Expected(StrContextValue::CharLiteral('}'))),
29         )
30         .parse_next(input)
31     })
32     .parse_next(input)
33 }
34 
table_from_pairs( v: Vec<(Vec<Key>, TableKeyValue)>, preamble: RawString, ) -> Result<InlineTable, CustomError>35 fn table_from_pairs(
36     v: Vec<(Vec<Key>, TableKeyValue)>,
37     preamble: RawString,
38 ) -> Result<InlineTable, CustomError> {
39     let mut root = InlineTable::new();
40     root.set_preamble(preamble);
41     // Assuming almost all pairs will be directly in `root`
42     root.items.reserve(v.len());
43 
44     for (path, kv) in v {
45         let table = descend_path(&mut root, &path)?;
46 
47         // "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed"
48         let mixed_table_types = table.is_dotted() == path.is_empty();
49         if mixed_table_types {
50             return Err(CustomError::DuplicateKey {
51                 key: kv.key.get().into(),
52                 table: None,
53             });
54         }
55 
56         let key: InternalString = kv.key.get_internal().into();
57         match table.items.entry(key) {
58             Entry::Vacant(o) => {
59                 o.insert(kv);
60             }
61             Entry::Occupied(o) => {
62                 return Err(CustomError::DuplicateKey {
63                     key: o.key().as_str().into(),
64                     table: None,
65                 });
66             }
67         }
68     }
69     Ok(root)
70 }
71 
descend_path<'a>( mut table: &'a mut InlineTable, path: &'a [Key], ) -> Result<&'a mut InlineTable, CustomError>72 fn descend_path<'a>(
73     mut table: &'a mut InlineTable,
74     path: &'a [Key],
75 ) -> Result<&'a mut InlineTable, CustomError> {
76     let dotted = !path.is_empty();
77     for (i, key) in path.iter().enumerate() {
78         let entry = table.entry_format(key).or_insert_with(|| {
79             let mut new_table = InlineTable::new();
80             new_table.set_implicit(dotted);
81             new_table.set_dotted(dotted);
82 
83             Value::InlineTable(new_table)
84         });
85         match *entry {
86             Value::InlineTable(ref mut sweet_child_of_mine) => {
87                 // Since tables cannot be defined more than once, redefining such tables using a
88                 // [table] header is not allowed. Likewise, using dotted keys to redefine tables
89                 // already defined in [table] form is not allowed.
90                 if dotted && !sweet_child_of_mine.is_implicit() {
91                     return Err(CustomError::DuplicateKey {
92                         key: key.get().into(),
93                         table: None,
94                     });
95                 }
96                 table = sweet_child_of_mine;
97             }
98             ref v => {
99                 return Err(CustomError::extend_wrong_type(path, i, v.type_name()));
100             }
101         }
102     }
103     Ok(table)
104 }
105 
106 // inline-table-open  = %x7B ws     ; {
107 pub(crate) const INLINE_TABLE_OPEN: u8 = b'{';
108 // inline-table-close = ws %x7D     ; }
109 const INLINE_TABLE_CLOSE: u8 = b'}';
110 // inline-table-sep   = ws %x2C ws  ; , Comma
111 const INLINE_TABLE_SEP: u8 = b',';
112 // keyval-sep = ws %x3D ws ; =
113 pub(crate) const KEYVAL_SEP: u8 = b'=';
114 
115 // inline-table-keyvals = [ inline-table-keyvals-non-empty ]
116 // inline-table-keyvals-non-empty =
117 // ( key keyval-sep val inline-table-sep inline-table-keyvals-non-empty ) /
118 // ( key keyval-sep val )
119 
inline_table_keyvals( input: &mut Input<'_>, ) -> PResult<(Vec<(Vec<Key>, TableKeyValue)>, RawString)>120 fn inline_table_keyvals(
121     input: &mut Input<'_>,
122 ) -> PResult<(Vec<(Vec<Key>, TableKeyValue)>, RawString)> {
123     (
124         separated(0.., keyval, INLINE_TABLE_SEP),
125         ws.span().map(RawString::with_span),
126     )
127         .parse_next(input)
128 }
129 
keyval(input: &mut Input<'_>) -> PResult<(Vec<Key>, TableKeyValue)>130 fn keyval(input: &mut Input<'_>) -> PResult<(Vec<Key>, TableKeyValue)> {
131     (
132         key,
133         cut_err((
134             one_of(KEYVAL_SEP)
135                 .context(StrContext::Expected(StrContextValue::CharLiteral('.')))
136                 .context(StrContext::Expected(StrContextValue::CharLiteral('='))),
137             (ws.span(), value, ws.span()),
138         )),
139     )
140         .map(|(key, (_, v))| {
141             let mut path = key;
142             let key = path.pop().expect("grammar ensures at least 1");
143 
144             let (pre, v, suf) = v;
145             let pre = RawString::with_span(pre);
146             let suf = RawString::with_span(suf);
147             let v = v.decorated(pre, suf);
148             (
149                 path,
150                 TableKeyValue {
151                     key,
152                     value: Item::Value(v),
153                 },
154             )
155         })
156         .parse_next(input)
157 }
158 
159 #[cfg(test)]
160 #[cfg(feature = "parse")]
161 #[cfg(feature = "display")]
162 mod test {
163     use super::*;
164 
165     #[test]
inline_tables()166     fn inline_tables() {
167         let inputs = [
168             r#"{}"#,
169             r#"{   }"#,
170             r#"{a = 1e165}"#,
171             r#"{ hello = "world", a = 1}"#,
172             r#"{ hello.world = "a" }"#,
173         ];
174         for input in inputs {
175             dbg!(input);
176             let mut parsed = inline_table.parse(new_input(input));
177             if let Ok(parsed) = &mut parsed {
178                 parsed.despan(input);
179             }
180             assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
181         }
182     }
183 
184     #[test]
invalid_inline_tables()185     fn invalid_inline_tables() {
186         let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#];
187         for input in invalid_inputs {
188             dbg!(input);
189             let mut parsed = inline_table.parse(new_input(input));
190             if let Ok(parsed) = &mut parsed {
191                 parsed.despan(input);
192             }
193             assert!(parsed.is_err());
194         }
195     }
196 }
197