1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2025 Orange
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *          http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
use crate::ast::{SourceInfo, Template, TemplateElement};
19
use crate::parser::primitives::try_literal;
20
use crate::parser::{string, ParseError, ParseErrorKind, ParseResult};
21
use crate::reader::Reader;
22

            
23
use super::placeholder;
24

            
25
33780
pub fn parse(reader: &mut Reader) -> ParseResult<Template> {
26
33780
    let start = reader.cursor();
27
33780
    let mut elements = vec![];
28
    loop {
29
65605
        let save_state = reader.cursor();
30
65605
        match placeholder::parse(reader) {
31
230
            Ok(placeholder) => {
32
230
                let element = TemplateElement::Placeholder(placeholder);
33
230
                elements.push(element);
34
            }
35
65375
            Err(e) => {
36
65375
                if e.recoverable {
37
65375
                    reader.seek(save_state);
38
65375
                    let value = key_string_content(reader)?;
39
65375
                    if value.is_empty() {
40
33780
                        break;
41
                    }
42
31595
                    let encoded = reader.read_from(start.index);
43
31595
                    let element = TemplateElement::String { value, encoded };
44
31595
                    elements.push(element);
45
                } else {
46
                    return Err(e);
47
                }
48
            }
49
        }
50
    }
51
33780
    if elements.is_empty() {
52
1955
        let kind = ParseErrorKind::Expecting {
53
1955
            value: "key-string".to_string(),
54
1955
        };
55
1955
        return Err(ParseError::new(start.pos, false, kind));
56
    }
57
31825
    if let Some(TemplateElement::String { encoded, .. }) = elements.first() {
58
31595
        if encoded.starts_with('[') {
59
10830
            let kind = ParseErrorKind::Expecting {
60
10830
                value: "key-string".to_string(),
61
10830
            };
62
10830
            return Err(ParseError::new(start.pos, false, kind));
63
        }
64
    }
65

            
66
20995
    let end = reader.cursor();
67
20995
    Ok(Template {
68
20995
        delimiter: None,
69
20995
        elements,
70
20995
        source_info: SourceInfo::new(start.pos, end.pos),
71
20995
    })
72
}
73

            
74
65375
fn key_string_content(reader: &mut Reader) -> ParseResult<String> {
75
65375
    let mut s = String::new();
76
    loop {
77
97090
        match key_string_escaped_char(reader) {
78
80
            Ok(c) => {
79
80
                s.push(c);
80
            }
81
97010
            Err(e) => {
82
97010
                if e.recoverable {
83
97010
                    let s2 = key_string_text(reader);
84
97010
                    if s2.is_empty() {
85
65375
                        break;
86
31635
                    } else {
87
31635
                        s.push_str(&s2);
88
                    }
89
                } else {
90
                    return Err(e);
91
                }
92
            }
93
        }
94
    }
95
65375
    Ok(s)
96
}
97

            
98
97010
fn key_string_text(reader: &mut Reader) -> String {
99
97010
    let mut s = String::new();
100
    loop {
101
305475
        let save = reader.cursor();
102
305475
        match reader.read() {
103
345
            None => break,
104
305130
            Some(c) => {
105
305130
                if c.is_alphanumeric()
106
121330
                    || c == '_'
107
120385
                    || c == '-'
108
118590
                    || c == '.'
109
118535
                    || c == '['
110
107665
                    || c == ']'
111
96800
                    || c == '@'
112
96745
                    || c == '$'
113
208465
                {
114
208465
                    s.push(c);
115
208465
                } else {
116
96665
                    reader.seek(save);
117
96665
                    break;
118
                }
119
            }
120
        }
121
    }
122

            
123
97010
    s
124
}
125

            
126
97090
fn key_string_escaped_char(reader: &mut Reader) -> ParseResult<char> {
127
97090
    try_literal("\\", reader)?;
128
80
    let start = reader.cursor();
129
80
    match reader.read() {
130
        Some('#') => Ok('#'),
131
        Some(':') => Ok(':'),
132
        Some('\\') => Ok('\\'),
133
        Some('/') => Ok('/'),
134
        Some('b') => Ok('\x08'),
135
        Some('f') => Ok('\x0c'),
136
        Some('n') => Ok('\n'),
137
        Some('r') => Ok('\r'),
138
        Some('t') => Ok('\t'),
139
80
        Some('u') => string::unicode(reader),
140
        _ => Err(ParseError::new(
141
            start.pos,
142
            false,
143
            ParseErrorKind::EscapeChar,
144
        )),
145
    }
146
}
147

            
148
#[cfg(test)]
149
mod tests {
150
    use super::*;
151
    use crate::reader::Pos;
152

            
153
    #[test]
154
    fn test_key_string() {
155
        let mut reader = Reader::new("aaa\\: ");
156
        assert_eq!(
157
            parse(&mut reader).unwrap(),
158
            Template {
159
                delimiter: None,
160
                elements: vec![TemplateElement::String {
161
                    value: "aaa:".to_string(),
162
                    encoded: "aaa\\:".to_string(),
163
                }],
164
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6)),
165
            }
166
        );
167
        assert_eq!(reader.cursor().index, 5);
168

            
169
        let mut reader = Reader::new("$top:");
170
        assert_eq!(
171
            parse(&mut reader).unwrap(),
172
            Template {
173
                delimiter: None,
174
                elements: vec![TemplateElement::String {
175
                    value: "$top".to_string(),
176
                    encoded: "$top".to_string(),
177
                }],
178
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
179
            }
180
        );
181
        assert_eq!(reader.cursor().index, 4);
182

            
183
        let mut reader = Reader::new("key\\u{20}\\u{3a} :");
184
        assert_eq!(
185
            parse(&mut reader).unwrap(),
186
            Template {
187
                delimiter: None,
188
                elements: vec![TemplateElement::String {
189
                    value: "key :".to_string(),
190
                    encoded: "key\\u{20}\\u{3a}".to_string(),
191
                }],
192
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 16)),
193
            }
194
        );
195
        assert_eq!(reader.cursor().index, 15);
196

            
197
        let mut reader = Reader::new("values\\u{5b}0\\u{5d} :");
198
        assert_eq!(
199
            parse(&mut reader).unwrap(),
200
            Template {
201
                delimiter: None,
202
                elements: vec![TemplateElement::String {
203
                    value: "values[0]".to_string(),
204
                    encoded: "values\\u{5b}0\\u{5d}".to_string(),
205
                }],
206
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 20)),
207
            }
208
        );
209
        assert_eq!(reader.cursor().index, 19);
210

            
211
        let mut reader = Reader::new("values[0] :");
212
        assert_eq!(
213
            parse(&mut reader).unwrap(),
214
            Template {
215
                delimiter: None,
216
                elements: vec![TemplateElement::String {
217
                    value: "values[0]".to_string(),
218
                    encoded: "values[0]".to_string(),
219
                }],
220
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 10)),
221
            }
222
        );
223
        assert_eq!(reader.cursor().index, 9);
224

            
225
        let mut reader = Reader::new("\\u{5b}0\\u{5d}");
226
        assert_eq!(
227
            parse(&mut reader).unwrap(),
228
            Template {
229
                delimiter: None,
230
                elements: vec![TemplateElement::String {
231
                    value: "[0]".to_string(),
232
                    encoded: "\\u{5b}0\\u{5d}".to_string(),
233
                }],
234
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 14)),
235
            }
236
        );
237
        assert_eq!(reader.cursor().index, 13);
238
    }
239

            
240
    #[test]
241
    fn test_key_string_error() {
242
        let mut reader = Reader::new("");
243
        let error = parse(&mut reader).err().unwrap();
244
        assert!(!error.recoverable);
245
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
246

            
247
        let mut reader = Reader::new("{{key");
248
        let error = parse(&mut reader).err().unwrap();
249
        assert!(!error.recoverable);
250
        assert_eq!(error.pos, Pos { line: 1, column: 6 });
251

            
252
        // key string can not start with a '[' (reserved for section)
253
        let mut reader = Reader::new("[0]:");
254
        let error = parse(&mut reader).err().unwrap();
255
        assert!(!error.recoverable);
256
        assert_eq!(reader.cursor().index, 3);
257

            
258
        let mut reader = Reader::new("\\l");
259
        let error = parse(&mut reader).err().unwrap();
260
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
261
        assert_eq!(error.kind, ParseErrorKind::EscapeChar);
262

            
263
        let mut reader = Reader::new(r#"{"id":1}"#);
264
        let error = parse(&mut reader).err().unwrap();
265
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
266
        assert_eq!(
267
            error.kind,
268
            ParseErrorKind::Expecting {
269
                value: "key-string".to_string()
270
            }
271
        );
272
    }
273

            
274
    #[test]
275
    fn test_key_string_content() {
276
        let mut reader = Reader::new("aaa\\:");
277
        assert_eq!(key_string_content(&mut reader).unwrap(), "aaa:");
278
    }
279

            
280
    #[test]
281
    fn test_key_string_text() {
282
        let mut reader = Reader::new("aaa\\:");
283
        assert_eq!(key_string_text(&mut reader), "aaa");
284
        assert_eq!(reader.cursor().index, 3);
285
    }
286

            
287
    #[test]
288
    fn test_key_string_escaped_char() {
289
        let mut reader = Reader::new("\\u{0a}");
290
        assert_eq!(key_string_escaped_char(&mut reader).unwrap(), '\n');
291
        assert_eq!(reader.cursor().index, 6);
292

            
293
        let mut reader = Reader::new("\\:");
294
        assert_eq!(key_string_escaped_char(&mut reader).unwrap(), ':');
295
        assert_eq!(reader.cursor().index, 2);
296

            
297
        let mut reader = Reader::new("x");
298
        let error = key_string_escaped_char(&mut reader).err().unwrap();
299
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
300
        assert_eq!(
301
            error.kind,
302
            ParseErrorKind::Expecting {
303
                value: "\\".to_string()
304
            }
305
        );
306
        assert!(error.recoverable);
307
        assert_eq!(reader.cursor().index, 0);
308
    }
309
}