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::{SourceString, ToSource};
26

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

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

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

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

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

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

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

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

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

            
171
832355
pub fn newline(reader: &mut Reader) -> ParseResult<Whitespace> {
172
832355
    let start = reader.cursor();
173
832355
    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
832355
        Err(_) => match literal("\n", reader) {
179
151880
            Ok(_) => Ok(Whitespace {
180
151880
                value: "\n".to_string(),
181
151880
                source_info: SourceInfo::new(start.pos, reader.cursor().pos),
182
151880
            }),
183
            Err(_) => {
184
680475
                let kind = ParseErrorKind::Expecting {
185
680475
                    value: String::from("newline"),
186
680475
                };
187
680475
                Err(ParseError::new(start.pos, false, kind))
188
            }
189
        },
190
    }
191
}
192

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

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

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

            
256
475
pub fn regex(reader: &mut Reader) -> ParseResult<Regex> {
257
475
    try_literal("/", reader)?;
258
465
    let start = reader.cursor();
259
465
    let mut unescaped = String::new();
260
465
    let mut source = SourceString::new();
261
465
    source.push('/');
262

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

            
325
18035
pub fn null(reader: &mut Reader) -> ParseResult<()> {
326
18035
    try_literal("null", reader)
327
}
328

            
329
48045
pub fn boolean(reader: &mut Reader) -> ParseResult<bool> {
330
48045
    let start = reader.cursor();
331
48045
    match try_literal("true", reader) {
332
1110
        Ok(_) => Ok(true),
333
46935
        Err(_) => match literal("false", reader) {
334
590
            Ok(_) => Ok(false),
335
            Err(_) => {
336
46345
                let kind = ParseErrorKind::Expecting {
337
46345
                    value: String::from("true|false"),
338
46345
                };
339
46345
                Err(ParseError::new(start.pos, true, kind))
340
            }
341
        },
342
    }
343
}
344

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

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

            
381
3855
pub fn eof(reader: &mut Reader) -> ParseResult<()> {
382
3855
    if reader.is_eof() {
383
3855
        Ok(())
384
    } else {
385
        let kind = ParseErrorKind::Expecting {
386
            value: String::from("eof"),
387
        };
388
        Err(ParseError::new(reader.cursor().pos, false, kind))
389
    }
390
}
391

            
392
36045
pub fn hex_digit_value(c: char) -> Option<u32> {
393
36045
    match c.to_ascii_lowercase() {
394
2055
        '0' => Some(0),
395
2260
        '1' => Some(1),
396
2525
        '2' => Some(2),
397
1735
        '3' => Some(3),
398
1745
        '4' => Some(4),
399
2160
        '5' => Some(5),
400
4420
        '6' => Some(6),
401
1960
        '7' => Some(7),
402
2355
        '8' => Some(8),
403
1075
        '9' => Some(9),
404
1155
        'a' => Some(10),
405
1710
        'b' => Some(11),
406
2680
        'c' => Some(12),
407
2635
        'd' => Some(13),
408
1625
        'e' => Some(14),
409
2365
        'f' => Some(15),
410
1585
        _ => None,
411
    }
412
}
413

            
414
36045
pub fn hex_digit(reader: &mut Reader) -> ParseResult<u32> {
415
36045
    let start = reader.cursor();
416
36045
    match reader.read() {
417
36045
        Some(c) => match hex_digit_value(c) {
418
34460
            Some(v) => Ok(v),
419
1585
            None => Err(ParseError::new(start.pos, true, ParseErrorKind::HexDigit)),
420
        },
421
        None => Err(ParseError::new(start.pos, true, ParseErrorKind::HexDigit)),
422
    }
423
}
424

            
425
#[cfg(test)]
426
mod tests {
427
    use super::*;
428
    use crate::ast::{Expr, ExprKind, Placeholder, Template, TemplateElement, Variable};
429
    use crate::reader::Pos;
430
    use crate::typing::ToSource;
431

            
432
    #[test]
433
    fn test_space() {
434
        let mut reader = Reader::new("x");
435
        let error = space(&mut reader).err().unwrap();
436
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
437
        assert_eq!(reader.cursor().index, 1);
438

            
439
        let mut reader = Reader::new("  ");
440
        assert_eq!(
441
            space(&mut reader),
442
            Ok(Whitespace {
443
                value: " ".to_string(),
444
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 2)),
445
            }),
446
        );
447
        assert_eq!(reader.cursor().index, 1);
448
    }
449

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

            
461
        let mut reader = Reader::new("abc");
462
        let error = one_or_more_spaces(&mut reader).err().unwrap();
463
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
464
    }
465

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

            
478
        let mut reader = Reader::new("xxx");
479
        assert_eq!(
480
            zero_or_more_spaces(&mut reader),
481
            Ok(Whitespace {
482
                value: String::new(),
483
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
484
            })
485
        );
486
        assert_eq!(reader.cursor().index, 0);
487

            
488
        let mut reader = Reader::new(" xxx");
489
        assert_eq!(
490
            zero_or_more_spaces(&mut reader),
491
            Ok(Whitespace {
492
                value: " ".to_string(),
493
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 2)),
494
            })
495
        );
496
        assert_eq!(reader.cursor().index, 1);
497
    }
498

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

            
510
        let mut reader = Reader::new("#\n");
511
        assert_eq!(
512
            comment(&mut reader),
513
            Ok(Comment {
514
                value: String::new(),
515
                source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
516
            })
517
        );
518

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

            
529
        let mut reader = Reader::new("xxx");
530
        let error = comment(&mut reader).err().unwrap();
531
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
532
        assert!(error.recoverable);
533
    }
534

            
535
    #[test]
536
    fn test_literal() {
537
        let mut reader = Reader::new("hello");
538
        assert_eq!(literal("hello", &mut reader), Ok(()));
539
        assert_eq!(reader.cursor().index, 5);
540

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

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

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

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

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

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

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

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

            
742
    #[test]
743
    fn test_boolean() {
744
        let mut reader = Reader::new("true");
745
        assert!(boolean(&mut reader).unwrap());
746

            
747
        let mut reader = Reader::new("xxx");
748
        let error = boolean(&mut reader).err().unwrap();
749
        assert_eq!(
750
            error.kind,
751
            ParseErrorKind::Expecting {
752
                value: String::from("true|false")
753
            }
754
        );
755
        assert!(error.recoverable);
756

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

            
768
    #[test]
769
    fn test_hex_digit() {
770
        let mut reader = Reader::new("0");
771
        assert_eq!(hex_digit(&mut reader).unwrap(), 0);
772

            
773
        let mut reader = Reader::new("x");
774
        let error = hex_digit(&mut reader).err().unwrap();
775
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
776
        assert_eq!(error.kind, ParseErrorKind::HexDigit);
777
        assert!(error.recoverable);
778
    }
779

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

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

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

            
825
    #[test]
826
    fn test_regex() {
827
        let mut reader = Reader::new(r#"/a{3}/"#);
828
        assert_eq!(
829
            regex(&mut reader).unwrap(),
830
            Regex {
831
                inner: regex::Regex::new(r#"a{3}"#).unwrap(),
832
                source: r#"/a{3}/"#.to_source(),
833
            }
834
        );
835

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

            
845
        let mut reader = Reader::new(r"/a\.b/");
846
        assert_eq!(
847
            regex(&mut reader).unwrap(),
848
            Regex {
849
                inner: regex::Regex::new(r"a\.b").unwrap(),
850
                source: r"/a\.b/".to_source(),
851
            }
852
        );
853

            
854
        let mut reader = Reader::new(r"/\d{4}-\d{2}-\d{2}/");
855
        assert_eq!(
856
            regex(&mut reader).unwrap(),
857
            Regex {
858
                inner: regex::Regex::new(r"\d{4}-\d{2}-\d{2}").unwrap(),
859
                source: r"/\d{4}-\d{2}-\d{2}/".to_source(),
860
            }
861
        );
862
    }
863

            
864
    #[test]
865
    fn test_regex_error() {
866
        let mut reader = Reader::new("xxx");
867
        let error = regex(&mut reader).err().unwrap();
868
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
869
        assert!(error.recoverable);
870

            
871
        let mut reader = Reader::new("/xxx");
872
        let error = regex(&mut reader).err().unwrap();
873
        assert_eq!(error.pos, Pos { line: 1, column: 5 });
874
        assert!(!error.recoverable);
875
        assert_eq!(
876
            error.kind,
877
            ParseErrorKind::RegexExpr {
878
                message: "unexpected end of file".to_string()
879
            }
880
        );
881

            
882
        let mut reader = Reader::new("/x{a}/");
883
        let error = regex(&mut reader).err().unwrap();
884
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
885
        assert!(!error.recoverable);
886
        assert_eq!(
887
            error.kind,
888
            ParseErrorKind::RegexExpr {
889
                message: "repetition quantifier expects a valid decimal".to_string()
890
            }
891
        );
892
    }
893

            
894
    #[test]
895
    fn test_file() {
896
        let mut reader = Reader::new("file,data.xml;");
897
        assert_eq!(
898
            file(&mut reader).unwrap(),
899
            File {
900
                space0: Whitespace {
901
                    value: String::new(),
902
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
903
                },
904
                filename: Template::new(
905
                    None,
906
                    vec![TemplateElement::String {
907
                        value: "data.xml".to_string(),
908
                        source: "data.xml".to_source(),
909
                    }],
910
                    SourceInfo::new(Pos::new(1, 6), Pos::new(1, 14))
911
                ),
912
                space1: Whitespace {
913
                    value: String::new(),
914
                    source_info: SourceInfo::new(Pos::new(1, 14), Pos::new(1, 14)),
915
                },
916
            }
917
        );
918

            
919
        let mut reader = Reader::new("file, filename1;");
920
        assert_eq!(
921
            file(&mut reader).unwrap(),
922
            File {
923
                space0: Whitespace {
924
                    value: String::from(" "),
925
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
926
                },
927
                filename: Template::new(
928
                    None,
929
                    vec![TemplateElement::String {
930
                        value: "filename1".to_string(),
931
                        source: "filename1".to_source(),
932
                    }],
933
                    SourceInfo::new(Pos::new(1, 7), Pos::new(1, 16)),
934
                ),
935
                space1: Whitespace {
936
                    value: String::new(),
937
                    source_info: SourceInfo::new(Pos::new(1, 16), Pos::new(1, 16)),
938
                },
939
            }
940
        );
941

            
942
        let mut reader = Reader::new("file, tmp/filename1;");
943
        assert_eq!(
944
            file(&mut reader).unwrap(),
945
            File {
946
                space0: Whitespace {
947
                    value: String::from(" "),
948
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
949
                },
950
                filename: Template::new(
951
                    None,
952
                    vec![TemplateElement::String {
953
                        value: "tmp/filename1".to_string(),
954
                        source: "tmp/filename1".to_source(),
955
                    }],
956
                    SourceInfo::new(Pos::new(1, 7), Pos::new(1, 20))
957
                ),
958
                space1: Whitespace {
959
                    value: String::new(),
960
                    source_info: SourceInfo::new(Pos::new(1, 20), Pos::new(1, 20)),
961
                },
962
            }
963
        );
964

            
965
        let mut reader = Reader::new(r"file, tmp/filename\ with\ spaces.txt;");
966
        assert_eq!(
967
            file(&mut reader).unwrap(),
968
            File {
969
                space0: Whitespace {
970
                    value: String::from(" "),
971
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
972
                },
973
                filename: Template::new(
974
                    None,
975
                    vec![TemplateElement::String {
976
                        value: "tmp/filename with spaces.txt".to_string(),
977
                        source: "tmp/filename\\ with\\ spaces.txt".to_source(),
978
                    }],
979
                    SourceInfo::new(Pos::new(1, 7), Pos::new(1, 37))
980
                ),
981
                space1: Whitespace {
982
                    value: String::new(),
983
                    source_info: SourceInfo::new(Pos::new(1, 37), Pos::new(1, 37)),
984
                },
985
            }
986
        );
987
    }
988

            
989
    #[test]
990
    fn test_file_error() {
991
        let mut reader = Reader::new("fil; filename1;");
992
        let error = file(&mut reader).err().unwrap();
993
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
994
        assert!(error.recoverable);
995

            
996
        let mut reader = Reader::new("file, filename1");
997
        let error = file(&mut reader).err().unwrap();
998
        assert_eq!(
999
            error.pos,
            Pos {
                line: 1,
                column: 16,
            }
        );
        assert!(!error.recoverable);
        assert_eq!(
            error.kind,
            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);
    }
}