1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2024 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
    Assert, Capture, Cookie, FileParam, FileValue, MultipartParam, Section, SectionValue,
20
    SourceInfo, Whitespace,
21
};
22
use crate::combinator::{optional, recover, zero_or_more};
23
use crate::parser::filter::filters;
24
use crate::parser::predicate::predicate;
25
use crate::parser::primitives::{
26
    key_value, line_terminator, literal, one_or_more_spaces, optional_line_terminators,
27
    try_literal, zero_or_more_spaces,
28
};
29
use crate::parser::query::query;
30
use crate::parser::string::unquoted_template;
31
use crate::parser::{filename, key_string, option, ParseError, ParseErrorKind, ParseResult};
32
use crate::reader::{Pos, Reader};
33

            
34
14145
pub fn request_sections(reader: &mut Reader) -> ParseResult<Vec<Section>> {
35
14145
    let sections = zero_or_more(request_section, reader)?;
36
14095
    Ok(sections)
37
}
38

            
39
12995
pub fn response_sections(reader: &mut Reader) -> ParseResult<Vec<Section>> {
40
12995
    let sections = zero_or_more(response_section, reader)?;
41
12940
    Ok(sections)
42
}
43

            
44
16320
fn request_section(reader: &mut Reader) -> ParseResult<Section> {
45
16320
    let line_terminators = optional_line_terminators(reader)?;
46
16320
    let space0 = zero_or_more_spaces(reader)?;
47
16320
    let start = reader.cursor();
48
16320
    let name = section_name(reader)?;
49
2405
    let source_info = SourceInfo::new(start.pos, reader.cursor().pos);
50

            
51
2405
    let line_terminator0 = line_terminator(reader)?;
52
2405
    let value = match name.as_str() {
53
2405
        "Query" => section_value_query_params(reader, true)?,
54
2290
        "QueryStringParams" => section_value_query_params(reader, false)?,
55
2065
        "BasicAuth" => section_value_basic_auth(reader)?,
56
1990
        "Form" => section_value_form_params(reader, true)?,
57
1955
        "FormParams" => section_value_form_params(reader, false)?,
58
1880
        "Multipart" => section_value_multipart_form_data(reader, true)?,
59
1845
        "MultipartFormData" => section_value_multipart_form_data(reader, false)?,
60
1735
        "Cookies" => section_value_cookies(reader)?,
61
1650
        "Options" => section_value_options(reader)?,
62
        _ => {
63
10
            let kind = ParseErrorKind::RequestSectionName { name: name.clone() };
64
10
            let pos = Pos::new(start.pos.line, start.pos.column + 1);
65
10
            return Err(ParseError::new(pos, false, kind));
66
        }
67
    };
68

            
69
2355
    Ok(Section {
70
2355
        line_terminators,
71
2355
        space0,
72
2355
        line_terminator0,
73
2355
        value,
74
2355
        source_info,
75
2355
    })
76
}
77

            
78
19390
fn response_section(reader: &mut Reader) -> ParseResult<Section> {
79
19390
    let line_terminators = optional_line_terminators(reader)?;
80
19390
    let space0 = zero_or_more_spaces(reader)?;
81
19390
    let start = reader.cursor();
82
19390
    let name = section_name(reader)?;
83
8380
    let end = reader.cursor();
84
8380
    let source_info = SourceInfo::new(start.pos, end.pos);
85

            
86
8380
    let line_terminator0 = line_terminator(reader)?;
87
8380
    let value = match name.as_str() {
88
8380
        "Captures" => section_value_captures(reader)?,
89
7960
        "Asserts" => section_value_asserts(reader)?,
90
        _ => {
91
            let kind = ParseErrorKind::ResponseSectionName { name: name.clone() };
92
            let pos = Pos::new(start.pos.line, start.pos.column + 1);
93
            return Err(ParseError::new(pos, false, kind));
94
        }
95
    };
96

            
97
8325
    Ok(Section {
98
8325
        line_terminators,
99
8325
        space0,
100
8325
        line_terminator0,
101
8325
        value,
102
8325
        source_info,
103
8325
    })
104
}
105

            
106
35710
fn section_name(reader: &mut Reader) -> ParseResult<String> {
107
35710
    let pos = reader.cursor().pos;
108
35710
    try_literal("[", reader)?;
109
92376
    let name = reader.read_while(|c| c.is_alphanumeric());
110
10830
    if name.is_empty() {
111
        // Could be the empty json array for the body
112
40
        let kind = ParseErrorKind::Expecting {
113
40
            value: "a valid section name".to_string(),
114
40
        };
115
40
        return Err(ParseError::new(pos, true, kind));
116
    }
117
10790
    try_literal("]", reader)?;
118
10785
    Ok(name)
119
}
120

            
121
340
fn section_value_query_params(reader: &mut Reader, short: bool) -> ParseResult<SectionValue> {
122
340
    let items = zero_or_more(key_value, reader)?;
123
340
    Ok(SectionValue::QueryParams(items, short))
124
}
125

            
126
75
fn section_value_basic_auth(reader: &mut Reader) -> ParseResult<SectionValue> {
127
75
    let v = optional(key_value, reader)?;
128
75
    Ok(SectionValue::BasicAuth(v))
129
}
130

            
131
110
fn section_value_form_params(reader: &mut Reader, short: bool) -> ParseResult<SectionValue> {
132
110
    let items = zero_or_more(key_value, reader)?;
133
110
    Ok(SectionValue::FormParams(items, short))
134
}
135

            
136
145
fn section_value_multipart_form_data(
137
145
    reader: &mut Reader,
138
145
    short: bool,
139
145
) -> ParseResult<SectionValue> {
140
145
    let items = zero_or_more(multipart_param, reader)?;
141
140
    Ok(SectionValue::MultipartFormData(items, short))
142
}
143

            
144
85
fn section_value_cookies(reader: &mut Reader) -> ParseResult<SectionValue> {
145
85
    let items = zero_or_more(cookie, reader)?;
146
85
    Ok(SectionValue::Cookies(items))
147
}
148

            
149
420
fn section_value_captures(reader: &mut Reader) -> ParseResult<SectionValue> {
150
420
    let items = zero_or_more(capture, reader)?;
151
420
    Ok(SectionValue::Captures(items))
152
}
153

            
154
7960
fn section_value_asserts(reader: &mut Reader) -> ParseResult<SectionValue> {
155
7960
    let asserts = zero_or_more(assert, reader)?;
156
7905
    Ok(SectionValue::Asserts(asserts))
157
}
158

            
159
1640
fn section_value_options(reader: &mut Reader) -> ParseResult<SectionValue> {
160
1640
    let options = zero_or_more(option::parse, reader)?;
161
1605
    Ok(SectionValue::Options(options))
162
}
163

            
164
170
fn cookie(reader: &mut Reader) -> ParseResult<Cookie> {
165
    // let start = reader.state.clone();
166
170
    let line_terminators = optional_line_terminators(reader)?;
167
170
    let space0 = zero_or_more_spaces(reader)?;
168
170
    let name = recover(key_string::parse, reader)?;
169
110
    let space1 = zero_or_more_spaces(reader)?;
170
132
    recover(|p1| literal(":", p1), reader)?;
171
85
    let space2 = zero_or_more_spaces(reader)?;
172
85
    let value = unquoted_template(reader)?;
173
85
    let line_terminator0 = line_terminator(reader)?;
174
85
    Ok(Cookie {
175
85
        line_terminators,
176
85
        space0,
177
85
        name,
178
85
        space1,
179
85
        space2,
180
85
        value,
181
85
        line_terminator0,
182
85
    })
183
}
184

            
185
465
fn multipart_param(reader: &mut Reader) -> ParseResult<MultipartParam> {
186
465
    let save = reader.cursor();
187
465
    match file_param(reader) {
188
200
        Ok(f) => Ok(MultipartParam::FileParam(f)),
189
265
        Err(e) => {
190
265
            if e.recoverable {
191
260
                reader.seek(save);
192
260
                let param = key_value(reader)?;
193
120
                Ok(MultipartParam::Param(param))
194
            } else {
195
5
                Err(e)
196
            }
197
        }
198
    }
199
}
200

            
201
465
fn file_param(reader: &mut Reader) -> ParseResult<FileParam> {
202
465
    let line_terminators = optional_line_terminators(reader)?;
203
465
    let space0 = zero_or_more_spaces(reader)?;
204
465
    let key = recover(key_string::parse, reader)?;
205
405
    let space1 = zero_or_more_spaces(reader)?;
206
486
    recover(|reader1| literal(":", reader1), reader)?;
207
325
    let space2 = zero_or_more_spaces(reader)?;
208
325
    let value = file_value(reader)?;
209
200
    let line_terminator0 = line_terminator(reader)?;
210
200
    Ok(FileParam {
211
200
        line_terminators,
212
200
        space0,
213
200
        key,
214
200
        space1,
215
200
        space2,
216
200
        value,
217
200
        line_terminator0,
218
200
    })
219
}
220

            
221
325
fn file_value(reader: &mut Reader) -> ParseResult<FileValue> {
222
325
    try_literal("file,", reader)?;
223
205
    let space0 = zero_or_more_spaces(reader)?;
224
205
    let f = filename::parse(reader)?;
225
205
    let space1 = zero_or_more_spaces(reader)?;
226
205
    literal(";", reader)?;
227
205
    let save = reader.cursor();
228
205
    let (space2, content_type) = match line_terminator(reader) {
229
        Ok(_) => {
230
135
            reader.seek(save);
231
135
            let space2 = Whitespace {
232
135
                value: String::new(),
233
135
                source_info: SourceInfo {
234
135
                    start: save.pos,
235
135
                    end: save.pos,
236
135
                },
237
135
            };
238
135
            (space2, None)
239
        }
240
        Err(_) => {
241
70
            reader.seek(save);
242
70
            let space2 = zero_or_more_spaces(reader)?;
243
70
            let content_type = file_content_type(reader)?;
244
65
            (space2, Some(content_type))
245
        }
246
    };
247

            
248
200
    Ok(FileValue {
249
200
        space0,
250
200
        filename: f,
251
200
        space1,
252
200
        space2,
253
200
        content_type,
254
200
    })
255
}
256

            
257
70
fn file_content_type(reader: &mut Reader) -> ParseResult<String> {
258
70
    let start = reader.cursor();
259
70
    let mut buf = String::new();
260
70
    let mut spaces = String::new();
261
70
    let mut save = reader.cursor();
262
755
    while let Some(c) = reader.read() {
263
755
        if c.is_alphanumeric() || c == '/' || c == ';' || c == '=' || c == '-' {
264
685
            buf.push_str(spaces.as_str());
265
685
            spaces = String::new();
266
685
            buf.push(c);
267
685
            save = reader.cursor();
268
685
        } else if c == ' ' {
269
            spaces.push(' ');
270
        } else {
271
70
            break;
272
        }
273
    }
274

            
275
70
    reader.seek(save);
276
70
    if buf.is_empty() {
277
5
        return Err(ParseError::new(
278
5
            start.pos,
279
5
            false,
280
5
            ParseErrorKind::FileContentType,
281
5
        ));
282
    }
283
65
    Ok(buf)
284
}
285

            
286
2260
fn capture(reader: &mut Reader) -> ParseResult<Capture> {
287
2260
    let line_terminators = optional_line_terminators(reader)?;
288
2260
    let space0 = zero_or_more_spaces(reader)?;
289
2260
    let name = recover(key_string::parse, reader)?;
290
1955
    let space1 = zero_or_more_spaces(reader)?;
291
2346
    recover(|p1| literal(":", p1), reader)?;
292
1840
    let space2 = zero_or_more_spaces(reader)?;
293
1840
    let q = query(reader)?;
294
1840
    let filters = filters(reader)?;
295
1840
    let line_terminator0 = line_terminator(reader)?;
296
1840
    Ok(Capture {
297
1840
        line_terminators,
298
1840
        space0,
299
1840
        name,
300
1840
        space1,
301
1840
        space2,
302
1840
        query: q,
303
1840
        filters,
304
1840
        line_terminator0,
305
1840
    })
306
}
307

            
308
25325
fn assert(reader: &mut Reader) -> ParseResult<Assert> {
309
25325
    let line_terminators = optional_line_terminators(reader)?;
310
25325
    let space0 = zero_or_more_spaces(reader)?;
311
25325
    let query0 = query(reader)?;
312
18015
    let filters = filters(reader)?;
313
18015
    let space1 = one_or_more_spaces(reader)?;
314
18015
    let predicate0 = predicate(reader)?;
315

            
316
17985
    let line_terminator0 = line_terminator(reader)?;
317
17985
    Ok(Assert {
318
17985
        line_terminators,
319
17985
        space0,
320
17985
        query: query0,
321
17985
        filters,
322
17985
        space1,
323
17985
        predicate: predicate0,
324
17985
        line_terminator0,
325
17985
    })
326
}
327

            
328
#[cfg(test)]
329
mod tests {
330
    use super::*;
331
    use crate::ast::{
332
        KeyValue, LineTerminator, Number, Predicate, PredicateFunc, PredicateFuncValue,
333
        PredicateValue, Query, QueryValue, Template, TemplateElement, I64,
334
    };
335

            
336
    #[test]
337
    fn test_section_name() {
338
        let mut reader = Reader::new("[SectionA]");
339
        assert_eq!(section_name(&mut reader).unwrap(), String::from("SectionA"));
340

            
341
        let mut reader = Reader::new("[]");
342
        assert!(section_name(&mut reader).err().unwrap().recoverable);
343
    }
344

            
345
    #[test]
346
    fn test_asserts_section() {
347
        let mut reader = Reader::new("[Asserts]\nheader \"Location\" == \"https://google.fr\"\n");
348

            
349
        assert_eq!(
350
            response_section(&mut reader).unwrap(),
351
            Section {
352
                line_terminators: vec![],
353
                space0: Whitespace {
354
                    value: String::new(),
355
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
356
                },
357
                line_terminator0: LineTerminator {
358
                    space0: Whitespace {
359
                        value: String::new(),
360
                        source_info: SourceInfo::new(Pos::new(1, 10), Pos::new(1, 10)),
361
                    },
362
                    comment: None,
363
                    newline: Whitespace {
364
                        value: String::from("\n"),
365
                        source_info: SourceInfo::new(Pos::new(1, 10), Pos::new(2, 1)),
366
                    },
367
                },
368
                value: SectionValue::Asserts(vec![Assert {
369
                    line_terminators: vec![],
370
                    space0: Whitespace {
371
                        value: String::new(),
372
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 1)),
373
                    },
374
                    query: Query {
375
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 18)),
376
                        value: QueryValue::Header {
377
                            space0: Whitespace {
378
                                value: String::from(" "),
379
                                source_info: SourceInfo::new(Pos::new(2, 7), Pos::new(2, 8)),
380
                            },
381
                            name: Template {
382
                                delimiter: Some('"'),
383
                                elements: vec![TemplateElement::String {
384
                                    value: "Location".to_string(),
385
                                    encoded: "Location".to_string(),
386
                                }],
387
                                source_info: SourceInfo::new(Pos::new(2, 8), Pos::new(2, 18)),
388
                            },
389
                        },
390
                    },
391
                    filters: vec![],
392
                    space1: Whitespace {
393
                        value: String::from(" "),
394
                        source_info: SourceInfo::new(Pos::new(2, 18), Pos::new(2, 19)),
395
                    },
396
                    predicate: Predicate {
397
                        not: false,
398
                        space0: Whitespace {
399
                            value: String::new(),
400
                            source_info: SourceInfo::new(Pos::new(2, 19), Pos::new(2, 19)),
401
                        },
402
                        predicate_func: PredicateFunc {
403
                            source_info: SourceInfo::new(Pos::new(2, 19), Pos::new(2, 41)),
404
                            value: PredicateFuncValue::Equal {
405
                                space0: Whitespace {
406
                                    value: String::from(" "),
407
                                    source_info: SourceInfo::new(Pos::new(2, 21), Pos::new(2, 22)),
408
                                },
409
                                value: PredicateValue::String(Template {
410
                                    delimiter: Some('"'),
411
                                    elements: vec![TemplateElement::String {
412
                                        value: "https://google.fr".to_string(),
413
                                        encoded: "https://google.fr".to_string(),
414
                                    }],
415
                                    source_info: SourceInfo::new(Pos::new(2, 22), Pos::new(2, 41)),
416
                                }),
417
                            },
418
                        },
419
                    },
420
                    line_terminator0: LineTerminator {
421
                        space0: Whitespace {
422
                            value: String::new(),
423
                            source_info: SourceInfo::new(Pos::new(2, 41), Pos::new(2, 41)),
424
                        },
425
                        comment: None,
426
                        newline: Whitespace {
427
                            value: String::from("\n"),
428
                            source_info: SourceInfo::new(Pos::new(2, 41), Pos::new(3, 1)),
429
                        },
430
                    },
431
                }]),
432
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 10)),
433
            }
434
        );
435
    }
436

            
437
    #[test]
438
    fn test_asserts_section_error() {
439
        let mut reader = Reader::new("x[Assertsx]\nheader Location == \"https://google.fr\"\n");
440
        let error = response_section(&mut reader).err().unwrap();
441
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
442
        assert_eq!(
443
            error.kind,
444
            ParseErrorKind::Expecting {
445
                value: String::from("[")
446
            }
447
        );
448
        assert!(error.recoverable);
449

            
450
        let mut reader = Reader::new("[Assertsx]\nheader Location == \"https://google.fr\"\n");
451
        let error = response_section(&mut reader).err().unwrap();
452
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
453
        assert_eq!(
454
            error.kind,
455
            ParseErrorKind::ResponseSectionName {
456
                name: String::from("Assertsx")
457
            }
458
        );
459
        assert!(!error.recoverable);
460
    }
461

            
462
    #[test]
463
    fn test_cookie() {
464
        let mut reader = Reader::new("Foo: Bar");
465
        let c = cookie(&mut reader).unwrap();
466
        assert_eq!(c.name.to_string(), String::from("Foo"));
467
        assert_eq!(
468
            c.value,
469
            Template {
470
                delimiter: None,
471
                elements: vec![TemplateElement::String {
472
                    value: "Bar".to_string(),
473
                    encoded: "Bar".to_string(),
474
                }],
475
                source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 9)),
476
            }
477
        );
478
    }
479

            
480
    #[test]
481
    fn test_cookie_error() {
482
        let mut reader = Reader::new("Foo: {{Bar");
483
        let error = cookie(&mut reader).err().unwrap();
484
        assert_eq!(
485
            error.pos,
486
            Pos {
487
                line: 1,
488
                column: 11,
489
            }
490
        );
491
        assert!(!error.recoverable);
492
        assert_eq!(
493
            error.kind,
494
            ParseErrorKind::Expecting {
495
                value: "}}".to_string()
496
            }
497
        );
498
    }
499

            
500
    #[test]
501
    fn test_file_value() {
502
        let mut reader = Reader::new("file,hello.txt;");
503
        assert_eq!(
504
            file_value(&mut reader).unwrap(),
505
            FileValue {
506
                space0: Whitespace {
507
                    value: String::new(),
508
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
509
                },
510
                filename: Template {
511
                    delimiter: None,
512
                    elements: vec![TemplateElement::String {
513
                        value: "hello.txt".to_string(),
514
                        encoded: "hello.txt".to_string(),
515
                    }],
516
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 15)),
517
                },
518
                space1: Whitespace {
519
                    value: String::new(),
520
                    source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
521
                },
522
                space2: Whitespace {
523
                    value: String::new(),
524
                    source_info: SourceInfo::new(Pos::new(1, 16), Pos::new(1, 16)),
525
                },
526
                content_type: None,
527
            }
528
        );
529
        let mut reader = Reader::new("file,hello.txt; text/html");
530
        assert_eq!(
531
            file_value(&mut reader).unwrap(),
532
            FileValue {
533
                space0: Whitespace {
534
                    value: String::new(),
535
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
536
                },
537
                filename: Template {
538
                    elements: vec![TemplateElement::String {
539
                        value: "hello.txt".to_string(),
540
                        encoded: "hello.txt".to_string(),
541
                    }],
542
                    delimiter: None,
543
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 15)),
544
                },
545
                space1: Whitespace {
546
                    value: String::new(),
547
                    source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
548
                },
549
                space2: Whitespace {
550
                    value: " ".to_string(),
551
                    source_info: SourceInfo::new(Pos::new(1, 16), Pos::new(1, 17)),
552
                },
553
                content_type: Some("text/html".to_string()),
554
            }
555
        );
556
    }
557

            
558
    #[test]
559
    fn test_file_content_type() {
560
        let mut reader = Reader::new("text/html");
561
        assert_eq!(
562
            file_content_type(&mut reader).unwrap(),
563
            "text/html".to_string()
564
        );
565
        assert_eq!(reader.cursor().index, 9);
566

            
567
        let mut reader = Reader::new("text/plain; charset=us-ascii");
568
        assert_eq!(
569
            file_content_type(&mut reader).unwrap(),
570
            "text/plain; charset=us-ascii".to_string()
571
        );
572
        assert_eq!(reader.cursor().index, 28);
573

            
574
        let mut reader = Reader::new("text/html # comment");
575
        assert_eq!(
576
            file_content_type(&mut reader).unwrap(),
577
            "text/html".to_string()
578
        );
579
        assert_eq!(reader.cursor().index, 9);
580
    }
581

            
582
    #[test]
583
    fn test_capture() {
584
        let mut reader = Reader::new("url: header \"Location\"");
585
        let capture0 = capture(&mut reader).unwrap();
586

            
587
        assert_eq!(
588
            capture0.name,
589
            Template {
590
                delimiter: None,
591
                elements: vec![TemplateElement::String {
592
                    value: "url".to_string(),
593
                    encoded: "url".to_string(),
594
                }],
595
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4)),
596
            },
597
        );
598
        assert_eq!(
599
            capture0.query,
600
            Query {
601
                source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 23)),
602
                value: QueryValue::Header {
603
                    space0: Whitespace {
604
                        value: String::from(" "),
605
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(1, 13)),
606
                    },
607
                    name: Template {
608
                        delimiter: Some('"'),
609
                        elements: vec![TemplateElement::String {
610
                            value: "Location".to_string(),
611
                            encoded: "Location".to_string(),
612
                        }],
613
                        source_info: SourceInfo::new(Pos::new(1, 13), Pos::new(1, 23)),
614
                    },
615
                },
616
            }
617
        );
618
    }
619

            
620
    #[test]
621
    fn test_capture_with_filter() {
622
        let mut reader = Reader::new("token: header \"Location\" regex \"token=(.*)\"");
623
        let capture0 = capture(&mut reader).unwrap();
624

            
625
        assert_eq!(
626
            capture0.query,
627
            Query {
628
                source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 25)),
629
                value: QueryValue::Header {
630
                    space0: Whitespace {
631
                        value: String::from(" "),
632
                        source_info: SourceInfo::new(Pos::new(1, 14), Pos::new(1, 15)),
633
                    },
634
                    name: Template {
635
                        delimiter: Some('"'),
636
                        elements: vec![TemplateElement::String {
637
                            value: "Location".to_string(),
638
                            encoded: "Location".to_string(),
639
                        }],
640
                        source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 25)),
641
                    },
642
                },
643
            }
644
        );
645
        assert_eq!(reader.cursor().index, 43);
646
    }
647

            
648
    #[test]
649
    fn test_capture_with_filter_error() {
650
        let mut reader = Reader::new("token: header \"Location\" regex ");
651
        let error = capture(&mut reader).err().unwrap();
652
        assert_eq!(
653
            error.pos,
654
            Pos {
655
                line: 1,
656
                column: 32,
657
            }
658
        );
659
        assert_eq!(
660
            error.kind,
661
            ParseErrorKind::Expecting {
662
                value: "\" or /".to_string()
663
            }
664
        );
665
        assert!(!error.recoverable);
666

            
667
        let mut reader = Reader::new("token: header \"Location\" xxx");
668
        let error = capture(&mut reader).err().unwrap();
669
        assert_eq!(
670
            error.pos,
671
            Pos {
672
                line: 1,
673
                column: 26,
674
            }
675
        );
676
        assert_eq!(
677
            error.kind,
678
            ParseErrorKind::Expecting {
679
                value: "line_terminator".to_string()
680
            }
681
        );
682
        assert!(!error.recoverable);
683
    }
684

            
685
    #[test]
686
    fn test_assert() {
687
        let mut reader = Reader::new("header \"Location\" == \"https://google.fr\"");
688
        let assert0 = assert(&mut reader).unwrap();
689

            
690
        assert_eq!(
691
            assert0.query,
692
            Query {
693
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 18)),
694
                value: QueryValue::Header {
695
                    space0: Whitespace {
696
                        value: String::from(" "),
697
                        source_info: SourceInfo::new(Pos::new(1, 7), Pos::new(1, 8)),
698
                    },
699
                    name: Template {
700
                        delimiter: Some('"'),
701
                        elements: vec![TemplateElement::String {
702
                            value: "Location".to_string(),
703
                            encoded: "Location".to_string(),
704
                        }],
705
                        source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 18)),
706
                    },
707
                },
708
            }
709
        );
710
    }
711

            
712
    #[test]
713
    fn test_assert_jsonpath() {
714
        let mut reader = Reader::new("jsonpath \"$.errors\" == 5");
715

            
716
        assert_eq!(
717
            assert(&mut reader).unwrap().predicate,
718
            Predicate {
719
                not: false,
720
                space0: Whitespace {
721
                    value: String::new(),
722
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 21)),
723
                },
724
                predicate_func: PredicateFunc {
725
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 25)),
726
                    value: PredicateFuncValue::Equal {
727
                        space0: Whitespace {
728
                            value: String::from(" "),
729
                            source_info: SourceInfo::new(Pos::new(1, 23), Pos::new(1, 24)),
730
                        },
731
                        value: PredicateValue::Number(Number::Integer(I64::new(
732
                            5,
733
                            "5".to_string()
734
                        ))),
735
                    },
736
                },
737
            }
738
        );
739
    }
740

            
741
    #[test]
742
    fn test_basicauth_section() {
743
        let mut reader = Reader::new("[BasicAuth]\nuser:password\n\nHTTP 200\n");
744

            
745
        assert_eq!(
746
            request_section(&mut reader).unwrap(),
747
            Section {
748
                line_terminators: vec![],
749
                space0: Whitespace {
750
                    value: String::new(),
751
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
752
                },
753
                line_terminator0: LineTerminator {
754
                    space0: Whitespace {
755
                        value: String::new(),
756
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(1, 12)),
757
                    },
758
                    comment: None,
759
                    newline: Whitespace {
760
                        value: String::from("\n"),
761
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(2, 1)),
762
                    },
763
                },
764
                value: SectionValue::BasicAuth(Some(KeyValue {
765
                    line_terminators: vec![],
766
                    space0: Whitespace {
767
                        value: String::new(),
768
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 1))
769
                    },
770
                    key: Template {
771
                        delimiter: None,
772
                        elements: vec![TemplateElement::String {
773
                            value: "user".to_string(),
774
                            encoded: "user".to_string()
775
                        }],
776
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 5)),
777
                    },
778
                    space1: Whitespace {
779
                        value: String::new(),
780
                        source_info: SourceInfo::new(Pos::new(2, 5), Pos::new(2, 5))
781
                    },
782
                    space2: Whitespace {
783
                        value: String::new(),
784
                        source_info: SourceInfo::new(Pos::new(2, 6), Pos::new(2, 6))
785
                    },
786
                    value: Template {
787
                        delimiter: None,
788
                        elements: vec![TemplateElement::String {
789
                            value: "password".to_string(),
790
                            encoded: "password".to_string()
791
                        }],
792
                        source_info: SourceInfo::new(Pos::new(2, 6), Pos::new(2, 14)),
793
                    },
794
                    line_terminator0: LineTerminator {
795
                        space0: Whitespace {
796
                            value: String::new(),
797
                            source_info: SourceInfo::new(Pos::new(2, 14), Pos::new(2, 14))
798
                        },
799
                        comment: None,
800
                        newline: Whitespace {
801
                            value: "\n".to_string(),
802
                            source_info: SourceInfo::new(Pos::new(2, 14), Pos::new(3, 1))
803
                        },
804
                    },
805
                })),
806
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 12)),
807
            }
808
        );
809
        assert_eq!(reader.cursor().pos, Pos { line: 3, column: 1 });
810

            
811
        let mut reader = Reader::new("[BasicAuth]\nHTTP 200\n");
812
        assert_eq!(
813
            request_section(&mut reader).unwrap(),
814
            Section {
815
                line_terminators: vec![],
816
                space0: Whitespace {
817
                    value: String::new(),
818
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
819
                },
820
                line_terminator0: LineTerminator {
821
                    space0: Whitespace {
822
                        value: String::new(),
823
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(1, 12)),
824
                    },
825
                    comment: None,
826
                    newline: Whitespace {
827
                        value: String::from("\n"),
828
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(2, 1)),
829
                    },
830
                },
831
                value: SectionValue::BasicAuth(None),
832
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 12)),
833
            }
834
        );
835
        assert_eq!(reader.cursor().pos, Pos { line: 2, column: 1 });
836
    }
837
}