1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2026 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
    BooleanOption, CountOption, DurationOption, EntryOption, NaturalOption, OptionKind, SourceInfo,
21
    VariableDefinition, VariableValue, VerbosityOption, is_variable_reserved,
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::{ParseError, ParseErrorKind, ParseResult, filename, filename_password};
32
use crate::reader::Reader;
33
use crate::types::Count;
34

            
35
/// Parse an option in an `[Options]` section.
36
7700
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
37
7700
    let line_terminators = optional_line_terminators(reader)?;
38
7700
    let space0 = zero_or_more_spaces(reader)?;
39
7700
    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
7700
    let option =
44
57100
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
45
7700
    let space1 = zero_or_more_spaces(reader)?;
46
7700
    try_literal(":", reader)?;
47
5255
    let space2 = zero_or_more_spaces(reader)?;
48
5255
    let kind = match option.as_str() {
49
5255
        "aws-sigv4" => option_aws_sigv4(reader)?,
50
5205
        "cacert" => option_cacert(reader)?,
51
5125
        "cert" => option_cert(reader)?,
52
5045
        "compressed" => option_compressed(reader)?,
53
4625
        "connect-to" => option_connect_to(reader)?,
54
4550
        "connect-timeout" => option_connect_timeout(reader)?,
55
4510
        "delay" => option_delay(reader)?,
56
3885
        "digest" => option_digest(reader)?,
57
3840
        "fail-with-body" => option_fail_with_boddy(reader)?,
58
3795
        "insecure" => option_insecure(reader)?,
59
3685
        "header" => option_header(reader)?,
60
3595
        "http1.0" => option_http_10(reader)?,
61
3465
        "http1.1" => option_http_11(reader)?,
62
3365
        "http2" => option_http_2(reader)?,
63
3305
        "http3" => option_http_3(reader)?,
64
3265
        "ipv4" => option_ipv4(reader)?,
65
3225
        "ipv6" => option_ipv6(reader)?,
66
3185
        "key" => option_key(reader)?,
67
3125
        "limit-rate" => option_limit_rate(reader)?,
68
3080
        "location" => option_follow_location(reader)?,
69
2615
        "location-trusted" => option_follow_location_trusted(reader)?,
70
2555
        "max-redirs" => option_max_redirect(reader)?,
71
2440
        "max-time" => option_max_time(reader)?,
72
2400
        "negotiate" => option_negotiate(reader)?,
73
2350
        "netrc" => option_netrc(reader)?,
74
2310
        "netrc-file" => option_netrc_file(reader)?,
75
2265
        "netrc-optional" => option_netrc_optional(reader)?,
76
2225
        "no-header" => option_no_header(reader)?,
77
2160
        "ntlm" => option_ntlm(reader)?,
78
2105
        "output" => option_output(reader)?,
79
1930
        "path-as-is" => option_path_as_is(reader)?,
80
1885
        "pinnedpubkey" => option_pinned_pub_key(reader)?,
81
1835
        "proxy" => option_proxy(reader)?,
82
1755
        "repeat" => option_repeat(reader)?,
83
1640
        "resolve" => option_resolve(reader)?,
84
1580
        "retry" => option_retry(reader)?,
85
1395
        "retry-interval" => option_retry_interval(reader)?,
86
1255
        "skip" => option_skip(reader)?,
87
1205
        "unix-socket" => option_unix_socket(reader)?,
88
1165
        "user" => option_user(reader)?,
89
1015
        "variable" => option_variable(reader)?,
90
310
        "verbose" => option_verbose(reader)?,
91
130
        "verbosity" => option_verbosity(reader)?,
92
70
        "very-verbose" => option_very_verbose(reader)?,
93
        _ => {
94
10
            return Err(ParseError::new(
95
10
                start.pos,
96
10
                false,
97
10
                ParseErrorKind::InvalidOption(option.to_string()),
98
10
            ));
99
        }
100
    };
101

            
102
5215
    let line_terminator0 = line_terminator(reader)?;
103
5215
    Ok(EntryOption {
104
5215
        line_terminators,
105
5215
        space0,
106
5215
        space1,
107
5215
        space2,
108
5215
        kind,
109
5215
        line_terminator0,
110
5215
    })
111
}
112

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

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

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

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

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

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

            
143
625
fn option_delay(reader: &mut Reader) -> ParseResult<OptionKind> {
144
625
    let value = duration_option(reader)?;
145
620
    Ok(OptionKind::Delay(value))
146
}
147

            
148
45
fn option_digest(reader: &mut Reader) -> ParseResult<OptionKind> {
149
45
    let value = non_recover(boolean_option, reader)?;
150
45
    Ok(OptionKind::Digest(value))
151
}
152

            
153
45
fn option_fail_with_boddy(reader: &mut Reader) -> ParseResult<OptionKind> {
154
45
    let value = non_recover(boolean_option, reader)?;
155
45
    Ok(OptionKind::FailWithBody(value))
156
}
157

            
158
465
fn option_follow_location(reader: &mut Reader) -> ParseResult<OptionKind> {
159
465
    let value = non_recover(boolean_option, reader)?;
160
465
    Ok(OptionKind::FollowLocation(value))
161
}
162

            
163
60
fn option_follow_location_trusted(reader: &mut Reader) -> ParseResult<OptionKind> {
164
60
    let value = non_recover(boolean_option, reader)?;
165
60
    Ok(OptionKind::FollowLocationTrusted(value))
166
}
167

            
168
90
fn option_header(reader: &mut Reader) -> ParseResult<OptionKind> {
169
90
    let value = unquoted_template(reader)?;
170
90
    Ok(OptionKind::Header(value))
171
}
172

            
173
130
fn option_http_10(reader: &mut Reader) -> ParseResult<OptionKind> {
174
130
    let value = non_recover(boolean_option, reader)?;
175
130
    Ok(OptionKind::Http10(value))
176
}
177

            
178
100
fn option_http_11(reader: &mut Reader) -> ParseResult<OptionKind> {
179
100
    let value = non_recover(boolean_option, reader)?;
180
100
    Ok(OptionKind::Http11(value))
181
}
182

            
183
60
fn option_http_2(reader: &mut Reader) -> ParseResult<OptionKind> {
184
60
    let value = non_recover(boolean_option, reader)?;
185
60
    Ok(OptionKind::Http2(value))
186
}
187

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

            
193
110
fn option_insecure(reader: &mut Reader) -> ParseResult<OptionKind> {
194
110
    let value = non_recover(boolean_option, reader)?;
195
105
    Ok(OptionKind::Insecure(value))
196
}
197

            
198
40
fn option_ipv4(reader: &mut Reader) -> ParseResult<OptionKind> {
199
40
    let value = non_recover(boolean_option, reader)?;
200
40
    Ok(OptionKind::IpV4(value))
201
}
202

            
203
40
fn option_ipv6(reader: &mut Reader) -> ParseResult<OptionKind> {
204
40
    let value = non_recover(boolean_option, reader)?;
205
40
    Ok(OptionKind::IpV6(value))
206
}
207

            
208
60
fn option_key(reader: &mut Reader) -> ParseResult<OptionKind> {
209
60
    let value = filename::parse(reader)?;
210
60
    Ok(OptionKind::ClientKey(value))
211
}
212

            
213
45
fn option_limit_rate(reader: &mut Reader) -> ParseResult<OptionKind> {
214
45
    let value = non_recover(natural_option, reader)?;
215
45
    Ok(OptionKind::LimitRate(value))
216
}
217

            
218
115
fn option_max_redirect(reader: &mut Reader) -> ParseResult<OptionKind> {
219
115
    let value = non_recover(count_option, reader)?;
220
115
    Ok(OptionKind::MaxRedirect(value))
221
}
222

            
223
40
fn option_max_time(reader: &mut Reader) -> ParseResult<OptionKind> {
224
40
    let value = duration_option(reader)?;
225
40
    Ok(OptionKind::MaxTime(value))
226
}
227

            
228
50
fn option_negotiate(reader: &mut Reader) -> ParseResult<OptionKind> {
229
50
    let value = boolean_option(reader)?;
230
50
    Ok(OptionKind::Negotiate(value))
231
}
232

            
233
40
fn option_netrc(reader: &mut Reader) -> ParseResult<OptionKind> {
234
40
    let value = non_recover(boolean_option, reader)?;
235
40
    Ok(OptionKind::NetRc(value))
236
}
237

            
238
45
fn option_netrc_file(reader: &mut Reader) -> ParseResult<OptionKind> {
239
45
    let value = unquoted_template(reader)?;
240
45
    Ok(OptionKind::NetRcFile(value))
241
}
242

            
243
40
fn option_netrc_optional(reader: &mut Reader) -> ParseResult<OptionKind> {
244
40
    let value = non_recover(boolean_option, reader)?;
245
40
    Ok(OptionKind::NetRcOptional(value))
246
}
247

            
248
65
fn option_no_header(reader: &mut Reader) -> ParseResult<OptionKind> {
249
65
    let value = unquoted_template(reader)?;
250
65
    Ok(OptionKind::NoHeader(value))
251
}
252

            
253
55
fn option_ntlm(reader: &mut Reader) -> ParseResult<OptionKind> {
254
55
    let value = non_recover(boolean_option, reader)?;
255
55
    Ok(OptionKind::Ntlm(value))
256
}
257

            
258
175
fn option_output(reader: &mut Reader) -> ParseResult<OptionKind> {
259
175
    let value = filename::parse(reader)?;
260
175
    Ok(OptionKind::Output(value))
261
}
262

            
263
45
fn option_path_as_is(reader: &mut Reader) -> ParseResult<OptionKind> {
264
45
    let value = non_recover(boolean_option, reader)?;
265
45
    Ok(OptionKind::PathAsIs(value))
266
}
267

            
268
50
fn option_pinned_pub_key(reader: &mut Reader) -> ParseResult<OptionKind> {
269
50
    let value = unquoted_template(reader)?;
270
50
    Ok(OptionKind::PinnedPublicKey(value))
271
}
272

            
273
80
fn option_proxy(reader: &mut Reader) -> ParseResult<OptionKind> {
274
80
    let value = unquoted_template(reader)?;
275
80
    Ok(OptionKind::Proxy(value))
276
}
277

            
278
115
fn option_repeat(reader: &mut Reader) -> ParseResult<OptionKind> {
279
115
    let value = non_recover(count_option, reader)?;
280
115
    Ok(OptionKind::Repeat(value))
281
}
282

            
283
60
fn option_resolve(reader: &mut Reader) -> ParseResult<OptionKind> {
284
60
    let value = unquoted_template(reader)?;
285
60
    Ok(OptionKind::Resolve(value))
286
}
287

            
288
185
fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
289
185
    let value = non_recover(count_option, reader)?;
290
180
    Ok(OptionKind::Retry(value))
291
}
292

            
293
140
fn option_retry_interval(reader: &mut Reader) -> ParseResult<OptionKind> {
294
140
    let value = non_recover(duration_option, reader)?;
295
140
    Ok(OptionKind::RetryInterval(value))
296
}
297

            
298
50
fn option_skip(reader: &mut Reader) -> ParseResult<OptionKind> {
299
50
    let value = non_recover(boolean_option, reader)?;
300
50
    Ok(OptionKind::Skip(value))
301
}
302

            
303
150
fn option_user(reader: &mut Reader) -> ParseResult<OptionKind> {
304
150
    let value = unquoted_template(reader)?;
305
150
    Ok(OptionKind::User(value))
306
}
307

            
308
40
fn option_unix_socket(reader: &mut Reader) -> ParseResult<OptionKind> {
309
40
    let value = unquoted_template(reader)?;
310
40
    Ok(OptionKind::UnixSocket(value))
311
}
312

            
313
705
fn option_variable(reader: &mut Reader) -> ParseResult<OptionKind> {
314
705
    let value = variable_definition(reader)?;
315
700
    Ok(OptionKind::Variable(value))
316
}
317

            
318
180
fn option_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
319
180
    let value = non_recover(boolean_option, reader)?;
320
180
    Ok(OptionKind::Verbose(value))
321
}
322

            
323
60
fn option_verbosity(reader: &mut Reader) -> ParseResult<OptionKind> {
324
60
    let start = reader.cursor();
325
372
    let name = reader.read_while(|c| c.is_ascii_alphabetic());
326
60
    match name.as_str() {
327
60
        "brief" => Ok(OptionKind::Verbosity(VerbosityOption::Brief)),
328
15
        "verbose" => Ok(OptionKind::Verbosity(VerbosityOption::Verbose)),
329
10
        "debug" => Ok(OptionKind::Verbosity(VerbosityOption::Debug)),
330
        _ => {
331
5
            reader.seek(start);
332
5
            let kind = ParseErrorKind::Expecting {
333
5
                value: "brief|verbose|debug".to_string(),
334
5
            };
335
5
            Err(ParseError::new(start.pos, false, kind))
336
        }
337
    }
338
}
339

            
340
60
fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
341
60
    let value = non_recover(boolean_option, reader)?;
342
60
    Ok(OptionKind::VeryVerbose(value))
343
}
344

            
345
415
fn count(reader: &mut Reader) -> ParseResult<Count> {
346
415
    let start = reader.cursor();
347
415
    let value = non_recover(integer, reader)?;
348
300
    if value.as_i64() == -1 {
349
50
        Ok(Count::Infinite)
350
250
    } else if value.as_i64() >= 0 {
351
245
        Ok(Count::Finite(value.as_i64() as usize))
352
    } else {
353
5
        let kind = ParseErrorKind::Expecting {
354
5
            value: "Expecting a count value".to_string(),
355
5
        };
356
5
        Err(ParseError::new(start.pos, false, kind))
357
    }
358
}
359

            
360
2075
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
361
2075
    let start = reader.cursor();
362
2075
    match boolean(reader) {
363
1630
        Ok(v) => Ok(BooleanOption::Literal(v)),
364
        Err(_) => {
365
445
            reader.seek(start);
366
446
            let exp = placeholder::parse(reader).map_err(|e| {
367
5
                let kind = ParseErrorKind::Expecting {
368
5
                    value: "true|false".to_string(),
369
5
                };
370
5
                ParseError::new(e.pos, false, kind)
371
6
            })?;
372
440
            Ok(BooleanOption::Placeholder(exp))
373
        }
374
    }
375
}
376

            
377
45
fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
378
45
    let start = reader.cursor();
379
45
    match natural(reader) {
380
25
        Ok(v) => Ok(NaturalOption::Literal(v)),
381
        Err(_) => {
382
20
            reader.seek(start);
383
20
            let placeholder = placeholder::parse(reader).map_err(|e| {
384
                let kind = ParseErrorKind::Expecting {
385
                    value: "integer >= 0".to_string(),
386
                };
387
                ParseError::new(e.pos, false, kind)
388
            })?;
389
20
            Ok(NaturalOption::Placeholder(placeholder))
390
        }
391
    }
392
}
393

            
394
415
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
395
415
    let start = reader.cursor();
396
415
    match count(reader) {
397
295
        Ok(v) => Ok(CountOption::Literal(v)),
398
        Err(_) => {
399
120
            reader.seek(start);
400
121
            let placeholder = placeholder::parse(reader).map_err(|e| {
401
5
                let kind = ParseErrorKind::Expecting {
402
5
                    value: "integer >= -1".to_string(),
403
5
                };
404
5
                ParseError::new(e.pos, false, kind)
405
6
            })?;
406
115
            Ok(CountOption::Placeholder(placeholder))
407
        }
408
    }
409
}
410

            
411
845
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
412
845
    let start = reader.cursor();
413
845
    match duration(reader) {
414
755
        Ok(v) => Ok(DurationOption::Literal(v)),
415
90
        Err(e) => {
416
90
            if e.recoverable {
417
85
                reader.seek(start);
418
85
                let placeholder = placeholder::parse(reader).map_err(|e| {
419
                    let kind = ParseErrorKind::Expecting {
420
                        value: "integer".to_string(),
421
                    };
422
                    ParseError::new(e.pos, false, kind)
423
                })?;
424
85
                Ok(DurationOption::Placeholder(placeholder))
425
            } else {
426
5
                Err(e)
427
            }
428
        }
429
    }
430
}
431

            
432
705
fn variable_definition(reader: &mut Reader) -> ParseResult<VariableDefinition> {
433
705
    let start = reader.cursor();
434
705
    let name = variable_name(reader)?;
435
700
    let space0 = zero_or_more_spaces(reader)?;
436
700
    literal("=", reader)?;
437
700
    let space1 = zero_or_more_spaces(reader)?;
438
700
    let value = variable_value(reader)?;
439
700
    let end = reader.cursor();
440
700
    let source_info = SourceInfo {
441
700
        start: start.pos,
442
700
        end: end.pos,
443
700
    };
444
700
    Ok(VariableDefinition {
445
700
        source_info,
446
700
        name,
447
700
        space0,
448
700
        space1,
449
700
        value,
450
700
    })
451
}
452

            
453
705
fn variable_name(reader: &mut Reader) -> ParseResult<String> {
454
705
    let start = reader.cursor();
455
4346
    let name = reader.read_while(|c| c.is_alphanumeric() || c == '_' || c == '-');
456
705
    if name.is_empty() {
457
        let kind = ParseErrorKind::Expecting {
458
            value: "variable name".to_string(),
459
        };
460
        return Err(ParseError::new(start.pos, false, kind));
461
705
    } else if is_variable_reserved(&name) {
462
5
        let kind = ParseErrorKind::Variable(format!(
463
5
            "conflicts with the {name} function, use a different name"
464
5
        ));
465
5
        return Err(ParseError::new(start.pos, false, kind));
466
    }
467
700
    Ok(name)
468
}
469

            
470
700
fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
471
700
    choice(
472
        &[
473
700
            |p1| match null(p1) {
474
20
                Ok(()) => Ok(VariableValue::Null),
475
680
                Err(e) => Err(e),
476
700
            },
477
680
            |p1| match boolean(p1) {
478
25
                Ok(value) => Ok(VariableValue::Bool(value)),
479
655
                Err(e) => Err(e),
480
680
            },
481
655
            |p1| match number(p1) {
482
245
                Ok(value) => Ok(VariableValue::Number(value)),
483
410
                Err(e) => Err(e),
484
655
            },
485
410
            |p1| match quoted_template(p1) {
486
                Ok(value) => Ok(VariableValue::String(value)),
487
410
                Err(e) => Err(e),
488
410
            },
489
410
            |p1| match unquoted_template(p1) {
490
410
                Ok(value) => Ok(VariableValue::String(value)),
491
                Err(e) => Err(e),
492
410
            },
493
        ],
494
700
        reader,
495
    )
496
700
    .map_err(|e| {
497
        let kind = ParseErrorKind::Expecting {
498
            value: "variable value".to_string(),
499
        };
500
        ParseError::new(e.pos, false, kind)
501
    })
502
}
503

            
504
#[cfg(test)]
505
mod tests {
506
    use super::*;
507
    use crate::ast::{I64, LineTerminator, Number, Template, TemplateElement, Whitespace};
508
    use crate::reader::Pos;
509
    use crate::types::ToSource;
510

            
511
    #[test]
512
    fn test_option_insecure() {
513
        let mut reader = Reader::new("insecure: true");
514
        let option = parse(&mut reader).unwrap();
515
        assert_eq!(
516
            option,
517
            EntryOption {
518
                line_terminators: vec![],
519
                space0: Whitespace {
520
                    value: String::new(),
521
                    source_info: SourceInfo {
522
                        start: Pos { line: 1, column: 1 },
523
                        end: Pos { line: 1, column: 1 },
524
                    },
525
                },
526
                space1: Whitespace {
527
                    value: String::new(),
528
                    source_info: SourceInfo {
529
                        start: Pos { line: 1, column: 9 },
530
                        end: Pos { line: 1, column: 9 },
531
                    },
532
                },
533
                space2: Whitespace {
534
                    value: " ".to_string(),
535
                    source_info: SourceInfo {
536
                        start: Pos {
537
                            line: 1,
538
                            column: 10,
539
                        },
540
                        end: Pos {
541
                            line: 1,
542
                            column: 11,
543
                        },
544
                    },
545
                },
546
                kind: OptionKind::Insecure(BooleanOption::Literal(true)),
547
                line_terminator0: LineTerminator {
548
                    space0: Whitespace {
549
                        value: String::new(),
550
                        source_info: SourceInfo {
551
                            start: Pos {
552
                                line: 1,
553
                                column: 15,
554
                            },
555
                            end: Pos {
556
                                line: 1,
557
                                column: 15,
558
                            },
559
                        },
560
                    },
561
                    comment: None,
562
                    newline: Whitespace {
563
                        value: String::new(),
564
                        source_info: SourceInfo {
565
                            start: Pos {
566
                                line: 1,
567
                                column: 15,
568
                            },
569
                            end: Pos {
570
                                line: 1,
571
                                column: 15,
572
                            },
573
                        },
574
                    },
575
                },
576
            }
577
        );
578
    }
579

            
580
    #[test]
581
    fn test_option_insecure_error() {
582
        let mut reader = Reader::new("insecure: error");
583
        let error = parse(&mut reader).err().unwrap();
584
        assert!(!error.recoverable);
585
    }
586

            
587
    #[test]
588
    fn test_option_cacert() {
589
        let mut reader = Reader::new("cacert: /home/foo/cert.pem");
590
        let option = parse(&mut reader).unwrap();
591
        assert_eq!(
592
            option,
593
            EntryOption {
594
                line_terminators: vec![],
595
                space0: Whitespace {
596
                    value: String::new(),
597
                    source_info: SourceInfo {
598
                        start: Pos { line: 1, column: 1 },
599
                        end: Pos { line: 1, column: 1 },
600
                    },
601
                },
602
                space1: Whitespace {
603
                    value: String::new(),
604
                    source_info: SourceInfo {
605
                        start: Pos { line: 1, column: 7 },
606
                        end: Pos { line: 1, column: 7 },
607
                    },
608
                },
609
                space2: Whitespace {
610
                    value: " ".to_string(),
611
                    source_info: SourceInfo {
612
                        start: Pos { line: 1, column: 8 },
613
                        end: Pos { line: 1, column: 9 },
614
                    },
615
                },
616
                kind: OptionKind::CaCertificate(Template::new(
617
                    None,
618
                    vec![TemplateElement::String {
619
                        value: "/home/foo/cert.pem".to_string(),
620
                        source: "/home/foo/cert.pem".to_source()
621
                    }],
622
                    SourceInfo {
623
                        start: Pos { line: 1, column: 9 },
624
                        end: Pos {
625
                            line: 1,
626
                            column: 27,
627
                        },
628
                    }
629
                )),
630
                line_terminator0: LineTerminator {
631
                    space0: Whitespace {
632
                        value: String::new(),
633
                        source_info: SourceInfo {
634
                            start: Pos {
635
                                line: 1,
636
                                column: 27,
637
                            },
638
                            end: Pos {
639
                                line: 1,
640
                                column: 27,
641
                            },
642
                        },
643
                    },
644
                    comment: None,
645
                    newline: Whitespace {
646
                        value: String::new(),
647
                        source_info: SourceInfo {
648
                            start: Pos {
649
                                line: 1,
650
                                column: 27,
651
                            },
652
                            end: Pos {
653
                                line: 1,
654
                                column: 27,
655
                            },
656
                        },
657
                    },
658
                },
659
            }
660
        );
661
    }
662

            
663
    #[test]
664
    fn test_option_cacert_error() {
665
        let mut reader = Reader::new("cacert: ###");
666
        let error = parse(&mut reader).err().unwrap();
667
        assert!(!error.recoverable);
668
    }
669

            
670
    #[test]
671
    fn test_option_cert() {
672
        let mut reader = Reader::new("/etc/client-cert.pem #foo");
673

            
674
        assert_eq!(
675
            option_cert(&mut reader).unwrap(),
676
            OptionKind::ClientCert(Template::new(
677
                None,
678
                vec![TemplateElement::String {
679
                    value: "/etc/client-cert.pem".to_string(),
680
                    source: "/etc/client-cert.pem".to_source()
681
                }],
682
                SourceInfo {
683
                    start: Pos { line: 1, column: 1 },
684
                    end: Pos {
685
                        line: 1,
686
                        column: 21,
687
                    },
688
                },
689
            )),
690
        );
691
    }
692

            
693
    #[test]
694
    fn test_option_retry_error() {
695
        let mut reader = Reader::new("retry: ###");
696
        let error = parse(&mut reader).err().unwrap();
697
        assert!(!error.recoverable);
698
        assert_eq!(error.pos, Pos { line: 1, column: 8 });
699
        assert_eq!(
700
            error.kind,
701
            ParseErrorKind::Expecting {
702
                value: "integer >= -1".to_string()
703
            }
704
        );
705
    }
706

            
707
    #[test]
708
    fn test_variable_definition() {
709
        let mut reader = Reader::new("a=1");
710
        assert_eq!(
711
            variable_definition(&mut reader).unwrap(),
712
            VariableDefinition {
713
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4)),
714
                name: "a".to_string(),
715
                space0: Whitespace {
716
                    value: String::new(),
717
                    source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
718
                },
719
                space1: Whitespace {
720
                    value: String::new(),
721
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
722
                },
723
                value: VariableValue::Number(Number::Integer(I64::new(1, "1".to_source()))),
724
            }
725
        );
726
    }
727

            
728
    #[test]
729
    fn test_variable_value() {
730
        let mut reader = Reader::new("null");
731
        assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null);
732

            
733
        let mut reader = Reader::new("true");
734
        assert_eq!(
735
            variable_value(&mut reader).unwrap(),
736
            VariableValue::Bool(true)
737
        );
738

            
739
        let mut reader = Reader::new("1");
740
        assert_eq!(
741
            variable_value(&mut reader).unwrap(),
742
            VariableValue::Number(Number::Integer(I64::new(1, "1".to_source())))
743
        );
744

            
745
        let mut reader = Reader::new("toto");
746
        assert_eq!(
747
            variable_value(&mut reader).unwrap(),
748
            VariableValue::String(Template::new(
749
                None,
750
                vec![TemplateElement::String {
751
                    value: "toto".to_string(),
752
                    source: "toto".to_source(),
753
                }],
754
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
755
            ))
756
        );
757
        let mut reader = Reader::new("\"123\"");
758
        assert_eq!(
759
            variable_value(&mut reader).unwrap(),
760
            VariableValue::String(Template::new(
761
                Some('"'),
762
                vec![TemplateElement::String {
763
                    value: "123".to_string(),
764
                    source: "123".to_source(),
765
                }],
766
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6))
767
            ))
768
        );
769
    }
770
}