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 super::placeholder;
19
use crate::ast::{SourceInfo, Template, TemplateElement};
20
use crate::parser::primitives::try_literal;
21
use crate::parser::{string, ParseError, ParseErrorKind, ParseResult};
22
use crate::reader::Reader;
23
use crate::typing::ToSource;
24

            
25
/// Parses a string into a [key string](https://hurl.dev/docs/grammar.html#key-string).
26
33965
pub fn parse(reader: &mut Reader) -> ParseResult<Template> {
27
33965
    let start = reader.cursor();
28
33965
    let mut elements = vec![];
29
    loop {
30
65985
        let save_state = reader.cursor();
31
65985
        match placeholder::parse(reader) {
32
230
            Ok(placeholder) => {
33
230
                let element = TemplateElement::Placeholder(placeholder);
34
230
                elements.push(element);
35
            }
36
65755
            Err(e) => {
37
65755
                if e.recoverable {
38
65755
                    reader.seek(save_state);
39
65755
                    let value = key_string_content(reader)?;
40
65755
                    if value.is_empty() {
41
33965
                        break;
42
                    }
43
31790
                    let source = reader.read_from(start.index).to_source();
44
31790
                    let element = TemplateElement::String { value, source };
45
31790
                    elements.push(element);
46
                } else {
47
                    return Err(e);
48
                }
49
            }
50
        }
51
    }
52
33965
    if elements.is_empty() {
53
1945
        let kind = ParseErrorKind::Expecting {
54
1945
            value: "key-string".to_string(),
55
1945
        };
56
1945
        return Err(ParseError::new(start.pos, false, kind));
57
    }
58
32020
    if let Some(TemplateElement::String { source, .. }) = elements.first() {
59
31790
        if source.starts_with('[') {
60
10955
            let kind = ParseErrorKind::Expecting {
61
10955
                value: "key-string".to_string(),
62
10955
            };
63
10955
            return Err(ParseError::new(start.pos, false, kind));
64
        }
65
    }
66

            
67
21065
    let end = reader.cursor();
68
21065
    let template = Template::new(None, elements, SourceInfo::new(start.pos, end.pos));
69
21065
    Ok(template)
70
}
71

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

            
96
97585
fn key_string_text(reader: &mut Reader) -> String {
97
97585
    let mut s = String::new();
98
    loop {
99
307355
        let save = reader.cursor();
100
307355
        match reader.read() {
101
305
            None => break,
102
307050
            Some(c) => {
103
307050
                if c.is_alphanumeric()
104
122195
                    || c == '_'
105
121250
                    || c == '-'
106
119455
                    || c == '.'
107
119400
                    || c == '['
108
108405
                    || c == ']'
109
97415
                    || c == '@'
110
97360
                    || c == '$'
111
209770
                {
112
209770
                    s.push(c);
113
209770
                } else {
114
97280
                    reader.seek(save);
115
97280
                    break;
116
                }
117
            }
118
        }
119
    }
120

            
121
97585
    s
122
}
123

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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