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, VerbosityOption,
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::types::Count;
34

            
35
/// Parse an option in an `[Options]` section.
36
6815
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
37
6815
    let line_terminators = optional_line_terminators(reader)?;
38
6815
    let space0 = zero_or_more_spaces(reader)?;
39
6815
    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
6815
    let option =
44
51223
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
45
6815
    let space1 = zero_or_more_spaces(reader)?;
46
6815
    try_literal(":", reader)?;
47
4730
    let space2 = zero_or_more_spaces(reader)?;
48
4730
    let kind = match option.as_str() {
49
4730
        "aws-sigv4" => option_aws_sigv4(reader)?,
50
4680
        "cacert" => option_cacert(reader)?,
51
4600
        "cert" => option_cert(reader)?,
52
4520
        "compressed" => option_compressed(reader)?,
53
4100
        "connect-to" => option_connect_to(reader)?,
54
4025
        "connect-timeout" => option_connect_timeout(reader)?,
55
3985
        "delay" => option_delay(reader)?,
56
3610
        "insecure" => option_insecure(reader)?,
57
3500
        "header" => option_header(reader)?,
58
3435
        "http1.0" => option_http_10(reader)?,
59
3305
        "http1.1" => option_http_11(reader)?,
60
3205
        "http2" => option_http_2(reader)?,
61
3145
        "http3" => option_http_3(reader)?,
62
3105
        "ipv4" => option_ipv4(reader)?,
63
3065
        "ipv6" => option_ipv6(reader)?,
64
3025
        "key" => option_key(reader)?,
65
2965
        "limit-rate" => option_limit_rate(reader)?,
66
2920
        "location" => option_follow_location(reader)?,
67
2455
        "location-trusted" => option_follow_location_trusted(reader)?,
68
2395
        "max-redirs" => option_max_redirect(reader)?,
69
2280
        "max-time" => option_max_time(reader)?,
70
2240
        "negotiate" => option_negotiate(reader)?,
71
2190
        "netrc" => option_netrc(reader)?,
72
2150
        "netrc-file" => option_netrc_file(reader)?,
73
2105
        "netrc-optional" => option_netrc_optional(reader)?,
74
2065
        "ntlm" => option_ntlm(reader)?,
75
2010
        "output" => option_output(reader)?,
76
1870
        "path-as-is" => option_path_as_is(reader)?,
77
1825
        "pinnedpubkey" => option_pinned_pub_key(reader)?,
78
1775
        "proxy" => option_proxy(reader)?,
79
1695
        "repeat" => option_repeat(reader)?,
80
1580
        "resolve" => option_resolve(reader)?,
81
1520
        "retry" => option_retry(reader)?,
82
1345
        "retry-interval" => option_retry_interval(reader)?,
83
1215
        "skip" => option_skip(reader)?,
84
1165
        "unix-socket" => option_unix_socket(reader)?,
85
1125
        "user" => option_user(reader)?,
86
990
        "variable" => option_variable(reader)?,
87
310
        "verbose" => option_verbose(reader)?,
88
130
        "verbosity" => option_verbosity(reader)?,
89
70
        "very-verbose" => option_very_verbose(reader)?,
90
        _ => {
91
10
            return Err(ParseError::new(
92
10
                start.pos,
93
10
                false,
94
10
                ParseErrorKind::InvalidOption(option.to_string()),
95
10
            ))
96
        }
97
    };
98

            
99
4690
    let line_terminator0 = line_terminator(reader)?;
100
4690
    Ok(EntryOption {
101
4690
        line_terminators,
102
4690
        space0,
103
4690
        space1,
104
4690
        space2,
105
4690
        kind,
106
4690
        line_terminator0,
107
4690
    })
108
}
109

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

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

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

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

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

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

            
140
375
fn option_delay(reader: &mut Reader) -> ParseResult<OptionKind> {
141
375
    let value = duration_option(reader)?;
142
370
    Ok(OptionKind::Delay(value))
143
}
144

            
145
465
fn option_follow_location(reader: &mut Reader) -> ParseResult<OptionKind> {
146
465
    let value = non_recover(boolean_option, reader)?;
147
465
    Ok(OptionKind::FollowLocation(value))
148
}
149

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

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

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

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

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

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

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

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

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

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

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

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

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

            
215
50
fn option_negotiate(reader: &mut Reader) -> ParseResult<OptionKind> {
216
50
    let value = boolean_option(reader)?;
217
50
    Ok(OptionKind::Negotiate(value))
218
}
219

            
220
40
fn option_netrc(reader: &mut Reader) -> ParseResult<OptionKind> {
221
40
    let value = non_recover(boolean_option, reader)?;
222
40
    Ok(OptionKind::NetRc(value))
223
}
224

            
225
45
fn option_netrc_file(reader: &mut Reader) -> ParseResult<OptionKind> {
226
45
    let value = unquoted_template(reader)?;
227
45
    Ok(OptionKind::NetRcFile(value))
228
}
229

            
230
40
fn option_netrc_optional(reader: &mut Reader) -> ParseResult<OptionKind> {
231
40
    let value = non_recover(boolean_option, reader)?;
232
40
    Ok(OptionKind::NetRcOptional(value))
233
}
234

            
235
55
fn option_ntlm(reader: &mut Reader) -> ParseResult<OptionKind> {
236
55
    let value = non_recover(boolean_option, reader)?;
237
55
    Ok(OptionKind::Ntlm(value))
238
}
239

            
240
140
fn option_output(reader: &mut Reader) -> ParseResult<OptionKind> {
241
140
    let value = filename::parse(reader)?;
242
140
    Ok(OptionKind::Output(value))
243
}
244

            
245
45
fn option_path_as_is(reader: &mut Reader) -> ParseResult<OptionKind> {
246
45
    let value = non_recover(boolean_option, reader)?;
247
45
    Ok(OptionKind::PathAsIs(value))
248
}
249

            
250
50
fn option_pinned_pub_key(reader: &mut Reader) -> ParseResult<OptionKind> {
251
50
    let value = unquoted_template(reader)?;
252
50
    Ok(OptionKind::PinnedPublicKey(value))
253
}
254

            
255
80
fn option_proxy(reader: &mut Reader) -> ParseResult<OptionKind> {
256
80
    let value = unquoted_template(reader)?;
257
80
    Ok(OptionKind::Proxy(value))
258
}
259

            
260
115
fn option_repeat(reader: &mut Reader) -> ParseResult<OptionKind> {
261
115
    let value = non_recover(count_option, reader)?;
262
115
    Ok(OptionKind::Repeat(value))
263
}
264

            
265
60
fn option_resolve(reader: &mut Reader) -> ParseResult<OptionKind> {
266
60
    let value = unquoted_template(reader)?;
267
60
    Ok(OptionKind::Resolve(value))
268
}
269

            
270
175
fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
271
175
    let value = non_recover(count_option, reader)?;
272
170
    Ok(OptionKind::Retry(value))
273
}
274

            
275
130
fn option_retry_interval(reader: &mut Reader) -> ParseResult<OptionKind> {
276
130
    let value = non_recover(duration_option, reader)?;
277
130
    Ok(OptionKind::RetryInterval(value))
278
}
279

            
280
50
fn option_skip(reader: &mut Reader) -> ParseResult<OptionKind> {
281
50
    let value = non_recover(boolean_option, reader)?;
282
50
    Ok(OptionKind::Skip(value))
283
}
284

            
285
135
fn option_user(reader: &mut Reader) -> ParseResult<OptionKind> {
286
135
    let value = unquoted_template(reader)?;
287
135
    Ok(OptionKind::User(value))
288
}
289

            
290
40
fn option_unix_socket(reader: &mut Reader) -> ParseResult<OptionKind> {
291
40
    let value = unquoted_template(reader)?;
292
40
    Ok(OptionKind::UnixSocket(value))
293
}
294

            
295
680
fn option_variable(reader: &mut Reader) -> ParseResult<OptionKind> {
296
680
    let value = variable_definition(reader)?;
297
675
    Ok(OptionKind::Variable(value))
298
}
299

            
300
180
fn option_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
301
180
    let value = non_recover(boolean_option, reader)?;
302
180
    Ok(OptionKind::Verbose(value))
303
}
304

            
305
60
fn option_verbosity(reader: &mut Reader) -> ParseResult<OptionKind> {
306
60
    let start = reader.cursor();
307
372
    let name = reader.read_while(|c| c.is_ascii_alphabetic());
308
60
    match name.as_str() {
309
60
        "brief" => Ok(OptionKind::Verbosity(VerbosityOption::Brief)),
310
15
        "verbose" => Ok(OptionKind::Verbosity(VerbosityOption::Verbose)),
311
10
        "debug" => Ok(OptionKind::Verbosity(VerbosityOption::Debug)),
312
        _ => {
313
5
            reader.seek(start);
314
5
            let kind = ParseErrorKind::Expecting {
315
5
                value: "brief|verbose|debug".to_string(),
316
5
            };
317
5
            Err(ParseError::new(start.pos, false, kind))
318
        }
319
    }
320
}
321

            
322
60
fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
323
60
    let value = non_recover(boolean_option, reader)?;
324
60
    Ok(OptionKind::VeryVerbose(value))
325
}
326

            
327
405
fn count(reader: &mut Reader) -> ParseResult<Count> {
328
405
    let start = reader.cursor();
329
405
    let value = non_recover(integer, reader)?;
330
290
    if value.as_i64() == -1 {
331
50
        Ok(Count::Infinite)
332
240
    } else if value.as_i64() >= 0 {
333
235
        Ok(Count::Finite(value.as_i64() as usize))
334
    } else {
335
5
        let kind = ParseErrorKind::Expecting {
336
5
            value: "Expecting a count value".to_string(),
337
5
        };
338
5
        Err(ParseError::new(start.pos, false, kind))
339
    }
340
}
341

            
342
1985
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
343
1985
    let start = reader.cursor();
344
1985
    match boolean(reader) {
345
1580
        Ok(v) => Ok(BooleanOption::Literal(v)),
346
        Err(_) => {
347
405
            reader.seek(start);
348
406
            let exp = placeholder::parse(reader).map_err(|e| {
349
5
                let kind = ParseErrorKind::Expecting {
350
5
                    value: "true|false".to_string(),
351
5
                };
352
5
                ParseError::new(e.pos, false, kind)
353
6
            })?;
354
400
            Ok(BooleanOption::Placeholder(exp))
355
        }
356
    }
357
}
358

            
359
45
fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
360
45
    let start = reader.cursor();
361
45
    match natural(reader) {
362
25
        Ok(v) => Ok(NaturalOption::Literal(v)),
363
        Err(_) => {
364
20
            reader.seek(start);
365
20
            let placeholder = placeholder::parse(reader).map_err(|e| {
366
                let kind = ParseErrorKind::Expecting {
367
                    value: "integer >= 0".to_string(),
368
                };
369
                ParseError::new(e.pos, false, kind)
370
            })?;
371
20
            Ok(NaturalOption::Placeholder(placeholder))
372
        }
373
    }
374
}
375

            
376
405
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
377
405
    let start = reader.cursor();
378
405
    match count(reader) {
379
285
        Ok(v) => Ok(CountOption::Literal(v)),
380
        Err(_) => {
381
120
            reader.seek(start);
382
121
            let placeholder = placeholder::parse(reader).map_err(|e| {
383
5
                let kind = ParseErrorKind::Expecting {
384
5
                    value: "integer >= -1".to_string(),
385
5
                };
386
5
                ParseError::new(e.pos, false, kind)
387
6
            })?;
388
115
            Ok(CountOption::Placeholder(placeholder))
389
        }
390
    }
391
}
392

            
393
585
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
394
585
    let start = reader.cursor();
395
585
    match duration(reader) {
396
495
        Ok(v) => Ok(DurationOption::Literal(v)),
397
90
        Err(e) => {
398
90
            if e.recoverable {
399
85
                reader.seek(start);
400
85
                let placeholder = placeholder::parse(reader).map_err(|e| {
401
                    let kind = ParseErrorKind::Expecting {
402
                        value: "integer".to_string(),
403
                    };
404
                    ParseError::new(e.pos, false, kind)
405
                })?;
406
85
                Ok(DurationOption::Placeholder(placeholder))
407
            } else {
408
5
                Err(e)
409
            }
410
        }
411
    }
412
}
413

            
414
680
fn variable_definition(reader: &mut Reader) -> ParseResult<VariableDefinition> {
415
680
    let start = reader.cursor();
416
680
    let name = variable_name(reader)?;
417
675
    let space0 = zero_or_more_spaces(reader)?;
418
675
    literal("=", reader)?;
419
675
    let space1 = zero_or_more_spaces(reader)?;
420
675
    let value = variable_value(reader)?;
421
675
    let end = reader.cursor();
422
675
    let source_info = SourceInfo {
423
675
        start: start.pos,
424
675
        end: end.pos,
425
675
    };
426
675
    Ok(VariableDefinition {
427
675
        source_info,
428
675
        name,
429
675
        space0,
430
675
        space1,
431
675
        value,
432
675
    })
433
}
434

            
435
680
fn variable_name(reader: &mut Reader) -> ParseResult<String> {
436
680
    let start = reader.cursor();
437
4241
    let name = reader.read_while(|c| c.is_alphanumeric() || c == '_' || c == '-');
438
680
    if name.is_empty() {
439
        let kind = ParseErrorKind::Expecting {
440
            value: "variable name".to_string(),
441
        };
442
        return Err(ParseError::new(start.pos, false, kind));
443
680
    } else if is_variable_reserved(&name) {
444
5
        let kind = ParseErrorKind::Variable(format!(
445
5
            "conflicts with the {name} function, use a different name"
446
5
        ));
447
5
        return Err(ParseError::new(start.pos, false, kind));
448
    }
449
675
    Ok(name)
450
}
451

            
452
675
fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
453
675
    choice(
454
        &[
455
675
            |p1| match null(p1) {
456
20
                Ok(()) => Ok(VariableValue::Null),
457
655
                Err(e) => Err(e),
458
675
            },
459
655
            |p1| match boolean(p1) {
460
25
                Ok(value) => Ok(VariableValue::Bool(value)),
461
630
                Err(e) => Err(e),
462
655
            },
463
630
            |p1| match number(p1) {
464
245
                Ok(value) => Ok(VariableValue::Number(value)),
465
385
                Err(e) => Err(e),
466
630
            },
467
385
            |p1| match quoted_template(p1) {
468
                Ok(value) => Ok(VariableValue::String(value)),
469
385
                Err(e) => Err(e),
470
385
            },
471
385
            |p1| match unquoted_template(p1) {
472
385
                Ok(value) => Ok(VariableValue::String(value)),
473
                Err(e) => Err(e),
474
385
            },
475
        ],
476
675
        reader,
477
    )
478
675
    .map_err(|e| {
479
        let kind = ParseErrorKind::Expecting {
480
            value: "variable value".to_string(),
481
        };
482
        ParseError::new(e.pos, false, kind)
483
    })
484
}
485

            
486
#[cfg(test)]
487
mod tests {
488
    use super::*;
489
    use crate::ast::{LineTerminator, Number, Template, TemplateElement, Whitespace, I64};
490
    use crate::reader::Pos;
491
    use crate::types::ToSource;
492

            
493
    #[test]
494
    fn test_option_insecure() {
495
        let mut reader = Reader::new("insecure: true");
496
        let option = parse(&mut reader).unwrap();
497
        assert_eq!(
498
            option,
499
            EntryOption {
500
                line_terminators: vec![],
501
                space0: Whitespace {
502
                    value: String::new(),
503
                    source_info: SourceInfo {
504
                        start: Pos { line: 1, column: 1 },
505
                        end: Pos { line: 1, column: 1 },
506
                    },
507
                },
508
                space1: Whitespace {
509
                    value: String::new(),
510
                    source_info: SourceInfo {
511
                        start: Pos { line: 1, column: 9 },
512
                        end: Pos { line: 1, column: 9 },
513
                    },
514
                },
515
                space2: Whitespace {
516
                    value: " ".to_string(),
517
                    source_info: SourceInfo {
518
                        start: Pos {
519
                            line: 1,
520
                            column: 10,
521
                        },
522
                        end: Pos {
523
                            line: 1,
524
                            column: 11,
525
                        },
526
                    },
527
                },
528
                kind: OptionKind::Insecure(BooleanOption::Literal(true)),
529
                line_terminator0: LineTerminator {
530
                    space0: Whitespace {
531
                        value: String::new(),
532
                        source_info: SourceInfo {
533
                            start: Pos {
534
                                line: 1,
535
                                column: 15,
536
                            },
537
                            end: Pos {
538
                                line: 1,
539
                                column: 15,
540
                            },
541
                        },
542
                    },
543
                    comment: None,
544
                    newline: Whitespace {
545
                        value: String::new(),
546
                        source_info: SourceInfo {
547
                            start: Pos {
548
                                line: 1,
549
                                column: 15,
550
                            },
551
                            end: Pos {
552
                                line: 1,
553
                                column: 15,
554
                            },
555
                        },
556
                    },
557
                },
558
            }
559
        );
560
    }
561

            
562
    #[test]
563
    fn test_option_insecure_error() {
564
        let mut reader = Reader::new("insecure: error");
565
        let error = parse(&mut reader).err().unwrap();
566
        assert!(!error.recoverable);
567
    }
568

            
569
    #[test]
570
    fn test_option_cacert() {
571
        let mut reader = Reader::new("cacert: /home/foo/cert.pem");
572
        let option = parse(&mut reader).unwrap();
573
        assert_eq!(
574
            option,
575
            EntryOption {
576
                line_terminators: vec![],
577
                space0: Whitespace {
578
                    value: String::new(),
579
                    source_info: SourceInfo {
580
                        start: Pos { line: 1, column: 1 },
581
                        end: Pos { line: 1, column: 1 },
582
                    },
583
                },
584
                space1: Whitespace {
585
                    value: String::new(),
586
                    source_info: SourceInfo {
587
                        start: Pos { line: 1, column: 7 },
588
                        end: Pos { line: 1, column: 7 },
589
                    },
590
                },
591
                space2: Whitespace {
592
                    value: " ".to_string(),
593
                    source_info: SourceInfo {
594
                        start: Pos { line: 1, column: 8 },
595
                        end: Pos { line: 1, column: 9 },
596
                    },
597
                },
598
                kind: OptionKind::CaCertificate(Template::new(
599
                    None,
600
                    vec![TemplateElement::String {
601
                        value: "/home/foo/cert.pem".to_string(),
602
                        source: "/home/foo/cert.pem".to_source()
603
                    }],
604
                    SourceInfo {
605
                        start: Pos { line: 1, column: 9 },
606
                        end: Pos {
607
                            line: 1,
608
                            column: 27,
609
                        },
610
                    }
611
                )),
612
                line_terminator0: LineTerminator {
613
                    space0: Whitespace {
614
                        value: String::new(),
615
                        source_info: SourceInfo {
616
                            start: Pos {
617
                                line: 1,
618
                                column: 27,
619
                            },
620
                            end: Pos {
621
                                line: 1,
622
                                column: 27,
623
                            },
624
                        },
625
                    },
626
                    comment: None,
627
                    newline: Whitespace {
628
                        value: String::new(),
629
                        source_info: SourceInfo {
630
                            start: Pos {
631
                                line: 1,
632
                                column: 27,
633
                            },
634
                            end: Pos {
635
                                line: 1,
636
                                column: 27,
637
                            },
638
                        },
639
                    },
640
                },
641
            }
642
        );
643
    }
644

            
645
    #[test]
646
    fn test_option_cacert_error() {
647
        let mut reader = Reader::new("cacert: ###");
648
        let error = parse(&mut reader).err().unwrap();
649
        assert!(!error.recoverable);
650
    }
651

            
652
    #[test]
653
    fn test_option_cert() {
654
        let mut reader = Reader::new("/etc/client-cert.pem #foo");
655

            
656
        assert_eq!(
657
            option_cert(&mut reader).unwrap(),
658
            OptionKind::ClientCert(Template::new(
659
                None,
660
                vec![TemplateElement::String {
661
                    value: "/etc/client-cert.pem".to_string(),
662
                    source: "/etc/client-cert.pem".to_source()
663
                }],
664
                SourceInfo {
665
                    start: Pos { line: 1, column: 1 },
666
                    end: Pos {
667
                        line: 1,
668
                        column: 21,
669
                    },
670
                },
671
            )),
672
        );
673
    }
674

            
675
    #[test]
676
    fn test_option_retry_error() {
677
        let mut reader = Reader::new("retry: ###");
678
        let error = parse(&mut reader).err().unwrap();
679
        assert!(!error.recoverable);
680
        assert_eq!(error.pos, Pos { line: 1, column: 8 });
681
        assert_eq!(
682
            error.kind,
683
            ParseErrorKind::Expecting {
684
                value: "integer >= -1".to_string()
685
            }
686
        );
687
    }
688

            
689
    #[test]
690
    fn test_variable_definition() {
691
        let mut reader = Reader::new("a=1");
692
        assert_eq!(
693
            variable_definition(&mut reader).unwrap(),
694
            VariableDefinition {
695
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4)),
696
                name: "a".to_string(),
697
                space0: Whitespace {
698
                    value: String::new(),
699
                    source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
700
                },
701
                space1: Whitespace {
702
                    value: String::new(),
703
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
704
                },
705
                value: VariableValue::Number(Number::Integer(I64::new(1, "1".to_source()))),
706
            }
707
        );
708
    }
709

            
710
    #[test]
711
    fn test_variable_value() {
712
        let mut reader = Reader::new("null");
713
        assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null);
714

            
715
        let mut reader = Reader::new("true");
716
        assert_eq!(
717
            variable_value(&mut reader).unwrap(),
718
            VariableValue::Bool(true)
719
        );
720

            
721
        let mut reader = Reader::new("1");
722
        assert_eq!(
723
            variable_value(&mut reader).unwrap(),
724
            VariableValue::Number(Number::Integer(I64::new(1, "1".to_source())))
725
        );
726

            
727
        let mut reader = Reader::new("toto");
728
        assert_eq!(
729
            variable_value(&mut reader).unwrap(),
730
            VariableValue::String(Template::new(
731
                None,
732
                vec![TemplateElement::String {
733
                    value: "toto".to_string(),
734
                    source: "toto".to_source(),
735
                }],
736
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
737
            ))
738
        );
739
        let mut reader = Reader::new("\"123\"");
740
        assert_eq!(
741
            variable_value(&mut reader).unwrap(),
742
            VariableValue::String(Template::new(
743
                Some('"'),
744
                vec![TemplateElement::String {
745
                    value: "123".to_string(),
746
                    source: "123".to_source(),
747
                }],
748
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6))
749
            ))
750
        );
751
    }
752
}