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
    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
14425
pub fn request_sections(reader: &mut Reader) -> ParseResult<Vec<Section>> {
35
14425
    let sections = zero_or_more(request_section, reader)?;
36
14375
    Ok(sections)
37
}
38

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

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

            
51
2460
    let line_terminator0 = line_terminator(reader)?;
52
2460
    let value = match name.as_str() {
53
2460
        "Query" => section_value_query_params(reader, true)?,
54
2345
        "QueryStringParams" => section_value_query_params(reader, false)?,
55
2125
        "BasicAuth" => section_value_basic_auth(reader)?,
56
2050
        "Form" => section_value_form_params(reader, true)?,
57
2015
        "FormParams" => section_value_form_params(reader, false)?,
58
1940
        "Multipart" => section_value_multipart_form_data(reader, true)?,
59
1905
        "MultipartFormData" => section_value_multipart_form_data(reader, false)?,
60
1795
        "Cookies" => section_value_cookies(reader)?,
61
1715
        "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
2410
    Ok(Section {
70
2410
        line_terminators,
71
2410
        space0,
72
2410
        line_terminator0,
73
2410
        value,
74
2410
        source_info,
75
2410
    })
76
}
77

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

            
86
8550
    let line_terminator0 = line_terminator(reader)?;
87
8550
    let value = match name.as_str() {
88
8550
        "Captures" => section_value_captures(reader)?,
89
8090
        "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
8495
    Ok(Section {
98
8495
        line_terminators,
99
8495
        space0,
100
8495
        line_terminator0,
101
8495
        value,
102
8495
        source_info,
103
8495
    })
104
}
105

            
106
36400
fn section_name(reader: &mut Reader) -> ParseResult<String> {
107
36400
    let pos = reader.cursor().pos;
108
36400
    try_literal("[", reader)?;
109
94211
    let name = reader.read_while(|c| c.is_alphanumeric());
110
11055
    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
11015
    try_literal("]", reader)?;
118
11010
    Ok(name)
119
}
120

            
121
335
fn section_value_query_params(reader: &mut Reader, short: bool) -> ParseResult<SectionValue> {
122
335
    let items = zero_or_more(key_value, reader)?;
123
335
    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
80
fn section_value_cookies(reader: &mut Reader) -> ParseResult<SectionValue> {
145
80
    let items = zero_or_more(cookie, reader)?;
146
80
    Ok(SectionValue::Cookies(items))
147
}
148

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

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

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

            
164
160
fn cookie(reader: &mut Reader) -> ParseResult<Cookie> {
165
    // let start = reader.state.clone();
166
160
    let line_terminators = optional_line_terminators(reader)?;
167
160
    let space0 = zero_or_more_spaces(reader)?;
168
160
    let name = recover(key_string::parse, reader)?;
169
105
    let space1 = zero_or_more_spaces(reader)?;
170
126
    recover(|p1| literal(":", p1), reader)?;
171
80
    let space2 = zero_or_more_spaces(reader)?;
172
80
    let value = unquoted_template(reader)?;
173
80
    let line_terminator0 = line_terminator(reader)?;
174
80
    Ok(Cookie {
175
80
        line_terminators,
176
80
        space0,
177
80
        name,
178
80
        space1,
179
80
        space2,
180
80
        value,
181
80
        line_terminator0,
182
80
    })
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 start = reader.cursor();
244
70
            let Ok(content_type) = unquoted_template(reader) else {
245
5
                return Err(ParseError::new(
246
5
                    start.pos,
247
5
                    false,
248
5
                    ParseErrorKind::FileContentType,
249
5
                ));
250
            };
251
65
            (space2, Some(content_type))
252
        }
253
    };
254

            
255
200
    Ok(FileValue {
256
200
        space0,
257
200
        filename: f,
258
200
        space1,
259
200
        space2,
260
200
        content_type,
261
200
    })
262
}
263

            
264
2335
fn capture(reader: &mut Reader) -> ParseResult<Capture> {
265
2335
    let line_terminators = optional_line_terminators(reader)?;
266
2335
    let space0 = zero_or_more_spaces(reader)?;
267
2335
    let name = recover(key_string::parse, reader)?;
268
2015
    let space1 = zero_or_more_spaces(reader)?;
269
2418
    recover(|p1| literal(":", p1), reader)?;
270
1895
    let space2 = zero_or_more_spaces(reader)?;
271
1895
    let q = query(reader)?;
272
1895
    let filters = filters(reader)?;
273
1895
    let space3 = zero_or_more_spaces(reader)?;
274
1895
    let redact = try_literal("redact", reader).is_ok();
275
1895
    let line_terminator0 = line_terminator(reader)?;
276
1895
    Ok(Capture {
277
1895
        line_terminators,
278
1895
        space0,
279
1895
        name,
280
1895
        space1,
281
1895
        space2,
282
1895
        query: q,
283
1895
        filters,
284
1895
        space3,
285
1895
        redact,
286
1895
        line_terminator0,
287
1895
    })
288
}
289

            
290
25825
fn assert(reader: &mut Reader) -> ParseResult<Assert> {
291
25825
    let line_terminators = optional_line_terminators(reader)?;
292
25825
    let space0 = zero_or_more_spaces(reader)?;
293
25825
    let query0 = query(reader)?;
294
18410
    let filters = filters(reader)?;
295
18410
    let space1 = one_or_more_spaces(reader)?;
296
18410
    let predicate0 = predicate(reader)?;
297

            
298
18380
    let line_terminator0 = line_terminator(reader)?;
299
18380
    Ok(Assert {
300
18380
        line_terminators,
301
18380
        space0,
302
18380
        query: query0,
303
18380
        filters,
304
18380
        space1,
305
18380
        predicate: predicate0,
306
18380
        line_terminator0,
307
18380
    })
308
}
309

            
310
#[cfg(test)]
311
mod tests {
312
    use super::*;
313
    use crate::ast::{
314
        KeyValue, LineTerminator, Number, Predicate, PredicateFunc, PredicateFuncValue,
315
        PredicateValue, Query, QueryValue, Template, TemplateElement, I64,
316
    };
317
    use crate::typing::ToSource;
318

            
319
    #[test]
320
    fn test_section_name() {
321
        let mut reader = Reader::new("[SectionA]");
322
        assert_eq!(section_name(&mut reader).unwrap(), String::from("SectionA"));
323

            
324
        let mut reader = Reader::new("[]");
325
        assert!(section_name(&mut reader).err().unwrap().recoverable);
326
    }
327

            
328
    #[test]
329
    fn test_asserts_section() {
330
        let mut reader = Reader::new("[Asserts]\nheader \"Location\" == \"https://google.fr\"\n");
331

            
332
        assert_eq!(
333
            response_section(&mut reader).unwrap(),
334
            Section {
335
                line_terminators: vec![],
336
                space0: Whitespace {
337
                    value: String::new(),
338
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
339
                },
340
                line_terminator0: LineTerminator {
341
                    space0: Whitespace {
342
                        value: String::new(),
343
                        source_info: SourceInfo::new(Pos::new(1, 10), Pos::new(1, 10)),
344
                    },
345
                    comment: None,
346
                    newline: Whitespace {
347
                        value: String::from("\n"),
348
                        source_info: SourceInfo::new(Pos::new(1, 10), Pos::new(2, 1)),
349
                    },
350
                },
351
                value: SectionValue::Asserts(vec![Assert {
352
                    line_terminators: vec![],
353
                    space0: Whitespace {
354
                        value: String::new(),
355
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 1)),
356
                    },
357
                    query: Query {
358
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 18)),
359
                        value: QueryValue::Header {
360
                            space0: Whitespace {
361
                                value: String::from(" "),
362
                                source_info: SourceInfo::new(Pos::new(2, 7), Pos::new(2, 8)),
363
                            },
364
                            name: Template::new(
365
                                Some('"'),
366
                                vec![TemplateElement::String {
367
                                    value: "Location".to_string(),
368
                                    source: "Location".to_source(),
369
                                }],
370
                                SourceInfo::new(Pos::new(2, 8), Pos::new(2, 18)),
371
                            ),
372
                        },
373
                    },
374
                    filters: vec![],
375
                    space1: Whitespace {
376
                        value: String::from(" "),
377
                        source_info: SourceInfo::new(Pos::new(2, 18), Pos::new(2, 19)),
378
                    },
379
                    predicate: Predicate {
380
                        not: false,
381
                        space0: Whitespace {
382
                            value: String::new(),
383
                            source_info: SourceInfo::new(Pos::new(2, 19), Pos::new(2, 19)),
384
                        },
385
                        predicate_func: PredicateFunc {
386
                            source_info: SourceInfo::new(Pos::new(2, 19), Pos::new(2, 41)),
387
                            value: PredicateFuncValue::Equal {
388
                                space0: Whitespace {
389
                                    value: String::from(" "),
390
                                    source_info: SourceInfo::new(Pos::new(2, 21), Pos::new(2, 22)),
391
                                },
392
                                value: PredicateValue::String(Template::new(
393
                                    Some('"'),
394
                                    vec![TemplateElement::String {
395
                                        value: "https://google.fr".to_string(),
396
                                        source: "https://google.fr".to_source(),
397
                                    }],
398
                                    SourceInfo::new(Pos::new(2, 22), Pos::new(2, 41))
399
                                )),
400
                            },
401
                        },
402
                    },
403
                    line_terminator0: LineTerminator {
404
                        space0: Whitespace {
405
                            value: String::new(),
406
                            source_info: SourceInfo::new(Pos::new(2, 41), Pos::new(2, 41)),
407
                        },
408
                        comment: None,
409
                        newline: Whitespace {
410
                            value: String::from("\n"),
411
                            source_info: SourceInfo::new(Pos::new(2, 41), Pos::new(3, 1)),
412
                        },
413
                    },
414
                }]),
415
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 10)),
416
            }
417
        );
418
    }
419

            
420
    #[test]
421
    fn test_asserts_section_error() {
422
        let mut reader = Reader::new("x[Assertsx]\nheader Location == \"https://google.fr\"\n");
423
        let error = response_section(&mut reader).err().unwrap();
424
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
425
        assert_eq!(
426
            error.kind,
427
            ParseErrorKind::Expecting {
428
                value: String::from("[")
429
            }
430
        );
431
        assert!(error.recoverable);
432

            
433
        let mut reader = Reader::new("[Assertsx]\nheader Location == \"https://google.fr\"\n");
434
        let error = response_section(&mut reader).err().unwrap();
435
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
436
        assert_eq!(
437
            error.kind,
438
            ParseErrorKind::ResponseSectionName {
439
                name: String::from("Assertsx")
440
            }
441
        );
442
        assert!(!error.recoverable);
443
    }
444

            
445
    #[test]
446
    fn test_cookie() {
447
        let mut reader = Reader::new("Foo: Bar");
448
        let c = cookie(&mut reader).unwrap();
449
        assert_eq!(c.name.to_string(), String::from("Foo"));
450
        assert_eq!(
451
            c.value,
452
            Template::new(
453
                None,
454
                vec![TemplateElement::String {
455
                    value: "Bar".to_string(),
456
                    source: "Bar".to_source(),
457
                }],
458
                SourceInfo::new(Pos::new(1, 6), Pos::new(1, 9))
459
            )
460
        );
461
    }
462

            
463
    #[test]
464
    fn test_cookie_error() {
465
        let mut reader = Reader::new("Foo: {{Bar");
466
        let error = cookie(&mut reader).err().unwrap();
467
        assert_eq!(
468
            error.pos,
469
            Pos {
470
                line: 1,
471
                column: 11,
472
            }
473
        );
474
        assert!(!error.recoverable);
475
        assert_eq!(
476
            error.kind,
477
            ParseErrorKind::Expecting {
478
                value: "}}".to_string()
479
            }
480
        );
481
    }
482

            
483
    #[test]
484
    fn test_file_value() {
485
        let mut reader = Reader::new("file,hello.txt;");
486
        assert_eq!(
487
            file_value(&mut reader).unwrap(),
488
            FileValue {
489
                space0: Whitespace {
490
                    value: String::new(),
491
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
492
                },
493
                filename: Template::new(
494
                    None,
495
                    vec![TemplateElement::String {
496
                        value: "hello.txt".to_string(),
497
                        source: "hello.txt".to_source(),
498
                    }],
499
                    SourceInfo::new(Pos::new(1, 6), Pos::new(1, 15)),
500
                ),
501
                space1: Whitespace {
502
                    value: String::new(),
503
                    source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
504
                },
505
                space2: Whitespace {
506
                    value: String::new(),
507
                    source_info: SourceInfo::new(Pos::new(1, 16), Pos::new(1, 16)),
508
                },
509
                content_type: None,
510
            }
511
        );
512
        let mut reader = Reader::new("file,hello.txt; text/html");
513
        assert_eq!(
514
            file_value(&mut reader).unwrap(),
515
            FileValue {
516
                space0: Whitespace {
517
                    value: String::new(),
518
                    source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
519
                },
520
                filename: Template::new(
521
                    None,
522
                    vec![TemplateElement::String {
523
                        value: "hello.txt".to_string(),
524
                        source: "hello.txt".to_source(),
525
                    }],
526
                    SourceInfo::new(Pos::new(1, 6), Pos::new(1, 15)),
527
                ),
528
                space1: Whitespace {
529
                    value: String::new(),
530
                    source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
531
                },
532
                space2: Whitespace {
533
                    value: " ".to_string(),
534
                    source_info: SourceInfo::new(Pos::new(1, 16), Pos::new(1, 17)),
535
                },
536
                content_type: Some(Template::new(
537
                    None,
538
                    vec![TemplateElement::String {
539
                        value: "text/html".to_string(),
540
                        source: "text/html".to_source(),
541
                    }],
542
                    SourceInfo::new(Pos::new(1, 17), Pos::new(1, 26)),
543
                )),
544
            }
545
        );
546
    }
547

            
548
    #[test]
549
    fn test_file_content_type() {
550
        let mut reader = Reader::new("file,hello.txt; text/html");
551
        let file_value = file_value(&mut reader).unwrap();
552
        let content_type = file_value.content_type.unwrap();
553
        assert_eq!(content_type.to_string(), "text/html".to_string());
554
        assert_eq!(reader.cursor().index, 25);
555

            
556
        let mut reader = Reader::new("file,------; text/plain; charset=us-ascii");
557
        let file_value = crate::parser::sections::file_value(&mut reader).unwrap();
558
        let content_type = file_value.content_type.unwrap();
559
        assert_eq!(
560
            content_type.to_string(),
561
            "text/plain; charset=us-ascii".to_string()
562
        );
563
        assert_eq!(reader.cursor().index, 41);
564

            
565
        let mut reader = Reader::new("file,******; text/html # comment");
566
        let file_value = crate::parser::sections::file_value(&mut reader).unwrap();
567
        let content_type = file_value.content_type.unwrap();
568
        assert_eq!(content_type.to_string(), "text/html".to_string());
569
        assert_eq!(reader.cursor().index, 22);
570

            
571
        let mut reader = Reader::new("file,{{some_file}}; application/vnd.openxmlformats-officedocument.wordprocessingml.document # comment");
572
        let file_value = crate::parser::sections::file_value(&mut reader).unwrap();
573
        let content_type = file_value.content_type.unwrap();
574
        assert_eq!(
575
            content_type.to_string(),
576
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document".to_string()
577
        );
578
        assert_eq!(reader.cursor().index, 91);
579

            
580
        let mut reader = Reader::new("file,{{some_file}}; {{some_content_type}} # comment");
581
        let file_value = crate::parser::sections::file_value(&mut reader).unwrap();
582
        let content_type = file_value.content_type.unwrap();
583
        assert_eq!(
584
            content_type.to_string(),
585
            "{{some_content_type}}".to_string()
586
        );
587
        assert_eq!(reader.cursor().index, 41);
588
    }
589

            
590
    #[test]
591
    fn test_capture() {
592
        let mut reader = Reader::new("url: header \"Location\"");
593
        let capture0 = capture(&mut reader).unwrap();
594

            
595
        assert_eq!(
596
            capture0.name,
597
            Template::new(
598
                None,
599
                vec![TemplateElement::String {
600
                    value: "url".to_string(),
601
                    source: "url".to_source(),
602
                }],
603
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4))
604
            ),
605
        );
606
        assert_eq!(
607
            capture0.query,
608
            Query {
609
                source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 23)),
610
                value: QueryValue::Header {
611
                    space0: Whitespace {
612
                        value: String::from(" "),
613
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(1, 13)),
614
                    },
615
                    name: Template::new(
616
                        Some('"'),
617
                        vec![TemplateElement::String {
618
                            value: "Location".to_string(),
619
                            source: "Location".to_source(),
620
                        }],
621
                        SourceInfo::new(Pos::new(1, 13), Pos::new(1, 23))
622
                    )
623
                }
624
            }
625
        );
626

            
627
        let mut reader = Reader::new("url: header \"Token\" redact");
628
        let capture0 = capture(&mut reader).unwrap();
629
        assert!(capture0.redact);
630
    }
631

            
632
    #[test]
633
    fn test_capture_with_filter() {
634
        let mut reader = Reader::new("token: header \"Location\" regex \"token=(.*)\"");
635
        let capture0 = capture(&mut reader).unwrap();
636

            
637
        assert_eq!(
638
            capture0.query,
639
            Query {
640
                source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 25)),
641
                value: QueryValue::Header {
642
                    space0: Whitespace {
643
                        value: String::from(" "),
644
                        source_info: SourceInfo::new(Pos::new(1, 14), Pos::new(1, 15)),
645
                    },
646
                    name: Template::new(
647
                        Some('"'),
648
                        vec![TemplateElement::String {
649
                            value: "Location".to_string(),
650
                            source: "Location".to_source(),
651
                        }],
652
                        SourceInfo::new(Pos::new(1, 15), Pos::new(1, 25))
653
                    )
654
                }
655
            }
656
        );
657
        assert_eq!(reader.cursor().index, 43);
658
    }
659

            
660
    #[test]
661
    fn test_capture_with_filter_error() {
662
        let mut reader = Reader::new("token: header \"Location\" regex ");
663
        let error = capture(&mut reader).err().unwrap();
664
        assert_eq!(
665
            error.pos,
666
            Pos {
667
                line: 1,
668
                column: 32,
669
            }
670
        );
671
        assert_eq!(
672
            error.kind,
673
            ParseErrorKind::Expecting {
674
                value: "\" or /".to_string()
675
            }
676
        );
677
        assert!(!error.recoverable);
678

            
679
        let mut reader = Reader::new("token: header \"Location\" xxx");
680
        let error = capture(&mut reader).err().unwrap();
681
        assert_eq!(
682
            error.pos,
683
            Pos {
684
                line: 1,
685
                column: 26,
686
            }
687
        );
688
        assert_eq!(
689
            error.kind,
690
            ParseErrorKind::Expecting {
691
                value: "line_terminator".to_string()
692
            }
693
        );
694
        assert!(!error.recoverable);
695
    }
696

            
697
    #[test]
698
    fn test_assert() {
699
        let mut reader = Reader::new("header \"Location\" == \"https://google.fr\"");
700
        let assert0 = assert(&mut reader).unwrap();
701

            
702
        assert_eq!(
703
            assert0.query,
704
            Query {
705
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 18)),
706
                value: QueryValue::Header {
707
                    space0: Whitespace {
708
                        value: String::from(" "),
709
                        source_info: SourceInfo::new(Pos::new(1, 7), Pos::new(1, 8)),
710
                    },
711
                    name: Template::new(
712
                        Some('"'),
713
                        vec![TemplateElement::String {
714
                            value: "Location".to_string(),
715
                            source: "Location".to_source(),
716
                        }],
717
                        SourceInfo::new(Pos::new(1, 8), Pos::new(1, 18)),
718
                    )
719
                }
720
            }
721
        );
722
    }
723

            
724
    #[test]
725
    fn test_assert_jsonpath() {
726
        let mut reader = Reader::new("jsonpath \"$.errors\" == 5");
727

            
728
        assert_eq!(
729
            assert(&mut reader).unwrap().predicate,
730
            Predicate {
731
                not: false,
732
                space0: Whitespace {
733
                    value: String::new(),
734
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 21)),
735
                },
736
                predicate_func: PredicateFunc {
737
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 25)),
738
                    value: PredicateFuncValue::Equal {
739
                        space0: Whitespace {
740
                            value: String::from(" "),
741
                            source_info: SourceInfo::new(Pos::new(1, 23), Pos::new(1, 24)),
742
                        },
743
                        value: PredicateValue::Number(Number::Integer(I64::new(
744
                            5,
745
                            "5".to_source()
746
                        ))),
747
                    },
748
                },
749
            }
750
        );
751
    }
752

            
753
    #[test]
754
    fn test_basicauth_section() {
755
        let mut reader = Reader::new("[BasicAuth]\nuser:password\n\nHTTP 200\n");
756

            
757
        assert_eq!(
758
            request_section(&mut reader).unwrap(),
759
            Section {
760
                line_terminators: vec![],
761
                space0: Whitespace {
762
                    value: String::new(),
763
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
764
                },
765
                line_terminator0: LineTerminator {
766
                    space0: Whitespace {
767
                        value: String::new(),
768
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(1, 12)),
769
                    },
770
                    comment: None,
771
                    newline: Whitespace {
772
                        value: String::from("\n"),
773
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(2, 1)),
774
                    },
775
                },
776
                value: SectionValue::BasicAuth(Some(KeyValue {
777
                    line_terminators: vec![],
778
                    space0: Whitespace {
779
                        value: String::new(),
780
                        source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 1))
781
                    },
782
                    key: Template::new(
783
                        None,
784
                        vec![TemplateElement::String {
785
                            value: "user".to_string(),
786
                            source: "user".to_source()
787
                        }],
788
                        SourceInfo::new(Pos::new(2, 1), Pos::new(2, 5)),
789
                    ),
790
                    space1: Whitespace {
791
                        value: String::new(),
792
                        source_info: SourceInfo::new(Pos::new(2, 5), Pos::new(2, 5))
793
                    },
794
                    space2: Whitespace {
795
                        value: String::new(),
796
                        source_info: SourceInfo::new(Pos::new(2, 6), Pos::new(2, 6))
797
                    },
798
                    value: Template::new(
799
                        None,
800
                        vec![TemplateElement::String {
801
                            value: "password".to_string(),
802
                            source: "password".to_source()
803
                        }],
804
                        SourceInfo::new(Pos::new(2, 6), Pos::new(2, 14)),
805
                    ),
806
                    line_terminator0: LineTerminator {
807
                        space0: Whitespace {
808
                            value: String::new(),
809
                            source_info: SourceInfo::new(Pos::new(2, 14), Pos::new(2, 14))
810
                        },
811
                        comment: None,
812
                        newline: Whitespace {
813
                            value: "\n".to_string(),
814
                            source_info: SourceInfo::new(Pos::new(2, 14), Pos::new(3, 1))
815
                        },
816
                    },
817
                })),
818
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 12)),
819
            }
820
        );
821
        assert_eq!(reader.cursor().pos, Pos { line: 3, column: 1 });
822

            
823
        let mut reader = Reader::new("[BasicAuth]\nHTTP 200\n");
824
        assert_eq!(
825
            request_section(&mut reader).unwrap(),
826
            Section {
827
                line_terminators: vec![],
828
                space0: Whitespace {
829
                    value: String::new(),
830
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
831
                },
832
                line_terminator0: LineTerminator {
833
                    space0: Whitespace {
834
                        value: String::new(),
835
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(1, 12)),
836
                    },
837
                    comment: None,
838
                    newline: Whitespace {
839
                        value: String::from("\n"),
840
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(2, 1)),
841
                    },
842
                },
843
                value: SectionValue::BasicAuth(None),
844
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 12)),
845
            }
846
        );
847
        assert_eq!(reader.cursor().pos, Pos { line: 2, column: 1 });
848
    }
849
}