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
6415
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
37
6415
    let line_terminators = optional_line_terminators(reader)?;
38
6415
    let space0 = zero_or_more_spaces(reader)?;
39
6415
    let start = reader.cursor();
40
    // We accept '_' even if there is no option name with this character. We do this to be able to
41
    // enter the parsing of the option name and to have better error description (ex: 'max-redirs'
42
    // vs 'max_redirs').
43
6415
    let option =
44
48618
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
45
6415
    let space1 = zero_or_more_spaces(reader)?;
46
6415
    try_literal(":", reader)?;
47
4440
    let space2 = zero_or_more_spaces(reader)?;
48
4440
    let kind = match option.as_str() {
49
4440
        "aws-sigv4" => option_aws_sigv4(reader)?,
50
4390
        "cacert" => option_cacert(reader)?,
51
4310
        "cert" => option_cert(reader)?,
52
4230
        "compressed" => option_compressed(reader)?,
53
3810
        "connect-to" => option_connect_to(reader)?,
54
3735
        "connect-timeout" => option_connect_timeout(reader)?,
55
3695
        "delay" => option_delay(reader)?,
56
3360
        "insecure" => option_insecure(reader)?,
57
3250
        "header" => option_header(reader)?,
58
3185
        "http1.0" => option_http_10(reader)?,
59
3055
        "http1.1" => option_http_11(reader)?,
60
2955
        "http2" => option_http_2(reader)?,
61
2895
        "http3" => option_http_3(reader)?,
62
2855
        "ipv4" => option_ipv4(reader)?,
63
2815
        "ipv6" => option_ipv6(reader)?,
64
2775
        "key" => option_key(reader)?,
65
2715
        "limit-rate" => option_limit_rate(reader)?,
66
2670
        "location" => option_follow_location(reader)?,
67
2230
        "location-trusted" => option_follow_location_trusted(reader)?,
68
2170
        "max-redirs" => option_max_redirect(reader)?,
69
2055
        "max-time" => option_max_time(reader)?,
70
2015
        "netrc" => option_netrc(reader)?,
71
1975
        "netrc-file" => option_netrc_file(reader)?,
72
1930
        "netrc-optional" => option_netrc_optional(reader)?,
73
1890
        "output" => option_output(reader)?,
74
1750
        "path-as-is" => option_path_as_is(reader)?,
75
1705
        "pinnedpubkey" => option_pinned_pub_key(reader)?,
76
1655
        "proxy" => option_proxy(reader)?,
77
1575
        "repeat" => option_repeat(reader)?,
78
1470
        "resolve" => option_resolve(reader)?,
79
1410
        "retry" => option_retry(reader)?,
80
1235
        "retry-interval" => option_retry_interval(reader)?,
81
1105
        "skip" => option_skip(reader)?,
82
1055
        "unix-socket" => option_unix_socket(reader)?,
83
1015
        "user" => option_user(reader)?,
84
905
        "variable" => option_variable(reader)?,
85
225
        "verbose" => option_verbose(reader)?,
86
70
        "very-verbose" => option_very_verbose(reader)?,
87
        _ => {
88
10
            return Err(ParseError::new(
89
10
                start.pos,
90
10
                false,
91
10
                ParseErrorKind::InvalidOption(option.to_string()),
92
10
            ))
93
        }
94
    };
95

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
237
50
fn option_pinned_pub_key(reader: &mut Reader) -> ParseResult<OptionKind> {
238
50
    let value = unquoted_template(reader)?;
239
50
    Ok(OptionKind::PinnedPublicKey(value))
240
}
241

            
242
80
fn option_proxy(reader: &mut Reader) -> ParseResult<OptionKind> {
243
80
    let value = unquoted_template(reader)?;
244
80
    Ok(OptionKind::Proxy(value))
245
}
246

            
247
105
fn option_repeat(reader: &mut Reader) -> ParseResult<OptionKind> {
248
105
    let value = non_recover(count_option, reader)?;
249
105
    Ok(OptionKind::Repeat(value))
250
}
251

            
252
60
fn option_resolve(reader: &mut Reader) -> ParseResult<OptionKind> {
253
60
    let value = unquoted_template(reader)?;
254
60
    Ok(OptionKind::Resolve(value))
255
}
256

            
257
175
fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
258
175
    let value = non_recover(count_option, reader)?;
259
170
    Ok(OptionKind::Retry(value))
260
}
261

            
262
130
fn option_retry_interval(reader: &mut Reader) -> ParseResult<OptionKind> {
263
130
    let value = non_recover(duration_option, reader)?;
264
130
    Ok(OptionKind::RetryInterval(value))
265
}
266

            
267
50
fn option_skip(reader: &mut Reader) -> ParseResult<OptionKind> {
268
50
    let value = non_recover(boolean_option, reader)?;
269
50
    Ok(OptionKind::Skip(value))
270
}
271

            
272
110
fn option_user(reader: &mut Reader) -> ParseResult<OptionKind> {
273
110
    let value = unquoted_template(reader)?;
274
110
    Ok(OptionKind::User(value))
275
}
276

            
277
40
fn option_unix_socket(reader: &mut Reader) -> ParseResult<OptionKind> {
278
40
    let value = unquoted_template(reader)?;
279
40
    Ok(OptionKind::UnixSocket(value))
280
}
281

            
282
680
fn option_variable(reader: &mut Reader) -> ParseResult<OptionKind> {
283
680
    let value = variable_definition(reader)?;
284
675
    Ok(OptionKind::Variable(value))
285
}
286

            
287
155
fn option_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
288
155
    let value = non_recover(boolean_option, reader)?;
289
155
    Ok(OptionKind::Verbose(value))
290
}
291

            
292
60
fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
293
60
    let value = non_recover(boolean_option, reader)?;
294
60
    Ok(OptionKind::VeryVerbose(value))
295
}
296

            
297
395
fn count(reader: &mut Reader) -> ParseResult<Count> {
298
395
    let start = reader.cursor();
299
395
    let value = non_recover(integer, reader)?;
300
280
    if value.as_i64() == -1 {
301
50
        Ok(Count::Infinite)
302
230
    } else if value.as_i64() >= 0 {
303
225
        Ok(Count::Finite(value.as_i64() as usize))
304
    } else {
305
5
        let kind = ParseErrorKind::Expecting {
306
5
            value: "Expecting a count value".to_string(),
307
5
        };
308
5
        Err(ParseError::new(start.pos, false, kind))
309
    }
310
}
311

            
312
1830
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
313
1830
    let start = reader.cursor();
314
1830
    match boolean(reader) {
315
1465
        Ok(v) => Ok(BooleanOption::Literal(v)),
316
        Err(_) => {
317
365
            reader.seek(start);
318
366
            let exp = placeholder::parse(reader).map_err(|e| {
319
5
                let kind = ParseErrorKind::Expecting {
320
5
                    value: "true|false".to_string(),
321
5
                };
322
5
                ParseError::new(e.pos, false, kind)
323
6
            })?;
324
360
            Ok(BooleanOption::Placeholder(exp))
325
        }
326
    }
327
}
328

            
329
45
fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
330
45
    let start = reader.cursor();
331
45
    match natural(reader) {
332
25
        Ok(v) => Ok(NaturalOption::Literal(v)),
333
        Err(_) => {
334
20
            reader.seek(start);
335
20
            let placeholder = placeholder::parse(reader).map_err(|e| {
336
                let kind = ParseErrorKind::Expecting {
337
                    value: "integer >= 0".to_string(),
338
                };
339
                ParseError::new(e.pos, false, kind)
340
            })?;
341
20
            Ok(NaturalOption::Placeholder(placeholder))
342
        }
343
    }
344
}
345

            
346
395
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
347
395
    let start = reader.cursor();
348
395
    match count(reader) {
349
275
        Ok(v) => Ok(CountOption::Literal(v)),
350
        Err(_) => {
351
120
            reader.seek(start);
352
121
            let placeholder = placeholder::parse(reader).map_err(|e| {
353
5
                let kind = ParseErrorKind::Expecting {
354
5
                    value: "integer >= -1".to_string(),
355
5
                };
356
5
                ParseError::new(e.pos, false, kind)
357
6
            })?;
358
115
            Ok(CountOption::Placeholder(placeholder))
359
        }
360
    }
361
}
362

            
363
545
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
364
545
    let start = reader.cursor();
365
545
    match duration(reader) {
366
455
        Ok(v) => Ok(DurationOption::Literal(v)),
367
90
        Err(e) => {
368
90
            if e.recoverable {
369
85
                reader.seek(start);
370
85
                let placeholder = placeholder::parse(reader).map_err(|e| {
371
                    let kind = ParseErrorKind::Expecting {
372
                        value: "integer".to_string(),
373
                    };
374
                    ParseError::new(e.pos, false, kind)
375
                })?;
376
85
                Ok(DurationOption::Placeholder(placeholder))
377
            } else {
378
5
                Err(e)
379
            }
380
        }
381
    }
382
}
383

            
384
680
fn variable_definition(reader: &mut Reader) -> ParseResult<VariableDefinition> {
385
680
    let start = reader.cursor();
386
680
    let name = variable_name(reader)?;
387
675
    let space0 = zero_or_more_spaces(reader)?;
388
675
    literal("=", reader)?;
389
675
    let space1 = zero_or_more_spaces(reader)?;
390
675
    let value = variable_value(reader)?;
391
675
    let end = reader.cursor();
392
675
    let source_info = SourceInfo {
393
675
        start: start.pos,
394
675
        end: end.pos,
395
675
    };
396
675
    Ok(VariableDefinition {
397
675
        source_info,
398
675
        name,
399
675
        space0,
400
675
        space1,
401
675
        value,
402
675
    })
403
}
404

            
405
680
fn variable_name(reader: &mut Reader) -> ParseResult<String> {
406
680
    let start = reader.cursor();
407
4241
    let name = reader.read_while(|c| c.is_alphanumeric() || c == '_' || c == '-');
408
680
    if name.is_empty() {
409
        let kind = ParseErrorKind::Expecting {
410
            value: "variable name".to_string(),
411
        };
412
        return Err(ParseError::new(start.pos, false, kind));
413
680
    } else if is_variable_reserved(&name) {
414
5
        let kind = ParseErrorKind::Variable(format!(
415
5
            "conflicts with the {name} function, use a different name"
416
5
        ));
417
5
        return Err(ParseError::new(start.pos, false, kind));
418
    }
419
675
    Ok(name)
420
}
421

            
422
675
fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
423
675
    choice(
424
        &[
425
675
            |p1| match null(p1) {
426
20
                Ok(()) => Ok(VariableValue::Null),
427
655
                Err(e) => Err(e),
428
675
            },
429
655
            |p1| match boolean(p1) {
430
25
                Ok(value) => Ok(VariableValue::Bool(value)),
431
630
                Err(e) => Err(e),
432
655
            },
433
630
            |p1| match number(p1) {
434
245
                Ok(value) => Ok(VariableValue::Number(value)),
435
385
                Err(e) => Err(e),
436
630
            },
437
385
            |p1| match quoted_template(p1) {
438
                Ok(value) => Ok(VariableValue::String(value)),
439
385
                Err(e) => Err(e),
440
385
            },
441
385
            |p1| match unquoted_template(p1) {
442
385
                Ok(value) => Ok(VariableValue::String(value)),
443
                Err(e) => Err(e),
444
385
            },
445
        ],
446
675
        reader,
447
    )
448
675
    .map_err(|e| {
449
        let kind = ParseErrorKind::Expecting {
450
            value: "variable value".to_string(),
451
        };
452
        ParseError::new(e.pos, false, kind)
453
    })
454
}
455

            
456
#[cfg(test)]
457
mod tests {
458
    use super::*;
459
    use crate::ast::{LineTerminator, Number, Template, TemplateElement, Whitespace, I64};
460
    use crate::reader::Pos;
461
    use crate::typing::ToSource;
462

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

            
532
    #[test]
533
    fn test_option_insecure_error() {
534
        let mut reader = Reader::new("insecure: error");
535
        let error = parse(&mut reader).err().unwrap();
536
        assert!(!error.recoverable);
537
    }
538

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

            
615
    #[test]
616
    fn test_option_cacert_error() {
617
        let mut reader = Reader::new("cacert: ###");
618
        let error = parse(&mut reader).err().unwrap();
619
        assert!(!error.recoverable);
620
    }
621

            
622
    #[test]
623
    fn test_option_cert() {
624
        let mut reader = Reader::new("/etc/client-cert.pem #foo");
625

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

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

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

            
680
    #[test]
681
    fn test_variable_value() {
682
        let mut reader = Reader::new("null");
683
        assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null);
684

            
685
        let mut reader = Reader::new("true");
686
        assert_eq!(
687
            variable_value(&mut reader).unwrap(),
688
            VariableValue::Bool(true)
689
        );
690

            
691
        let mut reader = Reader::new("1");
692
        assert_eq!(
693
            variable_value(&mut reader).unwrap(),
694
            VariableValue::Number(Number::Integer(I64::new(1, "1".to_source())))
695
        );
696

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