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 crate::ast::{
19
    BooleanOption, CountOption, DurationOption, EntryOption, NaturalOption, OptionKind, SourceInfo,
20
    VariableDefinition, VariableValue, VerbosityOption, is_variable_reserved,
21
};
22
use crate::combinator::{choice, non_recover};
23
use crate::reader::Reader;
24
use crate::types::Count;
25

            
26
use super::duration::duration;
27
use super::error::{ParseError, ParseErrorKind};
28
use super::number::{integer, natural, number};
29
use super::primitives::{
30
    boolean, line_terminator, literal, null, optional_line_terminators, try_literal,
31
    zero_or_more_spaces,
32
};
33
use super::string::{quoted_template, unquoted_template};
34
use super::{ParseResult, filename, filename_password, placeholder};
35

            
36
/// Parse an option in an `[Options]` section.
37
6664
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
38
6664
    let line_terminators = optional_line_terminators(reader)?;
39
6664
    let space0 = zero_or_more_spaces(reader)?;
40
6664
    let start = reader.cursor();
41
    // We accept '_' even if there is no option name with this character. We do this to be able to
42
    // enter the parsing of the option name and to have better error description (ex: 'max-redirs'
43
    // vs 'max_redirs').
44
6664
    let option =
45
48516
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
46
6664
    let space1 = zero_or_more_spaces(reader)?;
47
6664
    try_literal(":", reader)?;
48
4524
    let space2 = zero_or_more_spaces(reader)?;
49
4524
    let kind = match option.as_str() {
50
4524
        "aws-sigv4" => option_aws_sigv4(reader)?,
51
4484
        "cacert" => option_cacert(reader)?,
52
4420
        "cert" => option_cert(reader)?,
53
4356
        "compressed" => option_compressed(reader)?,
54
4020
        "connect-to" => option_connect_to(reader)?,
55
3960
        "connect-timeout" => option_connect_timeout(reader)?,
56
3928
        "delay" => option_delay(reader)?,
57
3428
        "digest" => option_digest(reader)?,
58
3392
        "fail-with-body" => option_fail_with_boddy(reader)?,
59
3356
        "insecure" => option_insecure(reader)?,
60
3240
        "header" => option_header(reader)?,
61
3120
        "http1.0" => option_http_10(reader)?,
62
3016
        "http1.1" => option_http_11(reader)?,
63
2936
        "http2-prior-knowledge" => option_http_2_prior_knowledge(reader)?,
64
2904
        "http2" => option_http_2(reader)?,
65
2840
        "http3" => option_http_3(reader)?,
66
2808
        "ipv4" => option_ipv4(reader)?,
67
2776
        "ipv6" => option_ipv6(reader)?,
68
2744
        "key" => option_key(reader)?,
69
2696
        "limit-rate" => option_limit_rate(reader)?,
70
2660
        "location" => option_follow_location(reader)?,
71
2272
        "location-trusted" => option_follow_location_trusted(reader)?,
72
2224
        "max-redirs" => option_max_redirect(reader)?,
73
2132
        "max-time" => option_max_time(reader)?,
74
2100
        "negotiate" => option_negotiate(reader)?,
75
2060
        "netrc" => option_netrc(reader)?,
76
2028
        "netrc-file" => option_netrc_file(reader)?,
77
1992
        "netrc-optional" => option_netrc_optional(reader)?,
78
1960
        "no-header" => option_no_header(reader)?,
79
1908
        "no-jsonpath-coercion" => option_no_jsonpath_coercion(reader)?,
80
1884
        "ntlm" => option_ntlm(reader)?,
81
1840
        "output" => option_output(reader)?,
82
1684
        "path-as-is" => option_path_as_is(reader)?,
83
1648
        "pinnedpubkey" => option_pinned_pub_key(reader)?,
84
1608
        "proxy" => option_proxy(reader)?,
85
1520
        "repeat" => option_repeat(reader)?,
86
1428
        "resolve" => option_resolve(reader)?,
87
1380
        "retry" => option_retry(reader)?,
88
1228
        "retry-interval" => option_retry_interval(reader)?,
89
1112
        "skip" => option_skip(reader)?,
90
1060
        "tls-max" => option_tls_max(reader)?,
91
1016
        "tls-min" => option_tls_min(reader)?,
92
972
        "unix-socket" => option_unix_socket(reader)?,
93
940
        "user" => option_user(reader)?,
94
812
        "variable" => option_variable(reader)?,
95
248
        "verbose" => option_verbose(reader)?,
96
104
        "verbosity" => option_verbosity(reader)?,
97
56
        "very-verbose" => option_very_verbose(reader)?,
98
        _ => {
99
8
            return Err(ParseError::new(
100
8
                start.pos,
101
8
                false,
102
8
                ParseErrorKind::InvalidOption(option.to_string()),
103
8
            ));
104
        }
105
    };
106

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

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

            
123
64
fn option_cacert(reader: &mut Reader) -> ParseResult<OptionKind> {
124
64
    let value = filename::parse(reader)?;
125
60
    Ok(OptionKind::CaCertificate(value))
126
}
127

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

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

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

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

            
148
500
fn option_delay(reader: &mut Reader) -> ParseResult<OptionKind> {
149
500
    let value = duration_option(reader)?;
150
496
    Ok(OptionKind::Delay(value))
151
}
152

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
303
152
fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
304
152
    let value = non_recover(count_option, reader)?;
305
148
    Ok(OptionKind::Retry(value))
306
}
307

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

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

            
318
44
fn option_tls_max(reader: &mut Reader) -> ParseResult<OptionKind> {
319
44
    let value = unquoted_template(reader)?;
320
44
    Ok(OptionKind::TlsMax(value))
321
}
322

            
323
44
fn option_tls_min(reader: &mut Reader) -> ParseResult<OptionKind> {
324
44
    let value = unquoted_template(reader)?;
325
44
    Ok(OptionKind::TlsMin(value))
326
}
327

            
328
128
fn option_user(reader: &mut Reader) -> ParseResult<OptionKind> {
329
128
    let value = unquoted_template(reader)?;
330
128
    Ok(OptionKind::User(value))
331
}
332

            
333
32
fn option_unix_socket(reader: &mut Reader) -> ParseResult<OptionKind> {
334
32
    let value = unquoted_template(reader)?;
335
32
    Ok(OptionKind::UnixSocket(value))
336
}
337

            
338
564
fn option_variable(reader: &mut Reader) -> ParseResult<OptionKind> {
339
564
    let value = variable_definition(reader)?;
340
560
    Ok(OptionKind::Variable(value))
341
}
342

            
343
144
fn option_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
344
144
    let value = non_recover(boolean_option, reader)?;
345
144
    Ok(OptionKind::Verbose(value))
346
}
347

            
348
48
fn option_verbosity(reader: &mut Reader) -> ParseResult<OptionKind> {
349
48
    let start = reader.cursor();
350
288
    let name = reader.read_while(|c| c.is_ascii_alphabetic());
351
48
    match name.as_str() {
352
48
        "brief" => Ok(OptionKind::Verbosity(VerbosityOption::Brief)),
353
12
        "verbose" => Ok(OptionKind::Verbosity(VerbosityOption::Verbose)),
354
8
        "debug" => Ok(OptionKind::Verbosity(VerbosityOption::Debug)),
355
        _ => {
356
4
            reader.seek(start);
357
4
            let kind = ParseErrorKind::Expecting {
358
4
                value: "brief|verbose|debug".to_string(),
359
4
            };
360
4
            Err(ParseError::new(start.pos, false, kind))
361
        }
362
    }
363
}
364

            
365
48
fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
366
48
    let value = non_recover(boolean_option, reader)?;
367
48
    Ok(OptionKind::VeryVerbose(value))
368
}
369

            
370
336
fn count(reader: &mut Reader) -> ParseResult<Count> {
371
336
    let start = reader.cursor();
372
336
    let value = non_recover(integer, reader)?;
373
244
    if value.as_i64() == -1 {
374
40
        Ok(Count::Infinite)
375
204
    } else if value.as_i64() >= 0 {
376
200
        Ok(Count::Finite(value.as_i64() as usize))
377
    } else {
378
4
        let kind = ParseErrorKind::Expecting {
379
4
            value: "Expecting a count value".to_string(),
380
4
        };
381
4
        Err(ParseError::new(start.pos, false, kind))
382
    }
383
}
384

            
385
1788
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
386
1788
    let start = reader.cursor();
387
1788
    match boolean(reader) {
388
1420
        Ok(v) => Ok(BooleanOption::Literal(v)),
389
        Err(_) => {
390
368
            reader.seek(start);
391
368
            let exp = placeholder::parse(reader).map_err(|e| {
392
4
                let kind = ParseErrorKind::Expecting {
393
4
                    value: "true|false".to_string(),
394
4
                };
395
4
                ParseError::new(e.pos, false, kind)
396
4
            })?;
397
364
            Ok(BooleanOption::Placeholder(exp))
398
        }
399
    }
400
}
401

            
402
36
fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
403
36
    let start = reader.cursor();
404
36
    match natural(reader) {
405
20
        Ok(v) => Ok(NaturalOption::Literal(v)),
406
        Err(_) => {
407
16
            reader.seek(start);
408
16
            let placeholder = placeholder::parse(reader).map_err(|e| {
409
                let kind = ParseErrorKind::Expecting {
410
                    value: "integer >= 0".to_string(),
411
                };
412
                ParseError::new(e.pos, false, kind)
413
            })?;
414
16
            Ok(NaturalOption::Placeholder(placeholder))
415
        }
416
    }
417
}
418

            
419
336
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
420
336
    let start = reader.cursor();
421
336
    match count(reader) {
422
240
        Ok(v) => Ok(CountOption::Literal(v)),
423
        Err(_) => {
424
96
            reader.seek(start);
425
96
            let placeholder = placeholder::parse(reader).map_err(|e| {
426
4
                let kind = ParseErrorKind::Expecting {
427
4
                    value: "integer >= -1".to_string(),
428
4
                };
429
4
                ParseError::new(e.pos, false, kind)
430
4
            })?;
431
92
            Ok(CountOption::Placeholder(placeholder))
432
        }
433
    }
434
}
435

            
436
680
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
437
680
    let start = reader.cursor();
438
680
    match duration(reader) {
439
608
        Ok(v) => Ok(DurationOption::Literal(v)),
440
72
        Err(e) => {
441
72
            if e.recoverable {
442
68
                reader.seek(start);
443
68
                let placeholder = placeholder::parse(reader).map_err(|e| {
444
                    let kind = ParseErrorKind::Expecting {
445
                        value: "integer".to_string(),
446
                    };
447
                    ParseError::new(e.pos, false, kind)
448
                })?;
449
68
                Ok(DurationOption::Placeholder(placeholder))
450
            } else {
451
4
                Err(e)
452
            }
453
        }
454
    }
455
}
456

            
457
564
fn variable_definition(reader: &mut Reader) -> ParseResult<VariableDefinition> {
458
564
    let start = reader.cursor();
459
564
    let name = variable_name(reader)?;
460
560
    let space0 = zero_or_more_spaces(reader)?;
461
560
    literal("=", reader)?;
462
560
    let space1 = zero_or_more_spaces(reader)?;
463
560
    let value = variable_value(reader)?;
464
560
    let end = reader.cursor();
465
560
    let source_info = SourceInfo {
466
560
        start: start.pos,
467
560
        end: end.pos,
468
560
    };
469
560
    Ok(VariableDefinition {
470
560
        source_info,
471
560
        name,
472
560
        space0,
473
560
        space1,
474
560
        value,
475
560
    })
476
}
477

            
478
564
fn variable_name(reader: &mut Reader) -> ParseResult<String> {
479
564
    let start = reader.cursor();
480
3364
    let name = reader.read_while(|c| c.is_alphanumeric() || c == '_' || c == '-');
481
564
    if name.is_empty() {
482
        let kind = ParseErrorKind::Expecting {
483
            value: "variable name".to_string(),
484
        };
485
        return Err(ParseError::new(start.pos, false, kind));
486
564
    } else if is_variable_reserved(&name) {
487
4
        let kind = ParseErrorKind::Variable(format!(
488
4
            "conflicts with the {name} function, use a different name"
489
4
        ));
490
4
        return Err(ParseError::new(start.pos, false, kind));
491
    }
492
560
    Ok(name)
493
}
494

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

            
510
560
fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
511
560
    choice(
512
        &[
513
560
            |p1| match null(p1) {
514
16
                Ok(()) => literal_value(p1, VariableValue::Null),
515
544
                Err(e) => Err(e),
516
560
            },
517
544
            |p1| match boolean(p1) {
518
20
                Ok(value) => literal_value(p1, VariableValue::Bool(value)),
519
524
                Err(e) => Err(e),
520
544
            },
521
524
            |p1| match number(p1) {
522
196
                Ok(value) => literal_value(p1, VariableValue::Number(value)),
523
328
                Err(e) => Err(e),
524
524
            },
525
328
            |p1| match quoted_template(p1) {
526
                Ok(value) => Ok(VariableValue::String(value)),
527
328
                Err(e) => Err(e),
528
328
            },
529
328
            |p1| match unquoted_template(p1) {
530
328
                Ok(value) => Ok(VariableValue::String(value)),
531
                Err(e) => Err(e),
532
328
            },
533
        ],
534
560
        reader,
535
    )
536
560
    .map_err(|e| {
537
        let kind = ParseErrorKind::Expecting {
538
            value: "variable value".to_string(),
539
        };
540
        ParseError::new(e.pos, false, kind)
541
    })
542
}
543

            
544
#[cfg(test)]
545
mod tests {
546
    use super::*;
547
    use crate::ast::{I64, LineTerminator, Number, Template, TemplateElement, Whitespace};
548
    use crate::reader::Pos;
549
    use crate::types::ToSource;
550

            
551
    #[test]
552
    fn test_option_insecure() {
553
        let mut reader = Reader::new("insecure: true");
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: 9 },
570
                        end: Pos { line: 1, column: 9 },
571
                    },
572
                },
573
                space2: Whitespace {
574
                    value: " ".to_string(),
575
                    source_info: SourceInfo {
576
                        start: Pos {
577
                            line: 1,
578
                            column: 10,
579
                        },
580
                        end: Pos {
581
                            line: 1,
582
                            column: 11,
583
                        },
584
                    },
585
                },
586
                kind: OptionKind::Insecure(BooleanOption::Literal(true)),
587
                line_terminator0: LineTerminator {
588
                    space0: Whitespace {
589
                        value: String::new(),
590
                        source_info: SourceInfo {
591
                            start: Pos {
592
                                line: 1,
593
                                column: 15,
594
                            },
595
                            end: Pos {
596
                                line: 1,
597
                                column: 15,
598
                            },
599
                        },
600
                    },
601
                    comment: None,
602
                    newline: Whitespace {
603
                        value: String::new(),
604
                        source_info: SourceInfo {
605
                            start: Pos {
606
                                line: 1,
607
                                column: 15,
608
                            },
609
                            end: Pos {
610
                                line: 1,
611
                                column: 15,
612
                            },
613
                        },
614
                    },
615
                },
616
            }
617
        );
618
    }
619

            
620
    #[test]
621
    fn test_option_insecure_error() {
622
        let mut reader = Reader::new("insecure: error");
623
        let error = parse(&mut reader).err().unwrap();
624
        assert!(!error.recoverable);
625
    }
626

            
627
    #[test]
628
    fn test_option_no_jsonpath_coercion() {
629
        let mut reader = Reader::new("no-jsonpath-coercion: true");
630
        let option = parse(&mut reader).unwrap();
631
        assert_eq!(
632
            option.kind,
633
            OptionKind::NoJsonpathCoercion(BooleanOption::Literal(true))
634
        );
635
    }
636

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

            
713
    #[test]
714
    fn test_option_cacert_error() {
715
        let mut reader = Reader::new("cacert: ###");
716
        let error = parse(&mut reader).err().unwrap();
717
        assert!(!error.recoverable);
718
    }
719

            
720
    #[test]
721
    fn test_option_cert() {
722
        let mut reader = Reader::new("/etc/client-cert.pem #foo");
723

            
724
        assert_eq!(
725
            option_cert(&mut reader).unwrap(),
726
            OptionKind::ClientCert(Template::new(
727
                None,
728
                vec![TemplateElement::String {
729
                    value: "/etc/client-cert.pem".to_string(),
730
                    source: "/etc/client-cert.pem".to_source()
731
                }],
732
                SourceInfo {
733
                    start: Pos { line: 1, column: 1 },
734
                    end: Pos {
735
                        line: 1,
736
                        column: 21,
737
                    },
738
                },
739
            )),
740
        );
741
    }
742

            
743
    #[test]
744
    fn test_option_retry_error() {
745
        let mut reader = Reader::new("retry: ###");
746
        let error = parse(&mut reader).err().unwrap();
747
        assert!(!error.recoverable);
748
        assert_eq!(error.pos, Pos { line: 1, column: 8 });
749
        assert_eq!(
750
            error.kind,
751
            ParseErrorKind::Expecting {
752
                value: "integer >= -1".to_string()
753
            }
754
        );
755
    }
756

            
757
    #[test]
758
    fn test_variable_definition() {
759
        let mut reader = Reader::new("a=1");
760
        assert_eq!(
761
            variable_definition(&mut reader).unwrap(),
762
            VariableDefinition {
763
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4)),
764
                name: "a".to_string(),
765
                space0: Whitespace {
766
                    value: String::new(),
767
                    source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
768
                },
769
                space1: Whitespace {
770
                    value: String::new(),
771
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
772
                },
773
                value: VariableValue::Number(Number::Integer(I64::new(1, "1".to_source()))),
774
            }
775
        );
776
    }
777

            
778
    #[test]
779
    fn test_variable_value() {
780
        let mut reader = Reader::new("null");
781
        assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null);
782

            
783
        let mut reader = Reader::new("true");
784
        assert_eq!(
785
            variable_value(&mut reader).unwrap(),
786
            VariableValue::Bool(true)
787
        );
788

            
789
        let mut reader = Reader::new("1");
790
        assert_eq!(
791
            variable_value(&mut reader).unwrap(),
792
            VariableValue::Number(Number::Integer(I64::new(1, "1".to_source())))
793
        );
794

            
795
        let mut reader = Reader::new("toto");
796
        assert_eq!(
797
            variable_value(&mut reader).unwrap(),
798
            VariableValue::String(Template::new(
799
                None,
800
                vec![TemplateElement::String {
801
                    value: "toto".to_string(),
802
                    source: "toto".to_source(),
803
                }],
804
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
805
            ))
806
        );
807
        let mut reader = Reader::new("\"123\"");
808
        assert_eq!(
809
            variable_value(&mut reader).unwrap(),
810
            VariableValue::String(Template::new(
811
                Some('"'),
812
                vec![TemplateElement::String {
813
                    value: "123".to_string(),
814
                    source: "123".to_source(),
815
                }],
816
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6))
817
            ))
818
        );
819
    }
820

            
821
    #[test]
822
    fn test_variable_value_string_with_literal_prefix() {
823
        let mut reader = Reader::new("11aa");
824
        assert_eq!(
825
            variable_value(&mut reader).unwrap(),
826
            VariableValue::String(Template::new(
827
                None,
828
                vec![TemplateElement::String {
829
                    value: "11aa".to_string(),
830
                    source: "11aa".to_source(),
831
                }],
832
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
833
            ))
834
        );
835

            
836
        let mut reader = Reader::new("0.5x");
837
        assert_eq!(
838
            variable_value(&mut reader).unwrap(),
839
            VariableValue::String(Template::new(
840
                None,
841
                vec![TemplateElement::String {
842
                    value: "0.5x".to_string(),
843
                    source: "0.5x".to_source(),
844
                }],
845
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
846
            ))
847
        );
848

            
849
        let mut reader = Reader::new("true_is_true");
850
        assert_eq!(
851
            variable_value(&mut reader).unwrap(),
852
            VariableValue::String(Template::new(
853
                None,
854
                vec![TemplateElement::String {
855
                    value: "true_is_true".to_string(),
856
                    source: "true_is_true".to_source(),
857
                }],
858
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 13)),
859
            ))
860
        );
861

            
862
        let mut reader = Reader::new("nullable");
863
        assert_eq!(
864
            variable_value(&mut reader).unwrap(),
865
            VariableValue::String(Template::new(
866
                None,
867
                vec![TemplateElement::String {
868
                    value: "nullable".to_string(),
869
                    source: "nullable".to_source(),
870
                }],
871
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 9)),
872
            ))
873
        );
874
    }
875
}