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 super::placeholder;
19
use crate::ast::{
20
    is_variable_reserved, BooleanOption, CountOption, DurationOption, EntryOption, NaturalOption,
21
    OptionKind, SourceInfo, VariableDefinition, VariableValue,
22
};
23
use crate::combinator::{choice, non_recover};
24
use crate::parser::duration::duration;
25
use crate::parser::number::{integer, natural, number};
26
use crate::parser::primitives::{
27
    boolean, line_terminator, literal, null, optional_line_terminators, try_literal,
28
    zero_or_more_spaces,
29
};
30
use crate::parser::string::{quoted_template, unquoted_template};
31
use crate::parser::{filename, filename_password, ParseError, ParseErrorKind, ParseResult};
32
use crate::reader::Reader;
33
use crate::typing::Count;
34

            
35
/// Parse an option in an `[Options]` section.
36
5425
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
37
5425
    let line_terminators = optional_line_terminators(reader)?;
38
5425
    let space0 = zero_or_more_spaces(reader)?;
39
5425
    let start = reader.cursor();
40
5425
    // We accept '_' even if there is no option name with this character. We do this to be able to
41
5425
    // enter the parsing of the option name and to have better error description (ex: 'max-redirs'
42
5425
    // vs 'max_redirs').
43
5425
    let option =
44
41575
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
45
5425
    let space1 = zero_or_more_spaces(reader)?;
46
5425
    try_literal(":", reader)?;
47
3685
    let space2 = zero_or_more_spaces(reader)?;
48
3685
    let kind = match option.as_str() {
49
3685
        "aws-sigv4" => option_aws_sigv4(reader)?,
50
3645
        "cacert" => option_cacert(reader)?,
51
3585
        "cert" => option_cert(reader)?,
52
3520
        "compressed" => option_compressed(reader)?,
53
3110
        "connect-to" => option_connect_to(reader)?,
54
3045
        "connect-timeout" => option_connect_timeout(reader)?,
55
3015
        "delay" => option_delay(reader)?,
56
2840
        "insecure" => option_insecure(reader)?,
57
2745
        "header" => option_header(reader)?,
58
2690
        "http1.0" => option_http_10(reader)?,
59
2570
        "http1.1" => option_http_11(reader)?,
60
2480
        "http2" => option_http_2(reader)?,
61
2430
        "http3" => option_http_3(reader)?,
62
2400
        "ipv4" => option_ipv4(reader)?,
63
2370
        "ipv6" => option_ipv6(reader)?,
64
2340
        "key" => option_key(reader)?,
65
2290
        "limit-rate" => option_limit_rate(reader)?,
66
2255
        "location" => option_follow_location(reader)?,
67
1850
        "location-trusted" => option_follow_location_trusted(reader)?,
68
1800
        "max-redirs" => option_max_redirect(reader)?,
69
1695
        "max-time" => option_max_time(reader)?,
70
1665
        "netrc" => option_netrc(reader)?,
71
1635
        "netrc-file" => option_netrc_file(reader)?,
72
1600
        "netrc-optional" => option_netrc_optional(reader)?,
73
1570
        "output" => option_output(reader)?,
74
1440
        "path-as-is" => option_path_as_is(reader)?,
75
1405
        "proxy" => option_proxy(reader)?,
76
1335
        "repeat" => option_repeat(reader)?,
77
1245
        "resolve" => option_resolve(reader)?,
78
1195
        "retry" => option_retry(reader)?,
79
1045
        "retry-interval" => option_retry_interval(reader)?,
80
935
        "skip" => option_skip(reader)?,
81
895
        "unix-socket" => option_unix_socket(reader)?,
82
865
        "user" => option_user(reader)?,
83
765
        "variable" => option_variable(reader)?,
84
190
        "verbose" => option_verbose(reader)?,
85
60
        "very-verbose" => option_very_verbose(reader)?,
86
        _ => {
87
10
            return Err(ParseError::new(
88
10
                start.pos,
89
10
                false,
90
10
                ParseErrorKind::InvalidOption(option.to_string()),
91
10
            ))
92
        }
93
    };
94

            
95
3650
    let line_terminator0 = line_terminator(reader)?;
96
3650
    Ok(EntryOption {
97
3650
        line_terminators,
98
3650
        space0,
99
3650
        space1,
100
3650
        space2,
101
3650
        kind,
102
3650
        line_terminator0,
103
3650
    })
104
}
105

            
106
40
fn option_aws_sigv4(reader: &mut Reader) -> ParseResult<OptionKind> {
107
40
    let value = unquoted_template(reader)?;
108
40
    Ok(OptionKind::AwsSigV4(value))
109
}
110

            
111
60
fn option_cacert(reader: &mut Reader) -> ParseResult<OptionKind> {
112
60
    let value = filename::parse(reader)?;
113
55
    Ok(OptionKind::CaCertificate(value))
114
}
115

            
116
65
fn option_cert(reader: &mut Reader) -> ParseResult<OptionKind> {
117
65
    let value = filename_password::parse(reader)?;
118
65
    Ok(OptionKind::ClientCert(value))
119
}
120

            
121
410
fn option_compressed(reader: &mut Reader) -> ParseResult<OptionKind> {
122
410
    let value = non_recover(boolean_option, reader)?;
123
410
    Ok(OptionKind::Compressed(value))
124
}
125

            
126
65
fn option_connect_to(reader: &mut Reader) -> ParseResult<OptionKind> {
127
65
    let value = unquoted_template(reader)?;
128
65
    Ok(OptionKind::ConnectTo(value))
129
}
130

            
131
30
fn option_connect_timeout(reader: &mut Reader) -> ParseResult<OptionKind> {
132
30
    let value = duration_option(reader)?;
133
30
    Ok(OptionKind::ConnectTimeout(value))
134
}
135

            
136
175
fn option_delay(reader: &mut Reader) -> ParseResult<OptionKind> {
137
175
    let value = duration_option(reader)?;
138
170
    Ok(OptionKind::Delay(value))
139
}
140

            
141
405
fn option_follow_location(reader: &mut Reader) -> ParseResult<OptionKind> {
142
405
    let value = non_recover(boolean_option, reader)?;
143
405
    Ok(OptionKind::FollowLocation(value))
144
}
145

            
146
50
fn option_follow_location_trusted(reader: &mut Reader) -> ParseResult<OptionKind> {
147
50
    let value = non_recover(boolean_option, reader)?;
148
50
    Ok(OptionKind::FollowLocationTrusted(value))
149
}
150

            
151
55
fn option_header(reader: &mut Reader) -> ParseResult<OptionKind> {
152
55
    let value = unquoted_template(reader)?;
153
55
    Ok(OptionKind::Header(value))
154
}
155

            
156
120
fn option_http_10(reader: &mut Reader) -> ParseResult<OptionKind> {
157
120
    let value = non_recover(boolean_option, reader)?;
158
120
    Ok(OptionKind::Http10(value))
159
}
160

            
161
90
fn option_http_11(reader: &mut Reader) -> ParseResult<OptionKind> {
162
90
    let value = non_recover(boolean_option, reader)?;
163
90
    Ok(OptionKind::Http11(value))
164
}
165

            
166
50
fn option_http_2(reader: &mut Reader) -> ParseResult<OptionKind> {
167
50
    let value = non_recover(boolean_option, reader)?;
168
50
    Ok(OptionKind::Http2(value))
169
}
170

            
171
30
fn option_http_3(reader: &mut Reader) -> ParseResult<OptionKind> {
172
30
    let value = non_recover(boolean_option, reader)?;
173
30
    Ok(OptionKind::Http3(value))
174
}
175

            
176
95
fn option_insecure(reader: &mut Reader) -> ParseResult<OptionKind> {
177
95
    let value = non_recover(boolean_option, reader)?;
178
90
    Ok(OptionKind::Insecure(value))
179
}
180

            
181
30
fn option_ipv4(reader: &mut Reader) -> ParseResult<OptionKind> {
182
30
    let value = non_recover(boolean_option, reader)?;
183
30
    Ok(OptionKind::IpV4(value))
184
}
185

            
186
30
fn option_ipv6(reader: &mut Reader) -> ParseResult<OptionKind> {
187
30
    let value = non_recover(boolean_option, reader)?;
188
30
    Ok(OptionKind::IpV6(value))
189
}
190

            
191
50
fn option_key(reader: &mut Reader) -> ParseResult<OptionKind> {
192
50
    let value = filename::parse(reader)?;
193
50
    Ok(OptionKind::ClientKey(value))
194
}
195

            
196
35
fn option_limit_rate(reader: &mut Reader) -> ParseResult<OptionKind> {
197
35
    let value = non_recover(natural_option, reader)?;
198
35
    Ok(OptionKind::LimitRate(value))
199
}
200

            
201
105
fn option_max_redirect(reader: &mut Reader) -> ParseResult<OptionKind> {
202
105
    let value = non_recover(count_option, reader)?;
203
105
    Ok(OptionKind::MaxRedirect(value))
204
}
205

            
206
30
fn option_max_time(reader: &mut Reader) -> ParseResult<OptionKind> {
207
30
    let value = duration_option(reader)?;
208
30
    Ok(OptionKind::MaxTime(value))
209
}
210

            
211
30
fn option_netrc(reader: &mut Reader) -> ParseResult<OptionKind> {
212
30
    let value = non_recover(boolean_option, reader)?;
213
30
    Ok(OptionKind::NetRc(value))
214
}
215

            
216
35
fn option_netrc_file(reader: &mut Reader) -> ParseResult<OptionKind> {
217
35
    let value = unquoted_template(reader)?;
218
35
    Ok(OptionKind::NetRcFile(value))
219
}
220

            
221
30
fn option_netrc_optional(reader: &mut Reader) -> ParseResult<OptionKind> {
222
30
    let value = non_recover(boolean_option, reader)?;
223
30
    Ok(OptionKind::NetRcOptional(value))
224
}
225

            
226
130
fn option_output(reader: &mut Reader) -> ParseResult<OptionKind> {
227
130
    let value = filename::parse(reader)?;
228
130
    Ok(OptionKind::Output(value))
229
}
230

            
231
35
fn option_path_as_is(reader: &mut Reader) -> ParseResult<OptionKind> {
232
35
    let value = non_recover(boolean_option, reader)?;
233
35
    Ok(OptionKind::PathAsIs(value))
234
}
235

            
236
70
fn option_proxy(reader: &mut Reader) -> ParseResult<OptionKind> {
237
70
    let value = unquoted_template(reader)?;
238
70
    Ok(OptionKind::Proxy(value))
239
}
240

            
241
90
fn option_repeat(reader: &mut Reader) -> ParseResult<OptionKind> {
242
90
    let value = non_recover(count_option, reader)?;
243
90
    Ok(OptionKind::Repeat(value))
244
}
245

            
246
50
fn option_resolve(reader: &mut Reader) -> ParseResult<OptionKind> {
247
50
    let value = unquoted_template(reader)?;
248
50
    Ok(OptionKind::Resolve(value))
249
}
250

            
251
150
fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
252
150
    let value = non_recover(count_option, reader)?;
253
145
    Ok(OptionKind::Retry(value))
254
}
255

            
256
110
fn option_retry_interval(reader: &mut Reader) -> ParseResult<OptionKind> {
257
110
    let value = non_recover(duration_option, reader)?;
258
110
    Ok(OptionKind::RetryInterval(value))
259
}
260

            
261
40
fn option_skip(reader: &mut Reader) -> ParseResult<OptionKind> {
262
40
    let value = non_recover(boolean_option, reader)?;
263
40
    Ok(OptionKind::Skip(value))
264
}
265

            
266
100
fn option_user(reader: &mut Reader) -> ParseResult<OptionKind> {
267
100
    let value = unquoted_template(reader)?;
268
100
    Ok(OptionKind::User(value))
269
}
270

            
271
30
fn option_unix_socket(reader: &mut Reader) -> ParseResult<OptionKind> {
272
30
    let value = unquoted_template(reader)?;
273
30
    Ok(OptionKind::UnixSocket(value))
274
}
275

            
276
575
fn option_variable(reader: &mut Reader) -> ParseResult<OptionKind> {
277
575
    let value = variable_definition(reader)?;
278
570
    Ok(OptionKind::Variable(value))
279
}
280

            
281
130
fn option_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
282
130
    let value = non_recover(boolean_option, reader)?;
283
130
    Ok(OptionKind::Verbose(value))
284
}
285

            
286
50
fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
287
50
    let value = non_recover(boolean_option, reader)?;
288
50
    Ok(OptionKind::VeryVerbose(value))
289
}
290

            
291
345
fn count(reader: &mut Reader) -> ParseResult<Count> {
292
345
    let start = reader.cursor();
293
345
    let value = non_recover(integer, reader)?;
294
245
    if value.as_i64() == -1 {
295
40
        Ok(Count::Infinite)
296
205
    } else if value.as_i64() >= 0 {
297
200
        Ok(Count::Finite(value.as_i64() as usize))
298
    } else {
299
5
        let kind = ParseErrorKind::Expecting {
300
5
            value: "Expecting a count value".to_string(),
301
5
        };
302
5
        Err(ParseError::new(start.pos, false, kind))
303
    }
304
}
305

            
306
1625
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
307
1625
    let start = reader.cursor();
308
1625
    match boolean(reader) {
309
1340
        Ok(v) => Ok(BooleanOption::Literal(v)),
310
        Err(_) => {
311
285
            reader.seek(start);
312
286
            let exp = placeholder::parse(reader).map_err(|e| {
313
5
                let kind = ParseErrorKind::Expecting {
314
5
                    value: "true|false".to_string(),
315
5
                };
316
5
                ParseError::new(e.pos, false, kind)
317
286
            })?;
318
280
            Ok(BooleanOption::Placeholder(exp))
319
        }
320
    }
321
}
322

            
323
35
fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
324
35
    let start = reader.cursor();
325
35
    match natural(reader) {
326
20
        Ok(v) => Ok(NaturalOption::Literal(v)),
327
        Err(_) => {
328
15
            reader.seek(start);
329
15
            let placeholder = placeholder::parse(reader).map_err(|e| {
330
                let kind = ParseErrorKind::Expecting {
331
                    value: "integer >= 0".to_string(),
332
                };
333
                ParseError::new(e.pos, false, kind)
334
15
            })?;
335
15
            Ok(NaturalOption::Placeholder(placeholder))
336
        }
337
    }
338
}
339

            
340
345
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
341
345
    let start = reader.cursor();
342
345
    match count(reader) {
343
240
        Ok(v) => Ok(CountOption::Literal(v)),
344
        Err(_) => {
345
105
            reader.seek(start);
346
106
            let placeholder = placeholder::parse(reader).map_err(|e| {
347
5
                let kind = ParseErrorKind::Expecting {
348
5
                    value: "integer >= -1".to_string(),
349
5
                };
350
5
                ParseError::new(e.pos, false, kind)
351
106
            })?;
352
100
            Ok(CountOption::Placeholder(placeholder))
353
        }
354
    }
355
}
356

            
357
345
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
358
345
    let start = reader.cursor();
359
345
    match duration(reader) {
360
275
        Ok(v) => Ok(DurationOption::Literal(v)),
361
70
        Err(e) => {
362
70
            if e.recoverable {
363
65
                reader.seek(start);
364
65
                let placeholder = placeholder::parse(reader).map_err(|e| {
365
                    let kind = ParseErrorKind::Expecting {
366
                        value: "integer".to_string(),
367
                    };
368
                    ParseError::new(e.pos, false, kind)
369
65
                })?;
370
65
                Ok(DurationOption::Placeholder(placeholder))
371
            } else {
372
5
                Err(e)
373
            }
374
        }
375
    }
376
}
377

            
378
575
fn variable_definition(reader: &mut Reader) -> ParseResult<VariableDefinition> {
379
575
    let start = reader.cursor();
380
575
    let name = variable_name(reader)?;
381
570
    let space0 = zero_or_more_spaces(reader)?;
382
570
    literal("=", reader)?;
383
570
    let space1 = zero_or_more_spaces(reader)?;
384
570
    let value = variable_value(reader)?;
385
570
    let end = reader.cursor();
386
570
    let source_info = SourceInfo {
387
570
        start: start.pos,
388
570
        end: end.pos,
389
570
    };
390
570
    Ok(VariableDefinition {
391
570
        source_info,
392
570
        name,
393
570
        space0,
394
570
        space1,
395
570
        value,
396
570
    })
397
}
398

            
399
575
fn variable_name(reader: &mut Reader) -> ParseResult<String> {
400
575
    let start = reader.cursor();
401
3260
    let name = reader.read_while(|c| c.is_alphanumeric() || c == '_' || c == '-');
402
575
    if name.is_empty() {
403
        let kind = ParseErrorKind::Expecting {
404
            value: "variable name".to_string(),
405
        };
406
        return Err(ParseError::new(start.pos, false, kind));
407
575
    } else if is_variable_reserved(&name) {
408
5
        let kind = ParseErrorKind::Variable(format!(
409
5
            "conflicts with the {name} function, use a different name"
410
5
        ));
411
5
        return Err(ParseError::new(start.pos, false, kind));
412
    }
413
570
    Ok(name)
414
}
415

            
416
570
fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
417
570
    choice(
418
570
        &[
419
684
            |p1| match null(p1) {
420
15
                Ok(()) => Ok(VariableValue::Null),
421
555
                Err(e) => Err(e),
422
684
            },
423
681
            |p1| match boolean(p1) {
424
20
                Ok(value) => Ok(VariableValue::Bool(value)),
425
535
                Err(e) => Err(e),
426
681
            },
427
677
            |p1| match number(p1) {
428
195
                Ok(value) => Ok(VariableValue::Number(value)),
429
340
                Err(e) => Err(e),
430
677
            },
431
638
            |p1| match quoted_template(p1) {
432
                Ok(value) => Ok(VariableValue::String(value)),
433
340
                Err(e) => Err(e),
434
638
            },
435
638
            |p1| match unquoted_template(p1) {
436
340
                Ok(value) => Ok(VariableValue::String(value)),
437
                Err(e) => Err(e),
438
638
            },
439
570
        ],
440
570
        reader,
441
570
    )
442
570
    .map_err(|e| {
443
        let kind = ParseErrorKind::Expecting {
444
            value: "variable value".to_string(),
445
        };
446
        ParseError::new(e.pos, false, kind)
447
570
    })
448
}
449

            
450
#[cfg(test)]
451
mod tests {
452
    use super::*;
453
    use crate::ast::{LineTerminator, Number, Template, TemplateElement, Whitespace, I64};
454
    use crate::reader::Pos;
455
    use crate::typing::ToSource;
456

            
457
    #[test]
458
    fn test_option_insecure() {
459
        let mut reader = Reader::new("insecure: true");
460
        let option = parse(&mut reader).unwrap();
461
        assert_eq!(
462
            option,
463
            EntryOption {
464
                line_terminators: vec![],
465
                space0: Whitespace {
466
                    value: String::new(),
467
                    source_info: SourceInfo {
468
                        start: Pos { line: 1, column: 1 },
469
                        end: Pos { line: 1, column: 1 },
470
                    },
471
                },
472
                space1: Whitespace {
473
                    value: String::new(),
474
                    source_info: SourceInfo {
475
                        start: Pos { line: 1, column: 9 },
476
                        end: Pos { line: 1, column: 9 },
477
                    },
478
                },
479
                space2: Whitespace {
480
                    value: " ".to_string(),
481
                    source_info: SourceInfo {
482
                        start: Pos {
483
                            line: 1,
484
                            column: 10,
485
                        },
486
                        end: Pos {
487
                            line: 1,
488
                            column: 11,
489
                        },
490
                    },
491
                },
492
                kind: OptionKind::Insecure(BooleanOption::Literal(true)),
493
                line_terminator0: LineTerminator {
494
                    space0: Whitespace {
495
                        value: String::new(),
496
                        source_info: SourceInfo {
497
                            start: Pos {
498
                                line: 1,
499
                                column: 15,
500
                            },
501
                            end: Pos {
502
                                line: 1,
503
                                column: 15,
504
                            },
505
                        },
506
                    },
507
                    comment: None,
508
                    newline: Whitespace {
509
                        value: String::new(),
510
                        source_info: SourceInfo {
511
                            start: Pos {
512
                                line: 1,
513
                                column: 15,
514
                            },
515
                            end: Pos {
516
                                line: 1,
517
                                column: 15,
518
                            },
519
                        },
520
                    },
521
                },
522
            }
523
        );
524
    }
525

            
526
    #[test]
527
    fn test_option_insecure_error() {
528
        let mut reader = Reader::new("insecure: error");
529
        let error = parse(&mut reader).err().unwrap();
530
        assert!(!error.recoverable);
531
    }
532

            
533
    #[test]
534
    fn test_option_cacert() {
535
        let mut reader = Reader::new("cacert: /home/foo/cert.pem");
536
        let option = parse(&mut reader).unwrap();
537
        assert_eq!(
538
            option,
539
            EntryOption {
540
                line_terminators: vec![],
541
                space0: Whitespace {
542
                    value: String::new(),
543
                    source_info: SourceInfo {
544
                        start: Pos { line: 1, column: 1 },
545
                        end: Pos { line: 1, column: 1 },
546
                    },
547
                },
548
                space1: Whitespace {
549
                    value: String::new(),
550
                    source_info: SourceInfo {
551
                        start: Pos { line: 1, column: 7 },
552
                        end: Pos { line: 1, column: 7 },
553
                    },
554
                },
555
                space2: Whitespace {
556
                    value: " ".to_string(),
557
                    source_info: SourceInfo {
558
                        start: Pos { line: 1, column: 8 },
559
                        end: Pos { line: 1, column: 9 },
560
                    },
561
                },
562
                kind: OptionKind::CaCertificate(Template::new(
563
                    None,
564
                    vec![TemplateElement::String {
565
                        value: "/home/foo/cert.pem".to_string(),
566
                        source: "/home/foo/cert.pem".to_source()
567
                    }],
568
                    SourceInfo {
569
                        start: Pos { line: 1, column: 9 },
570
                        end: Pos {
571
                            line: 1,
572
                            column: 27,
573
                        },
574
                    }
575
                )),
576
                line_terminator0: LineTerminator {
577
                    space0: Whitespace {
578
                        value: String::new(),
579
                        source_info: SourceInfo {
580
                            start: Pos {
581
                                line: 1,
582
                                column: 27,
583
                            },
584
                            end: Pos {
585
                                line: 1,
586
                                column: 27,
587
                            },
588
                        },
589
                    },
590
                    comment: None,
591
                    newline: Whitespace {
592
                        value: String::new(),
593
                        source_info: SourceInfo {
594
                            start: Pos {
595
                                line: 1,
596
                                column: 27,
597
                            },
598
                            end: Pos {
599
                                line: 1,
600
                                column: 27,
601
                            },
602
                        },
603
                    },
604
                },
605
            }
606
        );
607
    }
608

            
609
    #[test]
610
    fn test_option_cacert_error() {
611
        let mut reader = Reader::new("cacert: ###");
612
        let error = parse(&mut reader).err().unwrap();
613
        assert!(!error.recoverable);
614
    }
615

            
616
    #[test]
617
    fn test_option_cert() {
618
        let mut reader = Reader::new("/etc/client-cert.pem #foo");
619

            
620
        assert_eq!(
621
            option_cert(&mut reader).unwrap(),
622
            OptionKind::ClientCert(Template::new(
623
                None,
624
                vec![TemplateElement::String {
625
                    value: "/etc/client-cert.pem".to_string(),
626
                    source: "/etc/client-cert.pem".to_source()
627
                }],
628
                SourceInfo {
629
                    start: Pos { line: 1, column: 1 },
630
                    end: Pos {
631
                        line: 1,
632
                        column: 21,
633
                    },
634
                },
635
            )),
636
        );
637
    }
638

            
639
    #[test]
640
    fn test_option_retry_error() {
641
        let mut reader = Reader::new("retry: ###");
642
        let error = parse(&mut reader).err().unwrap();
643
        assert!(!error.recoverable);
644
        assert_eq!(error.pos, Pos { line: 1, column: 8 });
645
        assert_eq!(
646
            error.kind,
647
            ParseErrorKind::Expecting {
648
                value: "integer >= -1".to_string()
649
            }
650
        );
651
    }
652

            
653
    #[test]
654
    fn test_variable_definition() {
655
        let mut reader = Reader::new("a=1");
656
        assert_eq!(
657
            variable_definition(&mut reader).unwrap(),
658
            VariableDefinition {
659
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4)),
660
                name: "a".to_string(),
661
                space0: Whitespace {
662
                    value: String::new(),
663
                    source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
664
                },
665
                space1: Whitespace {
666
                    value: String::new(),
667
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
668
                },
669
                value: VariableValue::Number(Number::Integer(I64::new(1, "1".to_source()))),
670
            }
671
        );
672
    }
673

            
674
    #[test]
675
    fn test_variable_value() {
676
        let mut reader = Reader::new("null");
677
        assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null);
678

            
679
        let mut reader = Reader::new("true");
680
        assert_eq!(
681
            variable_value(&mut reader).unwrap(),
682
            VariableValue::Bool(true)
683
        );
684

            
685
        let mut reader = Reader::new("1");
686
        assert_eq!(
687
            variable_value(&mut reader).unwrap(),
688
            VariableValue::Number(Number::Integer(I64::new(1, "1".to_source())))
689
        );
690

            
691
        let mut reader = Reader::new("toto");
692
        assert_eq!(
693
            variable_value(&mut reader).unwrap(),
694
            VariableValue::String(Template::new(
695
                None,
696
                vec![TemplateElement::String {
697
                    value: "toto".to_string(),
698
                    source: "toto".to_source(),
699
                }],
700
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
701
            ))
702
        );
703
        let mut reader = Reader::new("\"123\"");
704
        assert_eq!(
705
            variable_value(&mut reader).unwrap(),
706
            VariableValue::String(Template::new(
707
                Some('"'),
708
                vec![TemplateElement::String {
709
                    value: "123".to_string(),
710
                    source: "123".to_source(),
711
                }],
712
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6))
713
            ))
714
        );
715
    }
716
}