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::{
19
    Base64, Comment, File, Hex, KeyValue, LineTerminator, Regex, SourceInfo, Whitespace,
20
};
21
use crate::combinator::{one_or_more, optional, recover, zero_or_more};
22
use crate::parser::string::unquoted_template;
23
use crate::parser::{base64, filename, key_string, ParseError, ParseErrorKind, ParseResult};
24
use crate::reader::Reader;
25
use crate::typing::ToSource;
26

            
27
746595
pub fn space(reader: &mut Reader) -> ParseResult<Whitespace> {
28
746595
    let start = reader.cursor();
29
746595
    match reader.read() {
30
5
        None => Err(ParseError::new(start.pos, true, ParseErrorKind::Space)),
31
746590
        Some(c) => {
32
746590
            if c == ' ' || c == '\t' {
33
154110
                Ok(Whitespace {
34
154110
                    value: c.to_string(),
35
154110
                    source_info: SourceInfo::new(start.pos, reader.cursor().pos),
36
154110
                })
37
            } else {
38
592480
                Err(ParseError::new(start.pos, true, ParseErrorKind::Space))
39
            }
40
        }
41
    }
42
}
43

            
44
62800
pub fn one_or_more_spaces(reader: &mut Reader) -> ParseResult<Whitespace> {
45
62800
    let start = reader.cursor();
46
62800
    match one_or_more(space, reader) {
47
62780
        Ok(v) => {
48
75366
            let s = v.iter().map(|x| x.value.clone()).collect();
49
62780
            Ok(Whitespace {
50
62780
                value: s,
51
62780
                source_info: SourceInfo::new(start.pos, reader.cursor().pos),
52
62780
            })
53
        }
54
20
        Err(e) => Err(e),
55
    }
56
}
57

            
58
536220
pub fn zero_or_more_spaces(reader: &mut Reader) -> ParseResult<Whitespace> {
59
536220
    let start = reader.cursor();
60
536220
    match zero_or_more(space, reader) {
61
        //Ok(v) => return Ok(v.join("")),
62
536220
        Ok(v) => {
63
554480
            let s = v.iter().map(|x| x.value.clone()).collect();
64
536220
            Ok(Whitespace {
65
536220
                value: s,
66
536220
                source_info: SourceInfo::new(start.pos, reader.cursor().pos),
67
536220
            })
68
        }
69
        Err(e) => Err(e),
70
    }
71
}
72

            
73
286850
pub fn line_terminator(reader: &mut Reader) -> ParseResult<LineTerminator> {
74
286850
    let space0 = zero_or_more_spaces(reader)?;
75
286850
    let comment = optional(comment, reader)?;
76
286850
    let nl = if reader.is_eof() {
77
35
        Whitespace {
78
35
            value: String::new(),
79
35
            source_info: SourceInfo::new(reader.cursor().pos, reader.cursor().pos),
80
        }
81
    } else {
82
286815
        match newline(reader) {
83
133460
            Ok(r) => r,
84
153355
            Err(e) => {
85
153355
                let kind = ParseErrorKind::Expecting {
86
153355
                    value: String::from("line_terminator"),
87
153355
                };
88
153355
                return Err(ParseError::new(e.pos, false, kind));
89
            }
90
        }
91
    };
92

            
93
133495
    Ok(LineTerminator {
94
133495
        space0,
95
133495
        comment,
96
133495
        newline: nl,
97
133495
    })
98
}
99

            
100
162390
pub fn optional_line_terminators(reader: &mut Reader) -> ParseResult<Vec<LineTerminator>> {
101
248698
    zero_or_more(|p2| recover(line_terminator, p2), reader)
102
}
103

            
104
286850
pub fn comment(reader: &mut Reader) -> ParseResult<Comment> {
105
286850
    try_literal("#", reader)?;
106
12500
    let start = reader.cursor();
107
12500
    let mut value = String::new();
108
    loop {
109
507805
        if reader.is_eof() {
110
            break;
111
        }
112
507805
        let save_state = reader.cursor();
113
507805
        match newline(reader) {
114
            Ok(_) => {
115
12500
                reader.seek(save_state);
116
12500
                break;
117
            }
118
            _ => {
119
495305
                reader.seek(save_state);
120
495305
                if let Some(c) = reader.read() {
121
495305
                    value.push(c);
122
                }
123
            }
124
        }
125
    }
126
12500
    let end = reader.cursor();
127
12500
    Ok(Comment {
128
12500
        value,
129
12500
        source_info: SourceInfo::new(start.pos, end.pos),
130
12500
    })
131
}
132

            
133
/// Does not return a value, non recoverable parser. Use combinator recover to make it recoverable
134
4414635
pub fn literal(s: &str, reader: &mut Reader) -> ParseResult<()> {
135
4414635
    let start = reader.cursor();
136
4670935
    for c in s.chars() {
137
4670935
        match reader.read() {
138
            None => {
139
46675
                let kind = ParseErrorKind::Expecting {
140
46675
                    value: s.to_string(),
141
46675
                };
142
46675
                return Err(ParseError::new(start.pos, false, kind));
143
            }
144
4624260
            Some(x) => {
145
4624260
                if x != c {
146
4036810
                    let kind = ParseErrorKind::Expecting {
147
4036810
                        value: s.to_string(),
148
4036810
                    };
149
4036810
                    return Err(ParseError::new(start.pos, false, kind));
150
                } else {
151
587450
                    continue;
152
                }
153
            }
154
        }
155
    }
156
331150
    Ok(())
157
}
158

            
159
/// Recoverable version which reset the cursor, meant to be combined with following action.
160
3511565
pub fn try_literal(s: &str, reader: &mut Reader) -> ParseResult<()> {
161
3511565
    let save_state = reader.cursor();
162
3511565
    match literal(s, reader) {
163
136185
        Ok(_) => Ok(()),
164
3375380
        Err(e) => {
165
3375380
            reader.seek(save_state);
166
3375380
            Err(ParseError::new(e.pos, true, e.kind))
167
        }
168
    }
169
}
170

            
171
795485
pub fn newline(reader: &mut Reader) -> ParseResult<Whitespace> {
172
795485
    let start = reader.cursor();
173
795485
    match try_literal("\r\n", reader) {
174
        Ok(_) => Ok(Whitespace {
175
            value: "\r\n".to_string(),
176
            source_info: SourceInfo::new(start.pos, reader.cursor().pos),
177
        }),
178
795485
        Err(_) => match literal("\n", reader) {
179
146825
            Ok(_) => Ok(Whitespace {
180
146825
                value: "\n".to_string(),
181
146825
                source_info: SourceInfo::new(start.pos, reader.cursor().pos),
182
146825
            }),
183
            Err(_) => {
184
648660
                let kind = ParseErrorKind::Expecting {
185
648660
                    value: String::from("newline"),
186
648660
                };
187
648660
                Err(ParseError::new(start.pos, false, kind))
188
            }
189
        },
190
    }
191
}
192

            
193
31005
pub fn key_value(reader: &mut Reader) -> ParseResult<KeyValue> {
194
31005
    let line_terminators = optional_line_terminators(reader)?;
195
31005
    let space0 = zero_or_more_spaces(reader)?;
196
31005
    let key = recover(key_string::parse, reader)?;
197
18540
    let space1 = zero_or_more_spaces(reader)?;
198
22248
    recover(|reader1| literal(":", reader1), reader)?;
199
4220
    let space2 = zero_or_more_spaces(reader)?;
200
4220
    let value = unquoted_template(reader)?;
201
4220
    let line_terminator0 = line_terminator(reader)?;
202
4220
    Ok(KeyValue {
203
4220
        line_terminators,
204
4220
        space0,
205
4220
        key,
206
4220
        space1,
207
4220
        space2,
208
4220
        value,
209
4220
        line_terminator0,
210
4220
    })
211
}
212

            
213
36595
pub fn hex(reader: &mut Reader) -> ParseResult<Hex> {
214
36595
    try_literal("hex", reader)?;
215
1240
    literal(",", reader)?;
216
1240
    let space0 = zero_or_more_spaces(reader)?;
217
1240
    let mut value: Vec<u8> = vec![];
218
1240
    let start = reader.cursor();
219
1240
    let mut current: i32 = -1;
220
    loop {
221
33570
        let s = reader.cursor();
222
33570
        match hex_digit(reader) {
223
32330
            Ok(d) => {
224
32330
                if current != -1 {
225
16165
                    value.push((current * 16 + d as i32) as u8);
226
16165
                    current = -1;
227
16165
                } else {
228
16165
                    current = d as i32;
229
                }
230
            }
231
            Err(_) => {
232
1240
                reader.seek(s);
233
1240
                break;
234
            }
235
1240
        };
236
    }
237
1240
    if current != -1 {
238
        return Err(ParseError::new(
239
            reader.cursor().pos,
240
            false,
241
            ParseErrorKind::OddNumberOfHexDigits,
242
        ));
243
    }
244
1240
    let source = reader.read_from(start.index).to_source();
245
1240
    let space1 = zero_or_more_spaces(reader)?;
246
1240
    literal(";", reader)?;
247

            
248
1240
    Ok(Hex {
249
1240
        space0,
250
1240
        value,
251
1240
        source,
252
1240
        space1,
253
1240
    })
254
}
255

            
256
415
pub fn regex(reader: &mut Reader) -> ParseResult<Regex> {
257
415
    try_literal("/", reader)?;
258
405
    let start = reader.cursor();
259
405
    let mut s = String::new();
260

            
261
    // Hurl escaping /
262
    // in order to avoid terminating the regex
263
    // eg. \a\b/
264
    //
265
    // Other escaped sequences such as \* are part of the regex expression
266
    // They are not part of the syntax of Hurl itself.
267
    loop {
268
5365
        match reader.read() {
269
            None => {
270
                let kind = ParseErrorKind::RegexExpr {
271
                    message: "unexpected end of file".to_string(),
272
                };
273
                return Err(ParseError::new(reader.cursor().pos, false, kind));
274
            }
275
405
            Some('/') => break,
276
            Some('\\') => {
277
360
                if let Some('/') = reader.peek() {
278
20
                    reader.read();
279
20
                    s.push('/');
280
340
                } else {
281
340
                    s.push('\\');
282
                }
283
            }
284
4600
            Some(c) => s.push(c),
285
        }
286
    }
287
405
    match regex::Regex::new(s.as_str()) {
288
400
        Ok(inner) => Ok(Regex { inner }),
289
5
        Err(e) => {
290
5
            let message = match e {
291
5
                regex::Error::Syntax(s) => {
292
5
                    // The regex syntax error from the crate returns a multiline String
293
5
                    // For example
294
5
                    //     regex parse error:
295
5
                    //         x{a}
296
5
                    //           ^
297
5
                    //     error: repetition quantifier expects a valid decimal
298
5
                    //
299
5
                    // To fit nicely in Hurl Error reporting, you need an error message string that does not spread on multiple lines
300
5
                    // You will assume that the error most relevant description is on the last line
301
5
                    let lines = s.split('\n').collect::<Vec<&str>>();
302
5
                    let last_line = lines.last().expect("at least one line");
303
5
                    last_line
304
5
                        .strip_prefix("error: ")
305
5
                        .unwrap_or(last_line)
306
5
                        .to_string()
307
                }
308
                regex::Error::CompiledTooBig(_) => "Size limit exceeded".to_string(),
309
                _ => "unknown".to_string(),
310
            };
311
5
            Err(ParseError::new(
312
5
                start.pos,
313
5
                false,
314
5
                ParseErrorKind::RegexExpr { message },
315
5
            ))
316
        }
317
    }
318
}
319

            
320
16945
pub fn null(reader: &mut Reader) -> ParseResult<()> {
321
16945
    try_literal("null", reader)
322
}
323

            
324
46535
pub fn boolean(reader: &mut Reader) -> ParseResult<bool> {
325
46535
    let start = reader.cursor();
326
46535
    match try_literal("true", reader) {
327
1075
        Ok(_) => Ok(true),
328
45460
        Err(_) => match literal("false", reader) {
329
580
            Ok(_) => Ok(false),
330
            Err(_) => {
331
44880
                let kind = ParseErrorKind::Expecting {
332
44880
                    value: String::from("true|false"),
333
44880
                };
334
44880
                Err(ParseError::new(start.pos, true, kind))
335
            }
336
        },
337
    }
338
}
339

            
340
36475
pub(crate) fn file(reader: &mut Reader) -> ParseResult<File> {
341
36475
    let _start = reader.cursor();
342
36475
    try_literal("file", reader)?;
343
470
    literal(",", reader)?;
344
470
    let space0 = zero_or_more_spaces(reader)?;
345
470
    let f = filename::parse(reader)?;
346
465
    let space1 = zero_or_more_spaces(reader)?;
347
465
    literal(";", reader)?;
348
460
    Ok(File {
349
460
        space0,
350
460
        filename: f,
351
460
        space1,
352
460
    })
353
}
354

            
355
35820
pub(crate) fn base64(reader: &mut Reader) -> ParseResult<Base64> {
356
35820
    // base64 => can have whitespace
357
35820
    // support parser position
358
35820
    try_literal("base64", reader)?;
359
320
    literal(",", reader)?;
360
320
    let space0 = zero_or_more_spaces(reader)?;
361
320
    let save_state = reader.cursor();
362
320
    let value = base64::parse(reader);
363
320
    let count = reader.cursor().index - save_state.index;
364
320
    reader.seek(save_state);
365
320
    let source = reader.read_n(count).to_source();
366
320
    let space1 = zero_or_more_spaces(reader)?;
367
320
    literal(";", reader)?;
368
315
    Ok(Base64 {
369
315
        space0,
370
315
        value,
371
315
        source,
372
315
        space1,
373
315
    })
374
}
375

            
376
3795
pub fn eof(reader: &mut Reader) -> ParseResult<()> {
377
3795
    if reader.is_eof() {
378
3795
        Ok(())
379
    } else {
380
        let kind = ParseErrorKind::Expecting {
381
            value: String::from("eof"),
382
        };
383
        Err(ParseError::new(reader.cursor().pos, false, kind))
384
    }
385
}
386

            
387
34620
pub fn hex_digit_value(c: char) -> Option<u32> {
388
34620
    match c.to_ascii_lowercase() {
389
1935
        '0' => Some(0),
390
2220
        '1' => Some(1),
391
2525
        '2' => Some(2),
392
1595
        '3' => Some(3),
393
1690
        '4' => Some(4),
394
2105
        '5' => Some(5),
395
4340
        '6' => Some(6),
396
1940
        '7' => Some(7),
397
2280
        '8' => Some(8),
398
1075
        '9' => Some(9),
399
1115
        'a' => Some(10),
400
1470
        'b' => Some(11),
401
2630
        'c' => Some(12),
402
2495
        'd' => Some(13),
403
1485
        'e' => Some(14),
404
2250
        'f' => Some(15),
405
1470
        _ => None,
406
    }
407
}
408

            
409
34620
pub fn hex_digit(reader: &mut Reader) -> ParseResult<u32> {
410
34620
    let start = reader.cursor();
411
34620
    match reader.read() {
412
34620
        Some(c) => match hex_digit_value(c) {
413
33150
            Some(v) => Ok(v),
414
1470
            None => Err(ParseError::new(start.pos, true, ParseErrorKind::HexDigit)),
415
        },
416
        None => Err(ParseError::new(start.pos, true, ParseErrorKind::HexDigit)),
417
    }
418
}
419

            
420
#[cfg(test)]
421
mod tests {
422
    use super::*;
423
    use crate::ast::{Expr, ExprKind, Placeholder, Template, TemplateElement, Variable};
424
    use crate::reader::Pos;
425
    use crate::typing::ToSource;
426

            
427
    #[test]
428
    fn test_space() {
429
        let mut reader = Reader::new("x");
430
        let error = space(&mut reader).err().unwrap();
431
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
432
        assert_eq!(reader.cursor().index, 1);
433

            
434
        let mut reader = Reader::new("  ");
435
        assert_eq!(
436
            space(&mut reader),
437
            Ok(Whitespace {
438
                value: " ".to_string(),
439
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 2)),
440
            }),
441
        );
442
        assert_eq!(reader.cursor().index, 1);
443
    }
444

            
445
    #[test]
446
    fn test_one_or_more_spaces() {
447
        let mut reader = Reader::new("  ");
448
        assert_eq!(
449
            one_or_more_spaces(&mut reader),
450
            Ok(Whitespace {
451
                value: "  ".to_string(),
452
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 3)),
453
            })
454
        );
455

            
456
        let mut reader = Reader::new("abc");
457
        let error = one_or_more_spaces(&mut reader).err().unwrap();
458
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
459
    }
460

            
461
    #[test]
462
    fn test_zero_or_more_spaces() {
463
        let mut reader = Reader::new("  ");
464
        assert_eq!(
465
            zero_or_more_spaces(&mut reader),
466
            Ok(Whitespace {
467
                value: "  ".to_string(),
468
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 3)),
469
            })
470
        );
471
        assert_eq!(reader.cursor().index, 2);
472

            
473
        let mut reader = Reader::new("xxx");
474
        assert_eq!(
475
            zero_or_more_spaces(&mut reader),
476
            Ok(Whitespace {
477
                value: String::new(),
478
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
479
            })
480
        );
481
        assert_eq!(reader.cursor().index, 0);
482

            
483
        let mut reader = Reader::new(" xxx");
484
        assert_eq!(
485
            zero_or_more_spaces(&mut reader),
486
            Ok(Whitespace {
487
                value: " ".to_string(),
488
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 2)),
489
            })
490
        );
491
        assert_eq!(reader.cursor().index, 1);
492
    }
493

            
494
    #[test]
495
    fn test_comment() {
496
        //    let mut parser = Parser::init("# comment");
497
        //    assert_eq!(
498
        //        comment(&mut reader),
499
        //        Ok(Comment {
500
        //            value: " comment".to_string()
501
        //        })
502
        //    );
503
        //    assert_eq!(reader.state.cursor, 9);
504

            
505
        let mut reader = Reader::new("#\n");
506
        assert_eq!(
507
            comment(&mut reader),
508
            Ok(Comment {
509
                value: String::new(),
510
                source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
511
            })
512
        );
513

            
514
        let mut reader = Reader::new("# comment\n");
515
        assert_eq!(
516
            comment(&mut reader),
517
            Ok(Comment {
518
                value: " comment".to_string(),
519
                source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 10)),
520
            })
521
        );
522
        assert_eq!(reader.cursor().index, 9);
523

            
524
        let mut reader = Reader::new("xxx");
525
        let error = comment(&mut reader).err().unwrap();
526
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
527
        assert!(error.recoverable);
528
    }
529

            
530
    #[test]
531
    fn test_literal() {
532
        let mut reader = Reader::new("hello");
533
        assert_eq!(literal("hello", &mut reader), Ok(()));
534
        assert_eq!(reader.cursor().index, 5);
535

            
536
        let mut reader = Reader::new("");
537
        let error = literal("hello", &mut reader).err().unwrap();
538
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
539
        assert_eq!(
540
            error.kind,
541
            ParseErrorKind::Expecting {
542
                value: String::from("hello")
543
            }
544
        );
545
        assert_eq!(reader.cursor().index, 0);
546

            
547
        let mut reader = Reader::new("hi");
548
        let error = literal("hello", &mut reader).err().unwrap();
549
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
550
        assert_eq!(
551
            error.kind,
552
            ParseErrorKind::Expecting {
553
                value: String::from("hello")
554
            }
555
        );
556
        assert_eq!(reader.cursor().index, 2);
557

            
558
        let mut reader = Reader::new("he");
559
        let error = literal("hello", &mut reader).err().unwrap();
560
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
561
        assert_eq!(
562
            error.kind,
563
            ParseErrorKind::Expecting {
564
                value: String::from("hello")
565
            }
566
        );
567
        assert_eq!(reader.cursor().index, 2);
568
    }
569

            
570
    #[test]
571
    fn test_new_line() {
572
        let mut reader = Reader::new("\n");
573
        assert_eq!(
574
            newline(&mut reader).unwrap(),
575
            Whitespace {
576
                value: String::from("\n"),
577
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(2, 1)),
578
            }
579
        );
580
    }
581

            
582
    #[test]
583
    fn test_key_value() {
584
        let mut reader = Reader::new("message: hello {{name}}! # comment");
585
        assert_eq!(
586
            key_value(&mut reader).unwrap(),
587
            KeyValue {
588
                line_terminators: vec![],
589
                space0: Whitespace {
590
                    value: String::new(),
591
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
592
                },
593
                key: Template::new(
594
                    None,
595
                    vec![TemplateElement::String {
596
                        value: "message".to_string(),
597
                        source: "message".to_source(),
598
                    }],
599
                    SourceInfo::new(Pos::new(1, 1), Pos::new(1, 8)),
600
                ),
601
                space1: Whitespace {
602
                    value: String::new(),
603
                    source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 8)),
604
                },
605
                space2: Whitespace {
606
                    value: " ".to_string(),
607
                    source_info: SourceInfo::new(Pos::new(1, 9), Pos::new(1, 10)),
608
                },
609
                value: Template::new(
610
                    None,
611
                    vec![
612
                        TemplateElement::String {
613
                            value: "hello ".to_string(),
614
                            source: "hello ".to_source(),
615
                        },
616
                        TemplateElement::Placeholder(Placeholder {
617
                            space0: Whitespace {
618
                                value: String::new(),
619
                                source_info: SourceInfo::new(Pos::new(1, 18), Pos::new(1, 18)),
620
                            },
621
                            expr: Expr {
622
                                kind: ExprKind::Variable(Variable {
623
                                    name: "name".to_string(),
624
                                    source_info: SourceInfo::new(Pos::new(1, 18), Pos::new(1, 22)),
625
                                }),
626
                                source_info: SourceInfo::new(Pos::new(1, 18), Pos::new(1, 22)),
627
                            },
628
                            space1: Whitespace {
629
                                value: String::new(),
630
                                source_info: SourceInfo::new(Pos::new(1, 22), Pos::new(1, 22)),
631
                            },
632
                        }),
633
                        TemplateElement::String {
634
                            value: "!".to_string(),
635
                            source: "!".to_source(),
636
                        },
637
                    ],
638
                    SourceInfo::new(Pos::new(1, 10), Pos::new(1, 25))
639
                ),
640
                line_terminator0: LineTerminator {
641
                    space0: Whitespace {
642
                        value: " ".to_string(),
643
                        source_info: SourceInfo::new(Pos::new(1, 25), Pos::new(1, 26)),
644
                    },
645
                    comment: Some(Comment {
646
                        value: " comment".to_string(),
647
                        source_info: SourceInfo::new(Pos::new(1, 27), Pos::new(1, 35)),
648
                    }),
649
                    newline: Whitespace {
650
                        value: String::new(),
651
                        source_info: SourceInfo::new(Pos::new(1, 35), Pos::new(1, 35)),
652
                    },
653
                },
654
            }
655
        );
656
    }
657

            
658
    #[test]
659
    fn test_key_value_template() {
660
        let mut reader = Reader::new("{{key}}: value");
661
        assert_eq!(
662
            key_value(&mut reader).unwrap(),
663
            KeyValue {
664
                line_terminators: vec![],
665
                space0: Whitespace {
666
                    value: String::new(),
667
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
668
                },
669
                key: Template::new(
670
                    None,
671
                    vec![TemplateElement::Placeholder(Placeholder {
672
                        space0: Whitespace {
673
                            value: String::new(),
674
                            source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
675
                        },
676
                        expr: Expr {
677
                            kind: ExprKind::Variable(Variable {
678
                                name: "key".to_string(),
679
                                source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 6)),
680
                            }),
681
                            source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 6))
682
                        },
683
                        space1: Whitespace {
684
                            value: String::new(),
685
                            source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
686
                        },
687
                    })],
688
                    SourceInfo::new(Pos::new(1, 1), Pos::new(1, 8)),
689
                ),
690
                space1: Whitespace {
691
                    value: String::new(),
692
                    source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 8)),
693
                },
694
                space2: Whitespace {
695
                    value: " ".to_string(),
696
                    source_info: SourceInfo::new(Pos::new(1, 9), Pos::new(1, 10)),
697
                },
698
                value: Template::new(
699
                    None,
700
                    vec![TemplateElement::String {
701
                        value: "value".to_string(),
702
                        source: "value".to_source(),
703
                    }],
704
                    SourceInfo::new(Pos::new(1, 10), Pos::new(1, 15)),
705
                ),
706
                line_terminator0: LineTerminator {
707
                    space0: Whitespace {
708
                        value: String::new(),
709
                        source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
710
                    },
711
                    comment: None,
712
                    newline: Whitespace {
713
                        value: String::new(),
714
                        source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
715
                    },
716
                },
717
            }
718
        );
719
        assert_eq!(reader.cursor().index, 14);
720
    }
721

            
722
    #[test]
723
    fn test_key_value_recover() {
724
        let mut reader = Reader::new("{{key");
725
        let error = key_value(&mut reader).err().unwrap();
726
        assert_eq!(error.pos, Pos { line: 1, column: 6 });
727
        assert!(error.recoverable);
728
        assert_eq!(reader.cursor().index, 5); // does not reset cursor
729

            
730
        let mut reader = Reader::new("GET ®http://google.fr");
731
        let error = key_value(&mut reader).err().unwrap();
732
        assert_eq!(error.pos, Pos { line: 1, column: 5 });
733
        assert!(error.recoverable);
734
        assert_eq!(reader.cursor().index, 5); // does not reset cursor
735
    }
736

            
737
    #[test]
738
    fn test_boolean() {
739
        let mut reader = Reader::new("true");
740
        assert!(boolean(&mut reader).unwrap());
741

            
742
        let mut reader = Reader::new("xxx");
743
        let error = boolean(&mut reader).err().unwrap();
744
        assert_eq!(
745
            error.kind,
746
            ParseErrorKind::Expecting {
747
                value: String::from("true|false")
748
            }
749
        );
750
        assert!(error.recoverable);
751

            
752
        let mut reader = Reader::new("trux");
753
        let error = boolean(&mut reader).err().unwrap();
754
        assert_eq!(
755
            error.kind,
756
            ParseErrorKind::Expecting {
757
                value: String::from("true|false")
758
            }
759
        );
760
        assert!(error.recoverable);
761
    }
762

            
763
    #[test]
764
    fn test_hex_digit() {
765
        let mut reader = Reader::new("0");
766
        assert_eq!(hex_digit(&mut reader).unwrap(), 0);
767

            
768
        let mut reader = Reader::new("x");
769
        let error = hex_digit(&mut reader).err().unwrap();
770
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
771
        assert_eq!(error.kind, ParseErrorKind::HexDigit);
772
        assert!(error.recoverable);
773
    }
774

            
775
    #[test]
776
    fn test_hex() {
777
        let mut reader = Reader::new("hex, ff;");
778
        assert_eq!(
779
            hex(&mut reader).unwrap(),
780
            Hex {
781
                space0: Whitespace {
782
                    value: " ".to_string(),
783
                    source_info: SourceInfo::new(Pos::new(1, 5), Pos::new(1, 6)),
784
                },
785
                value: vec![255],
786
                source: "ff".to_source(),
787
                space1: Whitespace {
788
                    value: String::new(),
789
                    source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 8)),
790
                },
791
            }
792
        );
793

            
794
        let mut reader = Reader::new("hex,010203 ;");
795
        assert_eq!(
796
            hex(&mut reader).unwrap(),
797
            Hex {
798
                space0: Whitespace {
799
                    value: String::new(),
800
                    source_info: SourceInfo::new(Pos::new(1, 5), Pos::new(1, 5)),
801
                },
802
                value: vec![1, 2, 3],
803
                source: "010203".to_source(),
804
                space1: Whitespace {
805
                    value: " ".to_string(),
806
                    source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 12)),
807
                },
808
            }
809
        );
810
    }
811

            
812
    #[test]
813
    fn test_hex_error() {
814
        let mut reader = Reader::new("hex,012;");
815
        let error = hex(&mut reader).err().unwrap();
816
        assert_eq!(error.pos, Pos { line: 1, column: 8 });
817
        assert_eq!(error.kind, ParseErrorKind::OddNumberOfHexDigits);
818
    }
819

            
820
    #[test]
821
    fn test_regex() {
822
        let mut reader = Reader::new(r#"/a{3}/"#);
823
        assert_eq!(
824
            regex(&mut reader).unwrap(),
825
            Regex {
826
                inner: regex::Regex::new(r#"a{3}"#).unwrap()
827
            }
828
        );
829

            
830
        let mut reader = Reader::new(r"/a\/b/");
831
        assert_eq!(
832
            regex(&mut reader).unwrap(),
833
            Regex {
834
                inner: regex::Regex::new(r#"a/b"#).unwrap()
835
            }
836
        );
837

            
838
        let mut reader = Reader::new(r"/a\.b/");
839
        assert_eq!(
840
            regex(&mut reader).unwrap(),
841
            Regex {
842
                inner: regex::Regex::new(r"a\.b").unwrap()
843
            }
844
        );
845

            
846
        let mut reader = Reader::new(r"/\d{4}-\d{2}-\d{2}/");
847
        assert_eq!(
848
            regex(&mut reader).unwrap(),
849
            Regex {
850
                inner: regex::Regex::new(r"\d{4}-\d{2}-\d{2}").unwrap()
851
            }
852
        );
853
    }
854

            
855
    #[test]
856
    fn test_regex_error() {
857
        let mut reader = Reader::new("xxx");
858
        let error = regex(&mut reader).err().unwrap();
859
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
860
        assert!(error.recoverable);
861

            
862
        let mut reader = Reader::new("/xxx");
863
        let error = regex(&mut reader).err().unwrap();
864
        assert_eq!(error.pos, Pos { line: 1, column: 5 });
865
        assert!(!error.recoverable);
866
        assert_eq!(
867
            error.kind,
868
            ParseErrorKind::RegexExpr {
869
                message: "unexpected end of file".to_string()
870
            }
871
        );
872

            
873
        let mut reader = Reader::new("/x{a}/");
874
        let error = regex(&mut reader).err().unwrap();
875
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
876
        assert!(!error.recoverable);
877
        assert_eq!(
878
            error.kind,
879
            ParseErrorKind::RegexExpr {
880
                message: "repetition quantifier expects a valid decimal".to_string()
881
            }
882
        );
883
    }
884

            
885
    #[test]
886
    fn test_file() {
887
        let mut reader = Reader::new("file,data.xml;");
888
        assert_eq!(
889
            file(&mut reader).unwrap(),
890
            File {
891
                space0: Whitespace {
892
                    value: String::new(),
893
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
894
                },
895
                filename: Template::new(
896
                    None,
897
                    vec![TemplateElement::String {
898
                        value: "data.xml".to_string(),
899
                        source: "data.xml".to_source(),
900
                    }],
901
                    SourceInfo::new(Pos::new(1, 6), Pos::new(1, 14))
902
                ),
903
                space1: Whitespace {
904
                    value: String::new(),
905
                    source_info: SourceInfo::new(Pos::new(1, 14), Pos::new(1, 14)),
906
                },
907
            }
908
        );
909

            
910
        let mut reader = Reader::new("file, filename1;");
911
        assert_eq!(
912
            file(&mut reader).unwrap(),
913
            File {
914
                space0: Whitespace {
915
                    value: String::from(" "),
916
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
917
                },
918
                filename: Template::new(
919
                    None,
920
                    vec![TemplateElement::String {
921
                        value: "filename1".to_string(),
922
                        source: "filename1".to_source(),
923
                    }],
924
                    SourceInfo::new(Pos::new(1, 7), Pos::new(1, 16)),
925
                ),
926
                space1: Whitespace {
927
                    value: String::new(),
928
                    source_info: SourceInfo::new(Pos::new(1, 16), Pos::new(1, 16)),
929
                },
930
            }
931
        );
932

            
933
        let mut reader = Reader::new("file, tmp/filename1;");
934
        assert_eq!(
935
            file(&mut reader).unwrap(),
936
            File {
937
                space0: Whitespace {
938
                    value: String::from(" "),
939
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
940
                },
941
                filename: Template::new(
942
                    None,
943
                    vec![TemplateElement::String {
944
                        value: "tmp/filename1".to_string(),
945
                        source: "tmp/filename1".to_source(),
946
                    }],
947
                    SourceInfo::new(Pos::new(1, 7), Pos::new(1, 20))
948
                ),
949
                space1: Whitespace {
950
                    value: String::new(),
951
                    source_info: SourceInfo::new(Pos::new(1, 20), Pos::new(1, 20)),
952
                },
953
            }
954
        );
955

            
956
        let mut reader = Reader::new(r"file, tmp/filename\ with\ spaces.txt;");
957
        assert_eq!(
958
            file(&mut reader).unwrap(),
959
            File {
960
                space0: Whitespace {
961
                    value: String::from(" "),
962
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
963
                },
964
                filename: Template::new(
965
                    None,
966
                    vec![TemplateElement::String {
967
                        value: "tmp/filename with spaces.txt".to_string(),
968
                        source: "tmp/filename\\ with\\ spaces.txt".to_source(),
969
                    }],
970
                    SourceInfo::new(Pos::new(1, 7), Pos::new(1, 37))
971
                ),
972
                space1: Whitespace {
973
                    value: String::new(),
974
                    source_info: SourceInfo::new(Pos::new(1, 37), Pos::new(1, 37)),
975
                },
976
            }
977
        );
978
    }
979

            
980
    #[test]
981
    fn test_file_error() {
982
        let mut reader = Reader::new("fil; filename1;");
983
        let error = file(&mut reader).err().unwrap();
984
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
985
        assert!(error.recoverable);
986

            
987
        let mut reader = Reader::new("file, filename1");
988
        let error = file(&mut reader).err().unwrap();
989
        assert_eq!(
990
            error.pos,
991
            Pos {
992
                line: 1,
993
                column: 16,
994
            }
995
        );
996
        assert!(!error.recoverable);
997
        assert_eq!(
998
            error.kind,
999
            ParseErrorKind::Expecting {
                value: String::from(";")
            }
        );
        let mut reader = Reader::new(r"file, tmp/filename\ with\ unescaped .txt;");
        let error = file(&mut reader).err().unwrap();
        assert_eq!(
            error.pos,
            Pos {
                line: 1,
                column: 37,
            }
        );
        assert!(!error.recoverable);
        assert_eq!(
            error.kind,
            ParseErrorKind::Expecting {
                value: String::from(";")
            }
        );
    }
    #[test]
    fn test_base64() {
        let mut reader = Reader::new("base64,  T WE=;xxx");
        assert_eq!(
            base64(&mut reader).unwrap(),
            Base64 {
                space0: Whitespace {
                    value: String::from("  "),
                    source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 10)),
                },
                value: vec![77, 97],
                source: "T WE=".to_source(),
                space1: Whitespace {
                    value: String::new(),
                    source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
                },
            }
        );
        assert_eq!(reader.cursor().index, 15);
    }
}