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

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

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

            
73
290055
pub fn line_terminator(reader: &mut Reader) -> ParseResult<LineTerminator> {
74
290055
    let space0 = zero_or_more_spaces(reader)?;
75
290055
    let comment = optional(comment, reader)?;
76
290055
    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
290020
        match newline(reader) {
83
135285
            Ok(r) => r,
84
154735
            Err(e) => {
85
154735
                let kind = ParseErrorKind::Expecting {
86
154735
                    value: String::from("line_terminator"),
87
154735
                };
88
154735
                return Err(ParseError::new(e.pos, false, kind));
89
            }
90
        }
91
    };
92

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

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

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

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

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

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

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

            
213
37115
pub fn hex(reader: &mut Reader) -> ParseResult<Hex> {
214
37115
    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
420
pub fn regex(reader: &mut Reader) -> ParseResult<Regex> {
257
420
    try_literal("/", reader)?;
258
410
    let start = reader.cursor();
259
410
    let mut unescaped = String::new();
260
410
    let mut source = SourceString::new();
261
410
    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
5485
        let c = reader.read();
271
5485
        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
5485
        source.push(c);
278
5485
        match c {
279
410
            '/' => break,
280
            '\\' => {
281
375
                if let Some('/') = reader.peek() {
282
20
                    reader.read();
283
20
                    unescaped.push('/');
284
355
                } else {
285
355
                    unescaped.push('\\');
286
                }
287
            }
288
4700
            c => unescaped.push(c),
289
        }
290
    }
291
410
    match regex::Regex::new(unescaped.as_str()) {
292
405
        Ok(inner) => Ok(Regex { inner, source }),
293
5
        Err(e) => {
294
5
            let message = match e {
295
5
                regex::Error::Syntax(s) => {
296
5
                    // The regex syntax error from the crate returns a multiline String
297
5
                    // For example
298
5
                    //     regex parse error:
299
5
                    //         x{a}
300
5
                    //           ^
301
5
                    //     error: repetition quantifier expects a valid decimal
302
5
                    //
303
5
                    // To fit nicely in Hurl Error reporting, you need an error message string that does not spread on multiple lines
304
5
                    // You will assume that the error most relevant description is on the last line
305
5
                    let lines = s.split('\n').collect::<Vec<&str>>();
306
5
                    let last_line = lines.last().expect("at least one line");
307
5
                    last_line
308
5
                        .strip_prefix("error: ")
309
5
                        .unwrap_or(last_line)
310
5
                        .to_string()
311
                }
312
                regex::Error::CompiledTooBig(_) => "Size limit exceeded".to_string(),
313
                _ => "unknown".to_string(),
314
            };
315
5
            Err(ParseError::new(
316
5
                start.pos,
317
5
                false,
318
5
                ParseErrorKind::RegexExpr { message },
319
5
            ))
320
        }
321
    }
322
}
323

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
995
        let mut reader = Reader::new("file, filename1");
996
        let error = file(&mut reader).err().unwrap();
997
        assert_eq!(
998
            error.pos,
999
            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);
    }
}