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
8015
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
37
8015
    let line_terminators = optional_line_terminators(reader)?;
38
8015
    let space0 = zero_or_more_spaces(reader)?;
39
8015
    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
8015
    let option =
44
60643
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
45
8015
    let space1 = zero_or_more_spaces(reader)?;
46
8015
    try_literal(":", reader)?;
47
5460
    let space2 = zero_or_more_spaces(reader)?;
48
5460
    let kind = match option.as_str() {
49
5460
        "aws-sigv4" => option_aws_sigv4(reader)?,
50
5410
        "cacert" => option_cacert(reader)?,
51
5330
        "cert" => option_cert(reader)?,
52
5250
        "compressed" => option_compressed(reader)?,
53
4830
        "connect-to" => option_connect_to(reader)?,
54
4755
        "connect-timeout" => option_connect_timeout(reader)?,
55
4715
        "delay" => option_delay(reader)?,
56
4090
        "digest" => option_digest(reader)?,
57
4045
        "fail-with-body" => option_fail_with_boddy(reader)?,
58
4000
        "insecure" => option_insecure(reader)?,
59
3885
        "header" => option_header(reader)?,
60
3795
        "http1.0" => option_http_10(reader)?,
61
3665
        "http1.1" => option_http_11(reader)?,
62
3565
        "http2-prior-knowledge" => option_http_2_prior_knowledge(reader)?,
63
3525
        "http2" => option_http_2(reader)?,
64
3445
        "http3" => option_http_3(reader)?,
65
3405
        "ipv4" => option_ipv4(reader)?,
66
3365
        "ipv6" => option_ipv6(reader)?,
67
3325
        "key" => option_key(reader)?,
68
3265
        "limit-rate" => option_limit_rate(reader)?,
69
3220
        "location" => option_follow_location(reader)?,
70
2735
        "location-trusted" => option_follow_location_trusted(reader)?,
71
2675
        "max-redirs" => option_max_redirect(reader)?,
72
2560
        "max-time" => option_max_time(reader)?,
73
2520
        "negotiate" => option_negotiate(reader)?,
74
2470
        "netrc" => option_netrc(reader)?,
75
2430
        "netrc-file" => option_netrc_file(reader)?,
76
2385
        "netrc-optional" => option_netrc_optional(reader)?,
77
2345
        "no-header" => option_no_header(reader)?,
78
2280
        "no-jsonpath-coercion" => option_no_jsonpath_coercion(reader)?,
79
2250
        "ntlm" => option_ntlm(reader)?,
80
2195
        "output" => option_output(reader)?,
81
2020
        "path-as-is" => option_path_as_is(reader)?,
82
1975
        "pinnedpubkey" => option_pinned_pub_key(reader)?,
83
1925
        "proxy" => option_proxy(reader)?,
84
1845
        "repeat" => option_repeat(reader)?,
85
1730
        "resolve" => option_resolve(reader)?,
86
1670
        "retry" => option_retry(reader)?,
87
1480
        "retry-interval" => option_retry_interval(reader)?,
88
1335
        "skip" => option_skip(reader)?,
89
1285
        "unix-socket" => option_unix_socket(reader)?,
90
1245
        "user" => option_user(reader)?,
91
1085
        "variable" => option_variable(reader)?,
92
365
        "variables-file" => option_variables_file(reader)?,
93
310
        "verbose" => option_verbose(reader)?,
94
130
        "verbosity" => option_verbosity(reader)?,
95
70
        "very-verbose" => option_very_verbose(reader)?,
96
        _ => {
97
10
            return Err(ParseError::new(
98
10
                start.pos,
99
10
                false,
100
10
                ParseErrorKind::InvalidOption(option.to_string()),
101
10
            ));
102
        }
103
    };
104

            
105
5420
    let line_terminator0 = line_terminator(reader)?;
106
5420
    Ok(EntryOption {
107
5420
        line_terminators,
108
5420
        space0,
109
5420
        space1,
110
5420
        space2,
111
5420
        kind,
112
5420
        line_terminator0,
113
5420
    })
114
}
115

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

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

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

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

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

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

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

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

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

            
161
485
fn option_follow_location(reader: &mut Reader) -> ParseResult<OptionKind> {
162
485
    let value = non_recover(boolean_option, reader)?;
163
485
    Ok(OptionKind::FollowLocation(value))
164
}
165

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

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

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

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

            
186
80
fn option_http_2(reader: &mut Reader) -> ParseResult<OptionKind> {
187
80
    let value = non_recover(boolean_option, reader)?;
188
80
    Ok(OptionKind::Http2(value))
189
}
190

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

            
196
40
fn option_http_3(reader: &mut Reader) -> ParseResult<OptionKind> {
197
40
    let value = non_recover(boolean_option, reader)?;
198
40
    Ok(OptionKind::Http3(value))
199
}
200

            
201
115
fn option_insecure(reader: &mut Reader) -> ParseResult<OptionKind> {
202
115
    let value = non_recover(boolean_option, reader)?;
203
110
    Ok(OptionKind::Insecure(value))
204
}
205

            
206
40
fn option_ipv4(reader: &mut Reader) -> ParseResult<OptionKind> {
207
40
    let value = non_recover(boolean_option, reader)?;
208
40
    Ok(OptionKind::IpV4(value))
209
}
210

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

            
216
60
fn option_key(reader: &mut Reader) -> ParseResult<OptionKind> {
217
60
    let value = filename::parse(reader)?;
218
60
    Ok(OptionKind::ClientKey(value))
219
}
220

            
221
45
fn option_limit_rate(reader: &mut Reader) -> ParseResult<OptionKind> {
222
45
    let value = non_recover(natural_option, reader)?;
223
45
    Ok(OptionKind::LimitRate(value))
224
}
225

            
226
115
fn option_max_redirect(reader: &mut Reader) -> ParseResult<OptionKind> {
227
115
    let value = non_recover(count_option, reader)?;
228
115
    Ok(OptionKind::MaxRedirect(value))
229
}
230

            
231
40
fn option_max_time(reader: &mut Reader) -> ParseResult<OptionKind> {
232
40
    let value = duration_option(reader)?;
233
40
    Ok(OptionKind::MaxTime(value))
234
}
235

            
236
50
fn option_negotiate(reader: &mut Reader) -> ParseResult<OptionKind> {
237
50
    let value = boolean_option(reader)?;
238
50
    Ok(OptionKind::Negotiate(value))
239
}
240

            
241
40
fn option_netrc(reader: &mut Reader) -> ParseResult<OptionKind> {
242
40
    let value = non_recover(boolean_option, reader)?;
243
40
    Ok(OptionKind::NetRc(value))
244
}
245

            
246
45
fn option_netrc_file(reader: &mut Reader) -> ParseResult<OptionKind> {
247
45
    let value = unquoted_template(reader)?;
248
45
    Ok(OptionKind::NetRcFile(value))
249
}
250

            
251
40
fn option_netrc_optional(reader: &mut Reader) -> ParseResult<OptionKind> {
252
40
    let value = non_recover(boolean_option, reader)?;
253
40
    Ok(OptionKind::NetRcOptional(value))
254
}
255

            
256
65
fn option_no_header(reader: &mut Reader) -> ParseResult<OptionKind> {
257
65
    let value = unquoted_template(reader)?;
258
65
    Ok(OptionKind::NoHeader(value))
259
}
260

            
261
30
fn option_no_jsonpath_coercion(reader: &mut Reader) -> ParseResult<OptionKind> {
262
30
    let value = non_recover(boolean_option, reader)?;
263
30
    Ok(OptionKind::NoJsonpathCoercion(value))
264
}
265

            
266
55
fn option_ntlm(reader: &mut Reader) -> ParseResult<OptionKind> {
267
55
    let value = non_recover(boolean_option, reader)?;
268
55
    Ok(OptionKind::Ntlm(value))
269
}
270

            
271
175
fn option_output(reader: &mut Reader) -> ParseResult<OptionKind> {
272
175
    let value = filename::parse(reader)?;
273
175
    Ok(OptionKind::Output(value))
274
}
275

            
276
45
fn option_path_as_is(reader: &mut Reader) -> ParseResult<OptionKind> {
277
45
    let value = non_recover(boolean_option, reader)?;
278
45
    Ok(OptionKind::PathAsIs(value))
279
}
280

            
281
50
fn option_pinned_pub_key(reader: &mut Reader) -> ParseResult<OptionKind> {
282
50
    let value = unquoted_template(reader)?;
283
50
    Ok(OptionKind::PinnedPublicKey(value))
284
}
285

            
286
80
fn option_proxy(reader: &mut Reader) -> ParseResult<OptionKind> {
287
80
    let value = unquoted_template(reader)?;
288
80
    Ok(OptionKind::Proxy(value))
289
}
290

            
291
115
fn option_repeat(reader: &mut Reader) -> ParseResult<OptionKind> {
292
115
    let value = non_recover(count_option, reader)?;
293
115
    Ok(OptionKind::Repeat(value))
294
}
295

            
296
60
fn option_resolve(reader: &mut Reader) -> ParseResult<OptionKind> {
297
60
    let value = unquoted_template(reader)?;
298
60
    Ok(OptionKind::Resolve(value))
299
}
300

            
301
190
fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
302
190
    let value = non_recover(count_option, reader)?;
303
185
    Ok(OptionKind::Retry(value))
304
}
305

            
306
145
fn option_retry_interval(reader: &mut Reader) -> ParseResult<OptionKind> {
307
145
    let value = non_recover(duration_option, reader)?;
308
145
    Ok(OptionKind::RetryInterval(value))
309
}
310

            
311
50
fn option_skip(reader: &mut Reader) -> ParseResult<OptionKind> {
312
50
    let value = non_recover(boolean_option, reader)?;
313
50
    Ok(OptionKind::Skip(value))
314
}
315

            
316
160
fn option_user(reader: &mut Reader) -> ParseResult<OptionKind> {
317
160
    let value = unquoted_template(reader)?;
318
160
    Ok(OptionKind::User(value))
319
}
320

            
321
40
fn option_unix_socket(reader: &mut Reader) -> ParseResult<OptionKind> {
322
40
    let value = unquoted_template(reader)?;
323
40
    Ok(OptionKind::UnixSocket(value))
324
}
325

            
326
720
fn option_variable(reader: &mut Reader) -> ParseResult<OptionKind> {
327
720
    let value = variable_definition(reader)?;
328
715
    Ok(OptionKind::Variable(value))
329
}
330

            
331
55
fn option_variables_file(reader: &mut Reader) -> ParseResult<OptionKind> {
332
55
    let value = filename::parse(reader)?;
333
55
    Ok(OptionKind::VariablesFile(value))
334
}
335

            
336
180
fn option_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
337
180
    let value = non_recover(boolean_option, reader)?;
338
180
    Ok(OptionKind::Verbose(value))
339
}
340

            
341
60
fn option_verbosity(reader: &mut Reader) -> ParseResult<OptionKind> {
342
60
    let start = reader.cursor();
343
372
    let name = reader.read_while(|c| c.is_ascii_alphabetic());
344
60
    match name.as_str() {
345
60
        "brief" => Ok(OptionKind::Verbosity(VerbosityOption::Brief)),
346
15
        "verbose" => Ok(OptionKind::Verbosity(VerbosityOption::Verbose)),
347
10
        "debug" => Ok(OptionKind::Verbosity(VerbosityOption::Debug)),
348
        _ => {
349
5
            reader.seek(start);
350
5
            let kind = ParseErrorKind::Expecting {
351
5
                value: "brief|verbose|debug".to_string(),
352
5
            };
353
5
            Err(ParseError::new(start.pos, false, kind))
354
        }
355
    }
356
}
357

            
358
60
fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
359
60
    let value = non_recover(boolean_option, reader)?;
360
60
    Ok(OptionKind::VeryVerbose(value))
361
}
362

            
363
420
fn count(reader: &mut Reader) -> ParseResult<Count> {
364
420
    let start = reader.cursor();
365
420
    let value = non_recover(integer, reader)?;
366
305
    if value.as_i64() == -1 {
367
50
        Ok(Count::Infinite)
368
255
    } else if value.as_i64() >= 0 {
369
250
        Ok(Count::Finite(value.as_i64() as usize))
370
    } else {
371
5
        let kind = ParseErrorKind::Expecting {
372
5
            value: "Expecting a count value".to_string(),
373
5
        };
374
5
        Err(ParseError::new(start.pos, false, kind))
375
    }
376
}
377

            
378
2190
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
379
2190
    let start = reader.cursor();
380
2190
    match boolean(reader) {
381
1745
        Ok(v) => Ok(BooleanOption::Literal(v)),
382
        Err(_) => {
383
445
            reader.seek(start);
384
446
            let exp = placeholder::parse(reader).map_err(|e| {
385
5
                let kind = ParseErrorKind::Expecting {
386
5
                    value: "true|false".to_string(),
387
5
                };
388
5
                ParseError::new(e.pos, false, kind)
389
6
            })?;
390
440
            Ok(BooleanOption::Placeholder(exp))
391
        }
392
    }
393
}
394

            
395
45
fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
396
45
    let start = reader.cursor();
397
45
    match natural(reader) {
398
25
        Ok(v) => Ok(NaturalOption::Literal(v)),
399
        Err(_) => {
400
20
            reader.seek(start);
401
20
            let placeholder = placeholder::parse(reader).map_err(|e| {
402
                let kind = ParseErrorKind::Expecting {
403
                    value: "integer >= 0".to_string(),
404
                };
405
                ParseError::new(e.pos, false, kind)
406
            })?;
407
20
            Ok(NaturalOption::Placeholder(placeholder))
408
        }
409
    }
410
}
411

            
412
420
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
413
420
    let start = reader.cursor();
414
420
    match count(reader) {
415
300
        Ok(v) => Ok(CountOption::Literal(v)),
416
        Err(_) => {
417
120
            reader.seek(start);
418
121
            let placeholder = placeholder::parse(reader).map_err(|e| {
419
5
                let kind = ParseErrorKind::Expecting {
420
5
                    value: "integer >= -1".to_string(),
421
5
                };
422
5
                ParseError::new(e.pos, false, kind)
423
6
            })?;
424
115
            Ok(CountOption::Placeholder(placeholder))
425
        }
426
    }
427
}
428

            
429
850
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
430
850
    let start = reader.cursor();
431
850
    match duration(reader) {
432
760
        Ok(v) => Ok(DurationOption::Literal(v)),
433
90
        Err(e) => {
434
90
            if e.recoverable {
435
85
                reader.seek(start);
436
85
                let placeholder = placeholder::parse(reader).map_err(|e| {
437
                    let kind = ParseErrorKind::Expecting {
438
                        value: "integer".to_string(),
439
                    };
440
                    ParseError::new(e.pos, false, kind)
441
                })?;
442
85
                Ok(DurationOption::Placeholder(placeholder))
443
            } else {
444
5
                Err(e)
445
            }
446
        }
447
    }
448
}
449

            
450
720
fn variable_definition(reader: &mut Reader) -> ParseResult<VariableDefinition> {
451
720
    let start = reader.cursor();
452
720
    let name = variable_name(reader)?;
453
715
    let space0 = zero_or_more_spaces(reader)?;
454
715
    literal("=", reader)?;
455
715
    let space1 = zero_or_more_spaces(reader)?;
456
715
    let value = variable_value(reader)?;
457
715
    let end = reader.cursor();
458
715
    let source_info = SourceInfo {
459
715
        start: start.pos,
460
715
        end: end.pos,
461
715
    };
462
715
    Ok(VariableDefinition {
463
715
        source_info,
464
715
        name,
465
715
        space0,
466
715
        space1,
467
715
        value,
468
715
    })
469
}
470

            
471
720
fn variable_name(reader: &mut Reader) -> ParseResult<String> {
472
720
    let start = reader.cursor();
473
4439
    let name = reader.read_while(|c| c.is_alphanumeric() || c == '_' || c == '-');
474
720
    if name.is_empty() {
475
        let kind = ParseErrorKind::Expecting {
476
            value: "variable name".to_string(),
477
        };
478
        return Err(ParseError::new(start.pos, false, kind));
479
720
    } else if is_variable_reserved(&name) {
480
5
        let kind = ParseErrorKind::Variable(format!(
481
5
            "conflicts with the {name} function, use a different name"
482
5
        ));
483
5
        return Err(ParseError::new(start.pos, false, kind));
484
    }
485
715
    Ok(name)
486
}
487

            
488
// `null`, a boolean or a number is only a variable value when the literal spans the whole
489
// value. When more characters follow (as in `11aa` or `true_is_true`), the literal is just
490
// the start of an unquoted string, so return a recoverable error to fall back to it.
491
290
fn literal_value<T>(reader: &mut Reader, value: T) -> ParseResult<T> {
492
290
    match reader.peek() {
493
290
        None | Some(' ' | '\t' | '\r' | '\n' | '#') => Ok(value),
494
        Some(_) => {
495
            let kind = ParseErrorKind::Expecting {
496
                value: "variable value".to_string(),
497
            };
498
            Err(ParseError::new(reader.cursor().pos, true, kind))
499
        }
500
    }
501
}
502

            
503
715
fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
504
715
    choice(
505
        &[
506
715
            |p1| match null(p1) {
507
20
                Ok(()) => literal_value(p1, VariableValue::Null),
508
695
                Err(e) => Err(e),
509
715
            },
510
695
            |p1| match boolean(p1) {
511
25
                Ok(value) => literal_value(p1, VariableValue::Bool(value)),
512
670
                Err(e) => Err(e),
513
695
            },
514
670
            |p1| match number(p1) {
515
245
                Ok(value) => literal_value(p1, VariableValue::Number(value)),
516
425
                Err(e) => Err(e),
517
670
            },
518
425
            |p1| match quoted_template(p1) {
519
                Ok(value) => Ok(VariableValue::String(value)),
520
425
                Err(e) => Err(e),
521
425
            },
522
425
            |p1| match unquoted_template(p1) {
523
425
                Ok(value) => Ok(VariableValue::String(value)),
524
                Err(e) => Err(e),
525
425
            },
526
        ],
527
715
        reader,
528
    )
529
715
    .map_err(|e| {
530
        let kind = ParseErrorKind::Expecting {
531
            value: "variable value".to_string(),
532
        };
533
        ParseError::new(e.pos, false, kind)
534
    })
535
}
536

            
537
#[cfg(test)]
538
mod tests {
539
    use super::*;
540
    use crate::ast::{I64, LineTerminator, Number, Template, TemplateElement, Whitespace};
541
    use crate::reader::Pos;
542
    use crate::types::ToSource;
543

            
544
    #[test]
545
    fn test_option_insecure() {
546
        let mut reader = Reader::new("insecure: true");
547
        let option = parse(&mut reader).unwrap();
548
        assert_eq!(
549
            option,
550
            EntryOption {
551
                line_terminators: vec![],
552
                space0: Whitespace {
553
                    value: String::new(),
554
                    source_info: SourceInfo {
555
                        start: Pos { line: 1, column: 1 },
556
                        end: Pos { line: 1, column: 1 },
557
                    },
558
                },
559
                space1: Whitespace {
560
                    value: String::new(),
561
                    source_info: SourceInfo {
562
                        start: Pos { line: 1, column: 9 },
563
                        end: Pos { line: 1, column: 9 },
564
                    },
565
                },
566
                space2: Whitespace {
567
                    value: " ".to_string(),
568
                    source_info: SourceInfo {
569
                        start: Pos {
570
                            line: 1,
571
                            column: 10,
572
                        },
573
                        end: Pos {
574
                            line: 1,
575
                            column: 11,
576
                        },
577
                    },
578
                },
579
                kind: OptionKind::Insecure(BooleanOption::Literal(true)),
580
                line_terminator0: LineTerminator {
581
                    space0: Whitespace {
582
                        value: String::new(),
583
                        source_info: SourceInfo {
584
                            start: Pos {
585
                                line: 1,
586
                                column: 15,
587
                            },
588
                            end: Pos {
589
                                line: 1,
590
                                column: 15,
591
                            },
592
                        },
593
                    },
594
                    comment: None,
595
                    newline: Whitespace {
596
                        value: String::new(),
597
                        source_info: SourceInfo {
598
                            start: Pos {
599
                                line: 1,
600
                                column: 15,
601
                            },
602
                            end: Pos {
603
                                line: 1,
604
                                column: 15,
605
                            },
606
                        },
607
                    },
608
                },
609
            }
610
        );
611
    }
612

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

            
620
    #[test]
621
    fn test_option_no_jsonpath_coercion() {
622
        let mut reader = Reader::new("no-jsonpath-coercion: true");
623
        let option = parse(&mut reader).unwrap();
624
        assert_eq!(
625
            option.kind,
626
            OptionKind::NoJsonpathCoercion(BooleanOption::Literal(true))
627
        );
628
    }
629

            
630
    #[test]
631
    fn test_option_cacert() {
632
        let mut reader = Reader::new("cacert: /home/foo/cert.pem");
633
        let option = parse(&mut reader).unwrap();
634
        assert_eq!(
635
            option,
636
            EntryOption {
637
                line_terminators: vec![],
638
                space0: Whitespace {
639
                    value: String::new(),
640
                    source_info: SourceInfo {
641
                        start: Pos { line: 1, column: 1 },
642
                        end: Pos { line: 1, column: 1 },
643
                    },
644
                },
645
                space1: Whitespace {
646
                    value: String::new(),
647
                    source_info: SourceInfo {
648
                        start: Pos { line: 1, column: 7 },
649
                        end: Pos { line: 1, column: 7 },
650
                    },
651
                },
652
                space2: Whitespace {
653
                    value: " ".to_string(),
654
                    source_info: SourceInfo {
655
                        start: Pos { line: 1, column: 8 },
656
                        end: Pos { line: 1, column: 9 },
657
                    },
658
                },
659
                kind: OptionKind::CaCertificate(Template::new(
660
                    None,
661
                    vec![TemplateElement::String {
662
                        value: "/home/foo/cert.pem".to_string(),
663
                        source: "/home/foo/cert.pem".to_source()
664
                    }],
665
                    SourceInfo {
666
                        start: Pos { line: 1, column: 9 },
667
                        end: Pos {
668
                            line: 1,
669
                            column: 27,
670
                        },
671
                    }
672
                )),
673
                line_terminator0: LineTerminator {
674
                    space0: Whitespace {
675
                        value: String::new(),
676
                        source_info: SourceInfo {
677
                            start: Pos {
678
                                line: 1,
679
                                column: 27,
680
                            },
681
                            end: Pos {
682
                                line: 1,
683
                                column: 27,
684
                            },
685
                        },
686
                    },
687
                    comment: None,
688
                    newline: Whitespace {
689
                        value: String::new(),
690
                        source_info: SourceInfo {
691
                            start: Pos {
692
                                line: 1,
693
                                column: 27,
694
                            },
695
                            end: Pos {
696
                                line: 1,
697
                                column: 27,
698
                            },
699
                        },
700
                    },
701
                },
702
            }
703
        );
704
    }
705

            
706
    #[test]
707
    fn test_option_variables_file() {
708
        let mut reader = Reader::new("variables-file: vars.env");
709
        let option = parse(&mut reader).unwrap();
710
        assert_eq!(
711
            option,
712
            EntryOption {
713
                line_terminators: vec![],
714
                space0: Whitespace {
715
                    value: String::new(),
716
                    source_info: SourceInfo {
717
                        start: Pos { line: 1, column: 1 },
718
                        end: Pos { line: 1, column: 1 },
719
                    },
720
                },
721
                space1: Whitespace {
722
                    value: String::new(),
723
                    source_info: SourceInfo {
724
                        start: Pos {
725
                            line: 1,
726
                            column: 15,
727
                        },
728
                        end: Pos {
729
                            line: 1,
730
                            column: 15,
731
                        },
732
                    },
733
                },
734
                space2: Whitespace {
735
                    value: " ".to_string(),
736
                    source_info: SourceInfo {
737
                        start: Pos {
738
                            line: 1,
739
                            column: 16,
740
                        },
741
                        end: Pos {
742
                            line: 1,
743
                            column: 17,
744
                        },
745
                    },
746
                },
747
                kind: OptionKind::VariablesFile(Template::new(
748
                    None,
749
                    vec![TemplateElement::String {
750
                        value: "vars.env".to_string(),
751
                        source: "vars.env".to_source()
752
                    }],
753
                    SourceInfo {
754
                        start: Pos {
755
                            line: 1,
756
                            column: 17,
757
                        },
758
                        end: Pos {
759
                            line: 1,
760
                            column: 25,
761
                        },
762
                    }
763
                )),
764
                line_terminator0: LineTerminator {
765
                    space0: Whitespace {
766
                        value: String::new(),
767
                        source_info: SourceInfo {
768
                            start: Pos {
769
                                line: 1,
770
                                column: 25,
771
                            },
772
                            end: Pos {
773
                                line: 1,
774
                                column: 25,
775
                            },
776
                        },
777
                    },
778
                    comment: None,
779
                    newline: Whitespace {
780
                        value: String::new(),
781
                        source_info: SourceInfo {
782
                            start: Pos {
783
                                line: 1,
784
                                column: 25,
785
                            },
786
                            end: Pos {
787
                                line: 1,
788
                                column: 25,
789
                            },
790
                        },
791
                    },
792
                },
793
            }
794
        );
795
    }
796

            
797
    #[test]
798
    fn test_option_cacert_error() {
799
        let mut reader = Reader::new("cacert: ###");
800
        let error = parse(&mut reader).err().unwrap();
801
        assert!(!error.recoverable);
802
    }
803

            
804
    #[test]
805
    fn test_option_cert() {
806
        let mut reader = Reader::new("/etc/client-cert.pem #foo");
807

            
808
        assert_eq!(
809
            option_cert(&mut reader).unwrap(),
810
            OptionKind::ClientCert(Template::new(
811
                None,
812
                vec![TemplateElement::String {
813
                    value: "/etc/client-cert.pem".to_string(),
814
                    source: "/etc/client-cert.pem".to_source()
815
                }],
816
                SourceInfo {
817
                    start: Pos { line: 1, column: 1 },
818
                    end: Pos {
819
                        line: 1,
820
                        column: 21,
821
                    },
822
                },
823
            )),
824
        );
825
    }
826

            
827
    #[test]
828
    fn test_option_retry_error() {
829
        let mut reader = Reader::new("retry: ###");
830
        let error = parse(&mut reader).err().unwrap();
831
        assert!(!error.recoverable);
832
        assert_eq!(error.pos, Pos { line: 1, column: 8 });
833
        assert_eq!(
834
            error.kind,
835
            ParseErrorKind::Expecting {
836
                value: "integer >= -1".to_string()
837
            }
838
        );
839
    }
840

            
841
    #[test]
842
    fn test_variable_definition() {
843
        let mut reader = Reader::new("a=1");
844
        assert_eq!(
845
            variable_definition(&mut reader).unwrap(),
846
            VariableDefinition {
847
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4)),
848
                name: "a".to_string(),
849
                space0: Whitespace {
850
                    value: String::new(),
851
                    source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
852
                },
853
                space1: Whitespace {
854
                    value: String::new(),
855
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
856
                },
857
                value: VariableValue::Number(Number::Integer(I64::new(1, "1".to_source()))),
858
            }
859
        );
860
    }
861

            
862
    #[test]
863
    fn test_variable_value() {
864
        let mut reader = Reader::new("null");
865
        assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null);
866

            
867
        let mut reader = Reader::new("true");
868
        assert_eq!(
869
            variable_value(&mut reader).unwrap(),
870
            VariableValue::Bool(true)
871
        );
872

            
873
        let mut reader = Reader::new("1");
874
        assert_eq!(
875
            variable_value(&mut reader).unwrap(),
876
            VariableValue::Number(Number::Integer(I64::new(1, "1".to_source())))
877
        );
878

            
879
        let mut reader = Reader::new("toto");
880
        assert_eq!(
881
            variable_value(&mut reader).unwrap(),
882
            VariableValue::String(Template::new(
883
                None,
884
                vec![TemplateElement::String {
885
                    value: "toto".to_string(),
886
                    source: "toto".to_source(),
887
                }],
888
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
889
            ))
890
        );
891
        let mut reader = Reader::new("\"123\"");
892
        assert_eq!(
893
            variable_value(&mut reader).unwrap(),
894
            VariableValue::String(Template::new(
895
                Some('"'),
896
                vec![TemplateElement::String {
897
                    value: "123".to_string(),
898
                    source: "123".to_source(),
899
                }],
900
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6))
901
            ))
902
        );
903
    }
904

            
905
    #[test]
906
    fn test_variable_value_string_with_literal_prefix() {
907
        let mut reader = Reader::new("11aa");
908
        assert_eq!(
909
            variable_value(&mut reader).unwrap(),
910
            VariableValue::String(Template::new(
911
                None,
912
                vec![TemplateElement::String {
913
                    value: "11aa".to_string(),
914
                    source: "11aa".to_source(),
915
                }],
916
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
917
            ))
918
        );
919

            
920
        let mut reader = Reader::new("0.5x");
921
        assert_eq!(
922
            variable_value(&mut reader).unwrap(),
923
            VariableValue::String(Template::new(
924
                None,
925
                vec![TemplateElement::String {
926
                    value: "0.5x".to_string(),
927
                    source: "0.5x".to_source(),
928
                }],
929
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
930
            ))
931
        );
932

            
933
        let mut reader = Reader::new("true_is_true");
934
        assert_eq!(
935
            variable_value(&mut reader).unwrap(),
936
            VariableValue::String(Template::new(
937
                None,
938
                vec![TemplateElement::String {
939
                    value: "true_is_true".to_string(),
940
                    source: "true_is_true".to_source(),
941
                }],
942
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 13)),
943
            ))
944
        );
945

            
946
        let mut reader = Reader::new("nullable");
947
        assert_eq!(
948
            variable_value(&mut reader).unwrap(),
949
            VariableValue::String(Template::new(
950
                None,
951
                vec![TemplateElement::String {
952
                    value: "nullable".to_string(),
953
                    source: "nullable".to_source(),
954
                }],
955
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 9)),
956
            ))
957
        );
958
    }
959
}