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
5585
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
37
5585
    let line_terminators = optional_line_terminators(reader)?;
38
5585
    let space0 = zero_or_more_spaces(reader)?;
39
5585
    let start = reader.cursor();
40
5585
    // We accept '_' even if there is no option name with this character. We do this to be able to
41
5585
    // enter the parsing of the option name and to have better error description (ex: 'max-redirs'
42
5585
    // vs 'max_redirs').
43
5585
    let option =
44
42697
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
45
5585
    let space1 = zero_or_more_spaces(reader)?;
46
5585
    try_literal(":", reader)?;
47
3785
    let space2 = zero_or_more_spaces(reader)?;
48
3785
    let kind = match option.as_str() {
49
3785
        "aws-sigv4" => option_aws_sigv4(reader)?,
50
3745
        "cacert" => option_cacert(reader)?,
51
3675
        "cert" => option_cert(reader)?,
52
3610
        "compressed" => option_compressed(reader)?,
53
3200
        "connect-to" => option_connect_to(reader)?,
54
3135
        "connect-timeout" => option_connect_timeout(reader)?,
55
3105
        "delay" => option_delay(reader)?,
56
2900
        "insecure" => option_insecure(reader)?,
57
2805
        "header" => option_header(reader)?,
58
2750
        "http1.0" => option_http_10(reader)?,
59
2630
        "http1.1" => option_http_11(reader)?,
60
2540
        "http2" => option_http_2(reader)?,
61
2490
        "http3" => option_http_3(reader)?,
62
2460
        "ipv4" => option_ipv4(reader)?,
63
2430
        "ipv6" => option_ipv6(reader)?,
64
2400
        "key" => option_key(reader)?,
65
2350
        "limit-rate" => option_limit_rate(reader)?,
66
2315
        "location" => option_follow_location(reader)?,
67
1890
        "location-trusted" => option_follow_location_trusted(reader)?,
68
1840
        "max-redirs" => option_max_redirect(reader)?,
69
1735
        "max-time" => option_max_time(reader)?,
70
1705
        "netrc" => option_netrc(reader)?,
71
1675
        "netrc-file" => option_netrc_file(reader)?,
72
1640
        "netrc-optional" => option_netrc_optional(reader)?,
73
1610
        "output" => option_output(reader)?,
74
1480
        "path-as-is" => option_path_as_is(reader)?,
75
1445
        "pinnedpubkey" => option_pinned_pub_key(reader)?,
76
1405
        "proxy" => option_proxy(reader)?,
77
1335
        "repeat" => option_repeat(reader)?,
78
1245
        "resolve" => option_resolve(reader)?,
79
1195
        "retry" => option_retry(reader)?,
80
1045
        "retry-interval" => option_retry_interval(reader)?,
81
935
        "skip" => option_skip(reader)?,
82
895
        "unix-socket" => option_unix_socket(reader)?,
83
865
        "user" => option_user(reader)?,
84
765
        "variable" => option_variable(reader)?,
85
190
        "verbose" => option_verbose(reader)?,
86
60
        "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
3750
    let line_terminator0 = line_terminator(reader)?;
97
3750
    Ok(EntryOption {
98
3750
        line_terminators,
99
3750
        space0,
100
3750
        space1,
101
3750
        space2,
102
3750
        kind,
103
3750
        line_terminator0,
104
3750
    })
105
}
106

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
297
345
fn count(reader: &mut Reader) -> ParseResult<Count> {
298
345
    let start = reader.cursor();
299
345
    let value = non_recover(integer, reader)?;
300
245
    if value.as_i64() == -1 {
301
40
        Ok(Count::Infinite)
302
205
    } else if value.as_i64() >= 0 {
303
200
        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
1645
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
313
1645
    let start = reader.cursor();
314
1645
    match boolean(reader) {
315
1360
        Ok(v) => Ok(BooleanOption::Literal(v)),
316
        Err(_) => {
317
285
            reader.seek(start);
318
286
            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
286
            })?;
324
280
            Ok(BooleanOption::Placeholder(exp))
325
        }
326
    }
327
}
328

            
329
35
fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
330
35
    let start = reader.cursor();
331
35
    match natural(reader) {
332
20
        Ok(v) => Ok(NaturalOption::Literal(v)),
333
        Err(_) => {
334
15
            reader.seek(start);
335
15
            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
15
            })?;
341
15
            Ok(NaturalOption::Placeholder(placeholder))
342
        }
343
    }
344
}
345

            
346
345
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
347
345
    let start = reader.cursor();
348
345
    match count(reader) {
349
240
        Ok(v) => Ok(CountOption::Literal(v)),
350
        Err(_) => {
351
105
            reader.seek(start);
352
106
            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
106
            })?;
358
100
            Ok(CountOption::Placeholder(placeholder))
359
        }
360
    }
361
}
362

            
363
375
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
364
375
    let start = reader.cursor();
365
375
    match duration(reader) {
366
305
        Ok(v) => Ok(DurationOption::Literal(v)),
367
70
        Err(e) => {
368
70
            if e.recoverable {
369
65
                reader.seek(start);
370
65
                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
65
                })?;
376
65
                Ok(DurationOption::Placeholder(placeholder))
377
            } else {
378
5
                Err(e)
379
            }
380
        }
381
    }
382
}
383

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

            
405
575
fn variable_name(reader: &mut Reader) -> ParseResult<String> {
406
575
    let start = reader.cursor();
407
3260
    let name = reader.read_while(|c| c.is_alphanumeric() || c == '_' || c == '-');
408
575
    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
575
    } 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
570
    Ok(name)
420
}
421

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