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

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

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

            
51
2485
    let line_terminator0 = line_terminator(reader)?;
52
2485
    let value = match name.as_str() {
53
2485
        "Query" => section_value_query_params(reader, true)?,
54
2370
        "QueryStringParams" => section_value_query_params(reader, false)?,
55
2150
        "BasicAuth" => section_value_basic_auth(reader)?,
56
2075
        "Form" => section_value_form_params(reader, true)?,
57
2040
        "FormParams" => section_value_form_params(reader, false)?,
58
1965
        "Multipart" => section_value_multipart_form_data(reader, true)?,
59
1930
        "MultipartFormData" => section_value_multipart_form_data(reader, false)?,
60
1820
        "Cookies" => section_value_cookies(reader)?,
61
1740
        "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
2435
    Ok(Section {
70
2435
        line_terminators,
71
2435
        space0,
72
2435
        line_terminator0,
73
2435
        value,
74
2435
        source_info,
75
2435
    })
76
}
77

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

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

            
106
36535
fn section_name(reader: &mut Reader) -> ParseResult<String> {
107
36535
    let pos = reader.cursor().pos;
108
36535
    try_literal("[", reader)?;
109
94846
    let name = reader.read_while(|c| c.is_alphanumeric());
110
11130
    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
11090
    try_literal("]", reader)?;
118
11085
    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
480
fn section_value_captures(reader: &mut Reader) -> ParseResult<SectionValue> {
150
480
    let items = zero_or_more(capture, reader)?;
151
480
    Ok(SectionValue::Captures(items))
152
}
153

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

            
159
1730
fn section_value_options(reader: &mut Reader) -> ParseResult<SectionValue> {
160
1730
    let options = zero_or_more(option::parse, reader)?;
161
1695
    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
2445
fn capture(reader: &mut Reader) -> ParseResult<Capture> {
265
2445
    let line_terminators = optional_line_terminators(reader)?;
266
2445
    let space0 = zero_or_more_spaces(reader)?;
267
2445
    let name = recover(key_string::parse, reader)?;
268
2105
    let space1 = zero_or_more_spaces(reader)?;
269
2526
    recover(|p1| literal(":", p1), reader)?;
270
1985
    let space2 = zero_or_more_spaces(reader)?;
271
1985
    let q = query(reader)?;
272
1985
    let filters = filters(reader)?;
273
1985
    let space3 = zero_or_more_spaces(reader)?;
274
1985
    let redact = try_literal("redact", reader).is_ok();
275
1985
    let line_terminator0 = line_terminator(reader)?;
276
1985
    Ok(Capture {
277
1985
        line_terminators,
278
1985
        space0,
279
1985
        name,
280
1985
        space1,
281
1985
        space2,
282
1985
        query: q,
283
1985
        filters,
284
1985
        space3,
285
1985
        redact,
286
1985
        line_terminator0,
287
1985
    })
288
}
289

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

            
298
19200
    let line_terminator0 = line_terminator(reader)?;
299
19200
    Ok(Assert {
300
19200
        line_terminators,
301
19200
        space0,
302
19200
        query: query0,
303
19200
        filters,
304
19200
        space1,
305
19200
        predicate: predicate0,
306
19200
        line_terminator0,
307
19200
    })
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_capture_with_comment() {
699
        let mut reader = Reader::new("name: jsonpath \"$.name\"          # name");
700
        let capture0 = capture(&mut reader).unwrap();
701
        assert!(capture0.filters.is_empty());
702
        assert_eq!(capture0.space3.as_str(), "          ");
703
        assert_eq!(capture0.line_terminator0.space0.as_str(), "");
704
    }
705

            
706
    #[test]
707
    fn test_assert() {
708
        let mut reader = Reader::new("header \"Location\" == \"https://google.fr\"");
709
        let assert0 = assert(&mut reader).unwrap();
710

            
711
        assert_eq!(
712
            assert0.query,
713
            Query {
714
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 18)),
715
                value: QueryValue::Header {
716
                    space0: Whitespace {
717
                        value: String::from(" "),
718
                        source_info: SourceInfo::new(Pos::new(1, 7), Pos::new(1, 8)),
719
                    },
720
                    name: Template::new(
721
                        Some('"'),
722
                        vec![TemplateElement::String {
723
                            value: "Location".to_string(),
724
                            source: "Location".to_source(),
725
                        }],
726
                        SourceInfo::new(Pos::new(1, 8), Pos::new(1, 18)),
727
                    )
728
                }
729
            }
730
        );
731
    }
732

            
733
    #[test]
734
    fn test_assert_jsonpath() {
735
        let mut reader = Reader::new("jsonpath \"$.errors\" == 5");
736

            
737
        assert_eq!(
738
            assert(&mut reader).unwrap().predicate,
739
            Predicate {
740
                not: false,
741
                space0: Whitespace {
742
                    value: String::new(),
743
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 21)),
744
                },
745
                predicate_func: PredicateFunc {
746
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 25)),
747
                    value: PredicateFuncValue::Equal {
748
                        space0: Whitespace {
749
                            value: String::from(" "),
750
                            source_info: SourceInfo::new(Pos::new(1, 23), Pos::new(1, 24)),
751
                        },
752
                        value: PredicateValue::Number(Number::Integer(I64::new(
753
                            5,
754
                            "5".to_source()
755
                        ))),
756
                    },
757
                },
758
            }
759
        );
760
    }
761

            
762
    #[test]
763
    fn test_basicauth_section() {
764
        let mut reader = Reader::new("[BasicAuth]\nuser:password\n\nHTTP 200\n");
765

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

            
832
        let mut reader = Reader::new("[BasicAuth]\nHTTP 200\n");
833
        assert_eq!(
834
            request_section(&mut reader).unwrap(),
835
            Section {
836
                line_terminators: vec![],
837
                space0: Whitespace {
838
                    value: String::new(),
839
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
840
                },
841
                line_terminator0: LineTerminator {
842
                    space0: Whitespace {
843
                        value: String::new(),
844
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(1, 12)),
845
                    },
846
                    comment: None,
847
                    newline: Whitespace {
848
                        value: String::from("\n"),
849
                        source_info: SourceInfo::new(Pos::new(1, 12), Pos::new(2, 1)),
850
                    },
851
                },
852
                value: SectionValue::BasicAuth(None),
853
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 12)),
854
            }
855
        );
856
        assert_eq!(reader.cursor().pos, Pos { line: 2, column: 1 });
857
    }
858
}