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
7785
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
37
7785
    let line_terminators = optional_line_terminators(reader)?;
38
7785
    let space0 = zero_or_more_spaces(reader)?;
39
7785
    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
7785
    let option =
44
57657
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
45
7785
    let space1 = zero_or_more_spaces(reader)?;
46
7785
    try_literal(":", reader)?;
47
5300
    let space2 = zero_or_more_spaces(reader)?;
48
5300
    let kind = match option.as_str() {
49
5300
        "aws-sigv4" => option_aws_sigv4(reader)?,
50
5250
        "cacert" => option_cacert(reader)?,
51
5170
        "cert" => option_cert(reader)?,
52
5090
        "compressed" => option_compressed(reader)?,
53
4670
        "connect-to" => option_connect_to(reader)?,
54
4595
        "connect-timeout" => option_connect_timeout(reader)?,
55
4555
        "delay" => option_delay(reader)?,
56
3930
        "digest" => option_digest(reader)?,
57
3885
        "fail-with-body" => option_fail_with_boddy(reader)?,
58
3840
        "insecure" => option_insecure(reader)?,
59
3725
        "header" => option_header(reader)?,
60
3635
        "http1.0" => option_http_10(reader)?,
61
3505
        "http1.1" => option_http_11(reader)?,
62
3405
        "http2" => option_http_2(reader)?,
63
3345
        "http3" => option_http_3(reader)?,
64
3305
        "ipv4" => option_ipv4(reader)?,
65
3265
        "ipv6" => option_ipv6(reader)?,
66
3225
        "key" => option_key(reader)?,
67
3165
        "limit-rate" => option_limit_rate(reader)?,
68
3120
        "location" => option_follow_location(reader)?,
69
2635
        "location-trusted" => option_follow_location_trusted(reader)?,
70
2575
        "max-redirs" => option_max_redirect(reader)?,
71
2460
        "max-time" => option_max_time(reader)?,
72
2420
        "negotiate" => option_negotiate(reader)?,
73
2370
        "netrc" => option_netrc(reader)?,
74
2330
        "netrc-file" => option_netrc_file(reader)?,
75
2285
        "netrc-optional" => option_netrc_optional(reader)?,
76
2245
        "no-header" => option_no_header(reader)?,
77
2180
        "ntlm" => option_ntlm(reader)?,
78
2125
        "output" => option_output(reader)?,
79
1950
        "path-as-is" => option_path_as_is(reader)?,
80
1905
        "pinnedpubkey" => option_pinned_pub_key(reader)?,
81
1855
        "proxy" => option_proxy(reader)?,
82
1775
        "repeat" => option_repeat(reader)?,
83
1660
        "resolve" => option_resolve(reader)?,
84
1600
        "retry" => option_retry(reader)?,
85
1410
        "retry-interval" => option_retry_interval(reader)?,
86
1265
        "skip" => option_skip(reader)?,
87
1215
        "unix-socket" => option_unix_socket(reader)?,
88
1175
        "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
5260
    let line_terminator0 = line_terminator(reader)?;
103
5260
    Ok(EntryOption {
104
5260
        line_terminators,
105
5260
        space0,
106
5260
        space1,
107
5260
        space2,
108
5260
        kind,
109
5260
        line_terminator0,
110
5260
    })
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
485
fn option_follow_location(reader: &mut Reader) -> ParseResult<OptionKind> {
159
485
    let value = non_recover(boolean_option, reader)?;
160
485
    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
115
fn option_insecure(reader: &mut Reader) -> ParseResult<OptionKind> {
194
115
    let value = non_recover(boolean_option, reader)?;
195
110
    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
190
fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
289
190
    let value = non_recover(count_option, reader)?;
290
185
    Ok(OptionKind::Retry(value))
291
}
292

            
293
145
fn option_retry_interval(reader: &mut Reader) -> ParseResult<OptionKind> {
294
145
    let value = non_recover(duration_option, reader)?;
295
145
    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
160
fn option_user(reader: &mut Reader) -> ParseResult<OptionKind> {
304
160
    let value = unquoted_template(reader)?;
305
160
    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
420
fn count(reader: &mut Reader) -> ParseResult<Count> {
346
420
    let start = reader.cursor();
347
420
    let value = non_recover(integer, reader)?;
348
305
    if value.as_i64() == -1 {
349
50
        Ok(Count::Infinite)
350
255
    } else if value.as_i64() >= 0 {
351
250
        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
2100
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
361
2100
    let start = reader.cursor();
362
2100
    match boolean(reader) {
363
1655
        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
420
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
395
420
    let start = reader.cursor();
396
420
    match count(reader) {
397
300
        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
850
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
412
850
    let start = reader.cursor();
413
850
    match duration(reader) {
414
760
        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
// `null`, a boolean or a number is only a variable value when the literal spans the whole
471
// value. When more characters follow (as in `11aa` or `true_is_true`), the literal is just
472
// the start of an unquoted string, so return a recoverable error to fall back to it.
473
290
fn literal_value<T>(reader: &mut Reader, value: T) -> ParseResult<T> {
474
290
    match reader.peek() {
475
290
        None | Some(' ' | '\t' | '\r' | '\n' | '#') => Ok(value),
476
        Some(_) => {
477
            let kind = ParseErrorKind::Expecting {
478
                value: "variable value".to_string(),
479
            };
480
            Err(ParseError::new(reader.cursor().pos, true, kind))
481
        }
482
    }
483
}
484

            
485
700
fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
486
700
    choice(
487
        &[
488
700
            |p1| match null(p1) {
489
20
                Ok(()) => literal_value(p1, VariableValue::Null),
490
680
                Err(e) => Err(e),
491
700
            },
492
680
            |p1| match boolean(p1) {
493
25
                Ok(value) => literal_value(p1, VariableValue::Bool(value)),
494
655
                Err(e) => Err(e),
495
680
            },
496
655
            |p1| match number(p1) {
497
245
                Ok(value) => literal_value(p1, VariableValue::Number(value)),
498
410
                Err(e) => Err(e),
499
655
            },
500
410
            |p1| match quoted_template(p1) {
501
                Ok(value) => Ok(VariableValue::String(value)),
502
410
                Err(e) => Err(e),
503
410
            },
504
410
            |p1| match unquoted_template(p1) {
505
410
                Ok(value) => Ok(VariableValue::String(value)),
506
                Err(e) => Err(e),
507
410
            },
508
        ],
509
700
        reader,
510
    )
511
700
    .map_err(|e| {
512
        let kind = ParseErrorKind::Expecting {
513
            value: "variable value".to_string(),
514
        };
515
        ParseError::new(e.pos, false, kind)
516
    })
517
}
518

            
519
#[cfg(test)]
520
mod tests {
521
    use super::*;
522
    use crate::ast::{I64, LineTerminator, Number, Template, TemplateElement, Whitespace};
523
    use crate::reader::Pos;
524
    use crate::types::ToSource;
525

            
526
    #[test]
527
    fn test_option_insecure() {
528
        let mut reader = Reader::new("insecure: true");
529
        let option = parse(&mut reader).unwrap();
530
        assert_eq!(
531
            option,
532
            EntryOption {
533
                line_terminators: vec![],
534
                space0: Whitespace {
535
                    value: String::new(),
536
                    source_info: SourceInfo {
537
                        start: Pos { line: 1, column: 1 },
538
                        end: Pos { line: 1, column: 1 },
539
                    },
540
                },
541
                space1: Whitespace {
542
                    value: String::new(),
543
                    source_info: SourceInfo {
544
                        start: Pos { line: 1, column: 9 },
545
                        end: Pos { line: 1, column: 9 },
546
                    },
547
                },
548
                space2: Whitespace {
549
                    value: " ".to_string(),
550
                    source_info: SourceInfo {
551
                        start: Pos {
552
                            line: 1,
553
                            column: 10,
554
                        },
555
                        end: Pos {
556
                            line: 1,
557
                            column: 11,
558
                        },
559
                    },
560
                },
561
                kind: OptionKind::Insecure(BooleanOption::Literal(true)),
562
                line_terminator0: LineTerminator {
563
                    space0: Whitespace {
564
                        value: String::new(),
565
                        source_info: SourceInfo {
566
                            start: Pos {
567
                                line: 1,
568
                                column: 15,
569
                            },
570
                            end: Pos {
571
                                line: 1,
572
                                column: 15,
573
                            },
574
                        },
575
                    },
576
                    comment: None,
577
                    newline: Whitespace {
578
                        value: String::new(),
579
                        source_info: SourceInfo {
580
                            start: Pos {
581
                                line: 1,
582
                                column: 15,
583
                            },
584
                            end: Pos {
585
                                line: 1,
586
                                column: 15,
587
                            },
588
                        },
589
                    },
590
                },
591
            }
592
        );
593
    }
594

            
595
    #[test]
596
    fn test_option_insecure_error() {
597
        let mut reader = Reader::new("insecure: error");
598
        let error = parse(&mut reader).err().unwrap();
599
        assert!(!error.recoverable);
600
    }
601

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

            
678
    #[test]
679
    fn test_option_cacert_error() {
680
        let mut reader = Reader::new("cacert: ###");
681
        let error = parse(&mut reader).err().unwrap();
682
        assert!(!error.recoverable);
683
    }
684

            
685
    #[test]
686
    fn test_option_cert() {
687
        let mut reader = Reader::new("/etc/client-cert.pem #foo");
688

            
689
        assert_eq!(
690
            option_cert(&mut reader).unwrap(),
691
            OptionKind::ClientCert(Template::new(
692
                None,
693
                vec![TemplateElement::String {
694
                    value: "/etc/client-cert.pem".to_string(),
695
                    source: "/etc/client-cert.pem".to_source()
696
                }],
697
                SourceInfo {
698
                    start: Pos { line: 1, column: 1 },
699
                    end: Pos {
700
                        line: 1,
701
                        column: 21,
702
                    },
703
                },
704
            )),
705
        );
706
    }
707

            
708
    #[test]
709
    fn test_option_retry_error() {
710
        let mut reader = Reader::new("retry: ###");
711
        let error = parse(&mut reader).err().unwrap();
712
        assert!(!error.recoverable);
713
        assert_eq!(error.pos, Pos { line: 1, column: 8 });
714
        assert_eq!(
715
            error.kind,
716
            ParseErrorKind::Expecting {
717
                value: "integer >= -1".to_string()
718
            }
719
        );
720
    }
721

            
722
    #[test]
723
    fn test_variable_definition() {
724
        let mut reader = Reader::new("a=1");
725
        assert_eq!(
726
            variable_definition(&mut reader).unwrap(),
727
            VariableDefinition {
728
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4)),
729
                name: "a".to_string(),
730
                space0: Whitespace {
731
                    value: String::new(),
732
                    source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
733
                },
734
                space1: Whitespace {
735
                    value: String::new(),
736
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
737
                },
738
                value: VariableValue::Number(Number::Integer(I64::new(1, "1".to_source()))),
739
            }
740
        );
741
    }
742

            
743
    #[test]
744
    fn test_variable_value() {
745
        let mut reader = Reader::new("null");
746
        assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null);
747

            
748
        let mut reader = Reader::new("true");
749
        assert_eq!(
750
            variable_value(&mut reader).unwrap(),
751
            VariableValue::Bool(true)
752
        );
753

            
754
        let mut reader = Reader::new("1");
755
        assert_eq!(
756
            variable_value(&mut reader).unwrap(),
757
            VariableValue::Number(Number::Integer(I64::new(1, "1".to_source())))
758
        );
759

            
760
        let mut reader = Reader::new("toto");
761
        assert_eq!(
762
            variable_value(&mut reader).unwrap(),
763
            VariableValue::String(Template::new(
764
                None,
765
                vec![TemplateElement::String {
766
                    value: "toto".to_string(),
767
                    source: "toto".to_source(),
768
                }],
769
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
770
            ))
771
        );
772
        let mut reader = Reader::new("\"123\"");
773
        assert_eq!(
774
            variable_value(&mut reader).unwrap(),
775
            VariableValue::String(Template::new(
776
                Some('"'),
777
                vec![TemplateElement::String {
778
                    value: "123".to_string(),
779
                    source: "123".to_source(),
780
                }],
781
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6))
782
            ))
783
        );
784
    }
785

            
786
    #[test]
787
    fn test_variable_value_string_with_literal_prefix() {
788
        let mut reader = Reader::new("11aa");
789
        assert_eq!(
790
            variable_value(&mut reader).unwrap(),
791
            VariableValue::String(Template::new(
792
                None,
793
                vec![TemplateElement::String {
794
                    value: "11aa".to_string(),
795
                    source: "11aa".to_source(),
796
                }],
797
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
798
            ))
799
        );
800

            
801
        let mut reader = Reader::new("0.5x");
802
        assert_eq!(
803
            variable_value(&mut reader).unwrap(),
804
            VariableValue::String(Template::new(
805
                None,
806
                vec![TemplateElement::String {
807
                    value: "0.5x".to_string(),
808
                    source: "0.5x".to_source(),
809
                }],
810
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
811
            ))
812
        );
813

            
814
        let mut reader = Reader::new("true_is_true");
815
        assert_eq!(
816
            variable_value(&mut reader).unwrap(),
817
            VariableValue::String(Template::new(
818
                None,
819
                vec![TemplateElement::String {
820
                    value: "true_is_true".to_string(),
821
                    source: "true_is_true".to_source(),
822
                }],
823
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 13)),
824
            ))
825
        );
826

            
827
        let mut reader = Reader::new("nullable");
828
        assert_eq!(
829
            variable_value(&mut reader).unwrap(),
830
            VariableValue::String(Template::new(
831
                None,
832
                vec![TemplateElement::String {
833
                    value: "nullable".to_string(),
834
                    source: "nullable".to_source(),
835
                }],
836
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 9)),
837
            ))
838
        );
839
    }
840
}