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
6690
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
37
6690
    let line_terminators = optional_line_terminators(reader)?;
38
6690
    let space0 = zero_or_more_spaces(reader)?;
39
6690
    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
6690
    let option =
44
50278
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
45
6690
    let space1 = zero_or_more_spaces(reader)?;
46
6690
    try_literal(":", reader)?;
47
4625
    let space2 = zero_or_more_spaces(reader)?;
48
4625
    let kind = match option.as_str() {
49
4625
        "aws-sigv4" => option_aws_sigv4(reader)?,
50
4575
        "cacert" => option_cacert(reader)?,
51
4495
        "cert" => option_cert(reader)?,
52
4415
        "compressed" => option_compressed(reader)?,
53
3995
        "connect-to" => option_connect_to(reader)?,
54
3920
        "connect-timeout" => option_connect_timeout(reader)?,
55
3880
        "delay" => option_delay(reader)?,
56
3545
        "insecure" => option_insecure(reader)?,
57
3435
        "header" => option_header(reader)?,
58
3370
        "http1.0" => option_http_10(reader)?,
59
3240
        "http1.1" => option_http_11(reader)?,
60
3140
        "http2" => option_http_2(reader)?,
61
3080
        "http3" => option_http_3(reader)?,
62
3040
        "ipv4" => option_ipv4(reader)?,
63
3000
        "ipv6" => option_ipv6(reader)?,
64
2960
        "key" => option_key(reader)?,
65
2900
        "limit-rate" => option_limit_rate(reader)?,
66
2855
        "location" => option_follow_location(reader)?,
67
2390
        "location-trusted" => option_follow_location_trusted(reader)?,
68
2330
        "max-redirs" => option_max_redirect(reader)?,
69
2215
        "max-time" => option_max_time(reader)?,
70
2175
        "negotiate" => option_negotiate(reader)?,
71
2125
        "netrc" => option_netrc(reader)?,
72
2085
        "netrc-file" => option_netrc_file(reader)?,
73
2040
        "netrc-optional" => option_netrc_optional(reader)?,
74
2000
        "ntlm" => option_ntlm(reader)?,
75
1945
        "output" => option_output(reader)?,
76
1805
        "path-as-is" => option_path_as_is(reader)?,
77
1760
        "pinnedpubkey" => option_pinned_pub_key(reader)?,
78
1710
        "proxy" => option_proxy(reader)?,
79
1630
        "repeat" => option_repeat(reader)?,
80
1515
        "resolve" => option_resolve(reader)?,
81
1455
        "retry" => option_retry(reader)?,
82
1280
        "retry-interval" => option_retry_interval(reader)?,
83
1150
        "skip" => option_skip(reader)?,
84
1100
        "unix-socket" => option_unix_socket(reader)?,
85
1060
        "user" => option_user(reader)?,
86
925
        "variable" => option_variable(reader)?,
87
245
        "verbose" => option_verbose(reader)?,
88
70
        "very-verbose" => option_very_verbose(reader)?,
89
        _ => {
90
10
            return Err(ParseError::new(
91
10
                start.pos,
92
10
                false,
93
10
                ParseErrorKind::InvalidOption(option.to_string()),
94
10
            ))
95
        }
96
    };
97

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
304
60
fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
305
60
    let value = non_recover(boolean_option, reader)?;
306
60
    Ok(OptionKind::VeryVerbose(value))
307
}
308

            
309
405
fn count(reader: &mut Reader) -> ParseResult<Count> {
310
405
    let start = reader.cursor();
311
405
    let value = non_recover(integer, reader)?;
312
290
    if value.as_i64() == -1 {
313
50
        Ok(Count::Infinite)
314
240
    } else if value.as_i64() >= 0 {
315
235
        Ok(Count::Finite(value.as_i64() as usize))
316
    } else {
317
5
        let kind = ParseErrorKind::Expecting {
318
5
            value: "Expecting a count value".to_string(),
319
5
        };
320
5
        Err(ParseError::new(start.pos, false, kind))
321
    }
322
}
323

            
324
1980
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
325
1980
    let start = reader.cursor();
326
1980
    match boolean(reader) {
327
1575
        Ok(v) => Ok(BooleanOption::Literal(v)),
328
        Err(_) => {
329
405
            reader.seek(start);
330
406
            let exp = placeholder::parse(reader).map_err(|e| {
331
5
                let kind = ParseErrorKind::Expecting {
332
5
                    value: "true|false".to_string(),
333
5
                };
334
5
                ParseError::new(e.pos, false, kind)
335
6
            })?;
336
400
            Ok(BooleanOption::Placeholder(exp))
337
        }
338
    }
339
}
340

            
341
45
fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
342
45
    let start = reader.cursor();
343
45
    match natural(reader) {
344
25
        Ok(v) => Ok(NaturalOption::Literal(v)),
345
        Err(_) => {
346
20
            reader.seek(start);
347
20
            let placeholder = placeholder::parse(reader).map_err(|e| {
348
                let kind = ParseErrorKind::Expecting {
349
                    value: "integer >= 0".to_string(),
350
                };
351
                ParseError::new(e.pos, false, kind)
352
            })?;
353
20
            Ok(NaturalOption::Placeholder(placeholder))
354
        }
355
    }
356
}
357

            
358
405
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
359
405
    let start = reader.cursor();
360
405
    match count(reader) {
361
285
        Ok(v) => Ok(CountOption::Literal(v)),
362
        Err(_) => {
363
120
            reader.seek(start);
364
121
            let placeholder = placeholder::parse(reader).map_err(|e| {
365
5
                let kind = ParseErrorKind::Expecting {
366
5
                    value: "integer >= -1".to_string(),
367
5
                };
368
5
                ParseError::new(e.pos, false, kind)
369
6
            })?;
370
115
            Ok(CountOption::Placeholder(placeholder))
371
        }
372
    }
373
}
374

            
375
545
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
376
545
    let start = reader.cursor();
377
545
    match duration(reader) {
378
455
        Ok(v) => Ok(DurationOption::Literal(v)),
379
90
        Err(e) => {
380
90
            if e.recoverable {
381
85
                reader.seek(start);
382
85
                let placeholder = placeholder::parse(reader).map_err(|e| {
383
                    let kind = ParseErrorKind::Expecting {
384
                        value: "integer".to_string(),
385
                    };
386
                    ParseError::new(e.pos, false, kind)
387
                })?;
388
85
                Ok(DurationOption::Placeholder(placeholder))
389
            } else {
390
5
                Err(e)
391
            }
392
        }
393
    }
394
}
395

            
396
680
fn variable_definition(reader: &mut Reader) -> ParseResult<VariableDefinition> {
397
680
    let start = reader.cursor();
398
680
    let name = variable_name(reader)?;
399
675
    let space0 = zero_or_more_spaces(reader)?;
400
675
    literal("=", reader)?;
401
675
    let space1 = zero_or_more_spaces(reader)?;
402
675
    let value = variable_value(reader)?;
403
675
    let end = reader.cursor();
404
675
    let source_info = SourceInfo {
405
675
        start: start.pos,
406
675
        end: end.pos,
407
675
    };
408
675
    Ok(VariableDefinition {
409
675
        source_info,
410
675
        name,
411
675
        space0,
412
675
        space1,
413
675
        value,
414
675
    })
415
}
416

            
417
680
fn variable_name(reader: &mut Reader) -> ParseResult<String> {
418
680
    let start = reader.cursor();
419
4241
    let name = reader.read_while(|c| c.is_alphanumeric() || c == '_' || c == '-');
420
680
    if name.is_empty() {
421
        let kind = ParseErrorKind::Expecting {
422
            value: "variable name".to_string(),
423
        };
424
        return Err(ParseError::new(start.pos, false, kind));
425
680
    } else if is_variable_reserved(&name) {
426
5
        let kind = ParseErrorKind::Variable(format!(
427
5
            "conflicts with the {name} function, use a different name"
428
5
        ));
429
5
        return Err(ParseError::new(start.pos, false, kind));
430
    }
431
675
    Ok(name)
432
}
433

            
434
675
fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
435
675
    choice(
436
        &[
437
675
            |p1| match null(p1) {
438
20
                Ok(()) => Ok(VariableValue::Null),
439
655
                Err(e) => Err(e),
440
675
            },
441
655
            |p1| match boolean(p1) {
442
25
                Ok(value) => Ok(VariableValue::Bool(value)),
443
630
                Err(e) => Err(e),
444
655
            },
445
630
            |p1| match number(p1) {
446
245
                Ok(value) => Ok(VariableValue::Number(value)),
447
385
                Err(e) => Err(e),
448
630
            },
449
385
            |p1| match quoted_template(p1) {
450
                Ok(value) => Ok(VariableValue::String(value)),
451
385
                Err(e) => Err(e),
452
385
            },
453
385
            |p1| match unquoted_template(p1) {
454
385
                Ok(value) => Ok(VariableValue::String(value)),
455
                Err(e) => Err(e),
456
385
            },
457
        ],
458
675
        reader,
459
    )
460
675
    .map_err(|e| {
461
        let kind = ParseErrorKind::Expecting {
462
            value: "variable value".to_string(),
463
        };
464
        ParseError::new(e.pos, false, kind)
465
    })
466
}
467

            
468
#[cfg(test)]
469
mod tests {
470
    use super::*;
471
    use crate::ast::{LineTerminator, Number, Template, TemplateElement, Whitespace, I64};
472
    use crate::reader::Pos;
473
    use crate::typing::ToSource;
474

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

            
544
    #[test]
545
    fn test_option_insecure_error() {
546
        let mut reader = Reader::new("insecure: error");
547
        let error = parse(&mut reader).err().unwrap();
548
        assert!(!error.recoverable);
549
    }
550

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

            
627
    #[test]
628
    fn test_option_cacert_error() {
629
        let mut reader = Reader::new("cacert: ###");
630
        let error = parse(&mut reader).err().unwrap();
631
        assert!(!error.recoverable);
632
    }
633

            
634
    #[test]
635
    fn test_option_cert() {
636
        let mut reader = Reader::new("/etc/client-cert.pem #foo");
637

            
638
        assert_eq!(
639
            option_cert(&mut reader).unwrap(),
640
            OptionKind::ClientCert(Template::new(
641
                None,
642
                vec![TemplateElement::String {
643
                    value: "/etc/client-cert.pem".to_string(),
644
                    source: "/etc/client-cert.pem".to_source()
645
                }],
646
                SourceInfo {
647
                    start: Pos { line: 1, column: 1 },
648
                    end: Pos {
649
                        line: 1,
650
                        column: 21,
651
                    },
652
                },
653
            )),
654
        );
655
    }
656

            
657
    #[test]
658
    fn test_option_retry_error() {
659
        let mut reader = Reader::new("retry: ###");
660
        let error = parse(&mut reader).err().unwrap();
661
        assert!(!error.recoverable);
662
        assert_eq!(error.pos, Pos { line: 1, column: 8 });
663
        assert_eq!(
664
            error.kind,
665
            ParseErrorKind::Expecting {
666
                value: "integer >= -1".to_string()
667
            }
668
        );
669
    }
670

            
671
    #[test]
672
    fn test_variable_definition() {
673
        let mut reader = Reader::new("a=1");
674
        assert_eq!(
675
            variable_definition(&mut reader).unwrap(),
676
            VariableDefinition {
677
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4)),
678
                name: "a".to_string(),
679
                space0: Whitespace {
680
                    value: String::new(),
681
                    source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
682
                },
683
                space1: Whitespace {
684
                    value: String::new(),
685
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
686
                },
687
                value: VariableValue::Number(Number::Integer(I64::new(1, "1".to_source()))),
688
            }
689
        );
690
    }
691

            
692
    #[test]
693
    fn test_variable_value() {
694
        let mut reader = Reader::new("null");
695
        assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null);
696

            
697
        let mut reader = Reader::new("true");
698
        assert_eq!(
699
            variable_value(&mut reader).unwrap(),
700
            VariableValue::Bool(true)
701
        );
702

            
703
        let mut reader = Reader::new("1");
704
        assert_eq!(
705
            variable_value(&mut reader).unwrap(),
706
            VariableValue::Number(Number::Integer(I64::new(1, "1".to_source())))
707
        );
708

            
709
        let mut reader = Reader::new("toto");
710
        assert_eq!(
711
            variable_value(&mut reader).unwrap(),
712
            VariableValue::String(Template::new(
713
                None,
714
                vec![TemplateElement::String {
715
                    value: "toto".to_string(),
716
                    source: "toto".to_source(),
717
                }],
718
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
719
            ))
720
        );
721
        let mut reader = Reader::new("\"123\"");
722
        assert_eq!(
723
            variable_value(&mut reader).unwrap(),
724
            VariableValue::String(Template::new(
725
                Some('"'),
726
                vec![TemplateElement::String {
727
                    value: "123".to_string(),
728
                    source: "123".to_source(),
729
                }],
730
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6))
731
            ))
732
        );
733
    }
734
}