1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2024 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
    is_variable_reserved, BooleanOption, CountOption, DurationOption, EntryOption, NaturalOption,
20
    OptionKind, SourceInfo, VariableDefinition, VariableValue,
21
};
22
use crate::combinator::{choice, non_recover};
23
use crate::parser::duration::duration;
24
use crate::parser::number::{integer, natural, number};
25
use crate::parser::primitives::{
26
    boolean, line_terminator, literal, null, optional_line_terminators, try_literal,
27
    zero_or_more_spaces,
28
};
29
use crate::parser::string::{quoted_template, unquoted_template};
30
use crate::parser::{filename, filename_password, ParseError, ParseErrorKind, ParseResult};
31
use crate::reader::Reader;
32
use crate::typing::Count;
33

            
34
use super::placeholder;
35

            
36
/// Parse an option in an `[Options]` section.
37
4985
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
38
4985
    let line_terminators = optional_line_terminators(reader)?;
39
4985
    let space0 = zero_or_more_spaces(reader)?;
40
4985
    let start = reader.cursor();
41
4985
    // We accept '_' even if there is no option name with this character. We do this to be able to
42
4985
    // enter the parsing of the option name and to have better error description (ex: 'max-redirs'
43
4985
    // vs 'max_redirs').
44
4985
    let option =
45
38662
        reader.read_while(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '_');
46
4985
    let space1 = zero_or_more_spaces(reader)?;
47
4985
    try_literal(":", reader)?;
48
3415
    let space2 = zero_or_more_spaces(reader)?;
49
3415
    let kind = match option.as_str() {
50
3415
        "aws-sigv4" => option_aws_sigv4(reader)?,
51
3375
        "cacert" => option_cacert(reader)?,
52
3315
        "cert" => option_cert(reader)?,
53
3250
        "compressed" => option_compressed(reader)?,
54
2840
        "connect-to" => option_connect_to(reader)?,
55
2775
        "connect-timeout" => option_connect_timeout(reader)?,
56
2745
        "delay" => option_delay(reader)?,
57
2650
        "insecure" => option_insecure(reader)?,
58
2555
        "http1.0" => option_http_10(reader)?,
59
2450
        "http1.1" => option_http_11(reader)?,
60
2375
        "http2" => option_http_2(reader)?,
61
2335
        "http3" => option_http_3(reader)?,
62
2305
        "ipv4" => option_ipv4(reader)?,
63
2275
        "ipv6" => option_ipv6(reader)?,
64
2245
        "key" => option_key(reader)?,
65
2195
        "limit-rate" => option_limit_rate(reader)?,
66
2160
        "location" => option_follow_location(reader)?,
67
1760
        "location-trusted" => option_follow_location_trusted(reader)?,
68
1710
        "max-redirs" => option_max_redirect(reader)?,
69
1605
        "netrc" => option_netrc(reader)?,
70
1575
        "netrc-file" => option_netrc_file(reader)?,
71
1540
        "netrc-optional" => option_netrc_optional(reader)?,
72
1510
        "output" => option_output(reader)?,
73
1380
        "path-as-is" => option_path_as_is(reader)?,
74
1345
        "proxy" => option_proxy(reader)?,
75
1275
        "repeat" => option_repeat(reader)?,
76
1195
        "resolve" => option_resolve(reader)?,
77
1145
        "retry" => option_retry(reader)?,
78
995
        "retry-interval" => option_retry_interval(reader)?,
79
885
        "skip" => option_skip(reader)?,
80
845
        "unix-socket" => option_unix_socket(reader)?,
81
815
        "user" => option_user(reader)?,
82
715
        "variable" => option_variable(reader)?,
83
155
        "verbose" => option_verbose(reader)?,
84
60
        "very-verbose" => option_very_verbose(reader)?,
85
        _ => {
86
10
            return Err(ParseError::new(
87
10
                start.pos,
88
10
                false,
89
10
                ParseErrorKind::InvalidOption(option.to_string()),
90
10
            ))
91
        }
92
    };
93

            
94
3380
    let line_terminator0 = line_terminator(reader)?;
95
3380
    Ok(EntryOption {
96
3380
        line_terminators,
97
3380
        space0,
98
3380
        space1,
99
3380
        space2,
100
3380
        kind,
101
3380
        line_terminator0,
102
3380
    })
103
}
104

            
105
40
fn option_aws_sigv4(reader: &mut Reader) -> ParseResult<OptionKind> {
106
40
    let value = unquoted_template(reader)?;
107
40
    Ok(OptionKind::AwsSigV4(value))
108
}
109

            
110
60
fn option_cacert(reader: &mut Reader) -> ParseResult<OptionKind> {
111
60
    let value = filename::parse(reader)?;
112
55
    Ok(OptionKind::CaCertificate(value))
113
}
114

            
115
65
fn option_cert(reader: &mut Reader) -> ParseResult<OptionKind> {
116
65
    let value = filename_password::parse(reader)?;
117
65
    Ok(OptionKind::ClientCert(value))
118
}
119

            
120
410
fn option_compressed(reader: &mut Reader) -> ParseResult<OptionKind> {
121
410
    let value = non_recover(boolean_option, reader)?;
122
410
    Ok(OptionKind::Compressed(value))
123
}
124

            
125
65
fn option_connect_to(reader: &mut Reader) -> ParseResult<OptionKind> {
126
65
    let value = unquoted_template(reader)?;
127
65
    Ok(OptionKind::ConnectTo(value))
128
}
129

            
130
30
fn option_connect_timeout(reader: &mut Reader) -> ParseResult<OptionKind> {
131
30
    let value = duration_option(reader)?;
132
30
    Ok(OptionKind::ConnectTimeout(value))
133
}
134

            
135
95
fn option_delay(reader: &mut Reader) -> ParseResult<OptionKind> {
136
95
    let value = duration_option(reader)?;
137
90
    Ok(OptionKind::Delay(value))
138
}
139

            
140
400
fn option_follow_location(reader: &mut Reader) -> ParseResult<OptionKind> {
141
400
    let value = non_recover(boolean_option, reader)?;
142
400
    Ok(OptionKind::FollowLocation(value))
143
}
144

            
145
50
fn option_follow_location_trusted(reader: &mut Reader) -> ParseResult<OptionKind> {
146
50
    let value = non_recover(boolean_option, reader)?;
147
50
    Ok(OptionKind::FollowLocationTrusted(value))
148
}
149

            
150
105
fn option_http_10(reader: &mut Reader) -> ParseResult<OptionKind> {
151
105
    let value = non_recover(boolean_option, reader)?;
152
105
    Ok(OptionKind::Http10(value))
153
}
154

            
155
75
fn option_http_11(reader: &mut Reader) -> ParseResult<OptionKind> {
156
75
    let value = non_recover(boolean_option, reader)?;
157
75
    Ok(OptionKind::Http11(value))
158
}
159

            
160
40
fn option_http_2(reader: &mut Reader) -> ParseResult<OptionKind> {
161
40
    let value = non_recover(boolean_option, reader)?;
162
40
    Ok(OptionKind::Http2(value))
163
}
164

            
165
30
fn option_http_3(reader: &mut Reader) -> ParseResult<OptionKind> {
166
30
    let value = non_recover(boolean_option, reader)?;
167
30
    Ok(OptionKind::Http3(value))
168
}
169

            
170
95
fn option_insecure(reader: &mut Reader) -> ParseResult<OptionKind> {
171
95
    let value = non_recover(boolean_option, reader)?;
172
90
    Ok(OptionKind::Insecure(value))
173
}
174

            
175
30
fn option_ipv4(reader: &mut Reader) -> ParseResult<OptionKind> {
176
30
    let value = non_recover(boolean_option, reader)?;
177
30
    Ok(OptionKind::IpV4(value))
178
}
179

            
180
30
fn option_ipv6(reader: &mut Reader) -> ParseResult<OptionKind> {
181
30
    let value = non_recover(boolean_option, reader)?;
182
30
    Ok(OptionKind::IpV6(value))
183
}
184

            
185
50
fn option_key(reader: &mut Reader) -> ParseResult<OptionKind> {
186
50
    let value = filename::parse(reader)?;
187
50
    Ok(OptionKind::ClientKey(value))
188
}
189

            
190
35
fn option_limit_rate(reader: &mut Reader) -> ParseResult<OptionKind> {
191
35
    let value = non_recover(natural_option, reader)?;
192
35
    Ok(OptionKind::LimitRate(value))
193
}
194

            
195
105
fn option_max_redirect(reader: &mut Reader) -> ParseResult<OptionKind> {
196
105
    let value = non_recover(count_option, reader)?;
197
105
    Ok(OptionKind::MaxRedirect(value))
198
}
199

            
200
30
fn option_netrc(reader: &mut Reader) -> ParseResult<OptionKind> {
201
30
    let value = non_recover(boolean_option, reader)?;
202
30
    Ok(OptionKind::NetRc(value))
203
}
204

            
205
35
fn option_netrc_file(reader: &mut Reader) -> ParseResult<OptionKind> {
206
35
    let value = unquoted_template(reader)?;
207
35
    Ok(OptionKind::NetRcFile(value))
208
}
209

            
210
30
fn option_netrc_optional(reader: &mut Reader) -> ParseResult<OptionKind> {
211
30
    let value = non_recover(boolean_option, reader)?;
212
30
    Ok(OptionKind::NetRcOptional(value))
213
}
214

            
215
130
fn option_output(reader: &mut Reader) -> ParseResult<OptionKind> {
216
130
    let value = filename::parse(reader)?;
217
130
    Ok(OptionKind::Output(value))
218
}
219

            
220
35
fn option_path_as_is(reader: &mut Reader) -> ParseResult<OptionKind> {
221
35
    let value = non_recover(boolean_option, reader)?;
222
35
    Ok(OptionKind::PathAsIs(value))
223
}
224

            
225
70
fn option_proxy(reader: &mut Reader) -> ParseResult<OptionKind> {
226
70
    let value = unquoted_template(reader)?;
227
70
    Ok(OptionKind::Proxy(value))
228
}
229

            
230
80
fn option_repeat(reader: &mut Reader) -> ParseResult<OptionKind> {
231
80
    let value = non_recover(count_option, reader)?;
232
80
    Ok(OptionKind::Repeat(value))
233
}
234

            
235
50
fn option_resolve(reader: &mut Reader) -> ParseResult<OptionKind> {
236
50
    let value = unquoted_template(reader)?;
237
50
    Ok(OptionKind::Resolve(value))
238
}
239

            
240
150
fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
241
150
    let value = non_recover(count_option, reader)?;
242
145
    Ok(OptionKind::Retry(value))
243
}
244

            
245
110
fn option_retry_interval(reader: &mut Reader) -> ParseResult<OptionKind> {
246
110
    let value = non_recover(duration_option, reader)?;
247
110
    Ok(OptionKind::RetryInterval(value))
248
}
249

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

            
255
100
fn option_user(reader: &mut Reader) -> ParseResult<OptionKind> {
256
100
    let value = unquoted_template(reader)?;
257
100
    Ok(OptionKind::User(value))
258
}
259

            
260
30
fn option_unix_socket(reader: &mut Reader) -> ParseResult<OptionKind> {
261
30
    let value = unquoted_template(reader)?;
262
30
    Ok(OptionKind::UnixSocket(value))
263
}
264

            
265
560
fn option_variable(reader: &mut Reader) -> ParseResult<OptionKind> {
266
560
    let value = variable_definition(reader)?;
267
555
    Ok(OptionKind::Variable(value))
268
}
269

            
270
95
fn option_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
271
95
    let value = non_recover(boolean_option, reader)?;
272
95
    Ok(OptionKind::Verbose(value))
273
}
274

            
275
50
fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
276
50
    let value = non_recover(boolean_option, reader)?;
277
50
    Ok(OptionKind::VeryVerbose(value))
278
}
279

            
280
335
fn count(reader: &mut Reader) -> ParseResult<Count> {
281
335
    let start = reader.cursor();
282
335
    let value = non_recover(integer, reader)?;
283
235
    if value.as_i64() == -1 {
284
40
        Ok(Count::Infinite)
285
195
    } else if value.as_i64() >= 0 {
286
190
        Ok(Count::Finite(value.as_i64() as usize))
287
    } else {
288
5
        let kind = ParseErrorKind::Expecting {
289
5
            value: "Expecting a count value".to_string(),
290
5
        };
291
5
        Err(ParseError::new(start.pos, false, kind))
292
    }
293
}
294

            
295
1545
fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
296
1545
    let start = reader.cursor();
297
1545
    match boolean(reader) {
298
1260
        Ok(v) => Ok(BooleanOption::Literal(v)),
299
        Err(_) => {
300
285
            reader.seek(start);
301
286
            let exp = placeholder::parse(reader).map_err(|e| {
302
5
                let kind = ParseErrorKind::Expecting {
303
5
                    value: "true|false".to_string(),
304
5
                };
305
5
                ParseError::new(e.pos, false, kind)
306
286
            })?;
307
280
            Ok(BooleanOption::Placeholder(exp))
308
        }
309
    }
310
}
311

            
312
35
fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
313
35
    let start = reader.cursor();
314
35
    match natural(reader) {
315
20
        Ok(v) => Ok(NaturalOption::Literal(v)),
316
        Err(_) => {
317
15
            reader.seek(start);
318
15
            let placeholder = placeholder::parse(reader).map_err(|e| {
319
                let kind = ParseErrorKind::Expecting {
320
                    value: "integer >= 0".to_string(),
321
                };
322
                ParseError::new(e.pos, false, kind)
323
15
            })?;
324
15
            Ok(NaturalOption::Placeholder(placeholder))
325
        }
326
    }
327
}
328

            
329
335
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
330
335
    let start = reader.cursor();
331
335
    match count(reader) {
332
230
        Ok(v) => Ok(CountOption::Literal(v)),
333
        Err(_) => {
334
105
            reader.seek(start);
335
106
            let placeholder = placeholder::parse(reader).map_err(|e| {
336
5
                let kind = ParseErrorKind::Expecting {
337
5
                    value: "integer >= -1".to_string(),
338
5
                };
339
5
                ParseError::new(e.pos, false, kind)
340
106
            })?;
341
100
            Ok(CountOption::Placeholder(placeholder))
342
        }
343
    }
344
}
345

            
346
235
fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
347
235
    let start = reader.cursor();
348
235
    match duration(reader) {
349
180
        Ok(v) => Ok(DurationOption::Literal(v)),
350
55
        Err(e) => {
351
55
            if e.recoverable {
352
50
                reader.seek(start);
353
50
                let placeholder = placeholder::parse(reader).map_err(|e| {
354
                    let kind = ParseErrorKind::Expecting {
355
                        value: "integer".to_string(),
356
                    };
357
                    ParseError::new(e.pos, false, kind)
358
50
                })?;
359
50
                Ok(DurationOption::Placeholder(placeholder))
360
            } else {
361
5
                Err(e)
362
            }
363
        }
364
    }
365
}
366

            
367
560
fn variable_definition(reader: &mut Reader) -> ParseResult<VariableDefinition> {
368
560
    let start = reader.cursor();
369
560
    let name = variable_name(reader)?;
370
555
    let space0 = zero_or_more_spaces(reader)?;
371
555
    literal("=", reader)?;
372
555
    let space1 = zero_or_more_spaces(reader)?;
373
555
    let value = variable_value(reader)?;
374
555
    let end = reader.cursor();
375
555
    let source_info = SourceInfo {
376
555
        start: start.pos,
377
555
        end: end.pos,
378
555
    };
379
555
    Ok(VariableDefinition {
380
555
        source_info,
381
555
        name,
382
555
        space0,
383
555
        space1,
384
555
        value,
385
555
    })
386
}
387

            
388
560
fn variable_name(reader: &mut Reader) -> ParseResult<String> {
389
560
    let start = reader.cursor();
390
3187
    let name = reader.read_while(|c| c.is_alphanumeric() || c == '_' || c == '-');
391
560
    if name.is_empty() {
392
        let kind = ParseErrorKind::Expecting {
393
            value: "variable name".to_string(),
394
        };
395
        return Err(ParseError::new(start.pos, false, kind));
396
560
    } else if is_variable_reserved(&name) {
397
5
        let kind = ParseErrorKind::Variable(format!(
398
5
            "conflicts with the {name} function, use a different name"
399
5
        ));
400
5
        return Err(ParseError::new(start.pos, false, kind));
401
    }
402
555
    Ok(name)
403
}
404

            
405
555
fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
406
555
    choice(
407
555
        &[
408
666
            |p1| match null(p1) {
409
15
                Ok(()) => Ok(VariableValue::Null),
410
540
                Err(e) => Err(e),
411
666
            },
412
663
            |p1| match boolean(p1) {
413
20
                Ok(value) => Ok(VariableValue::Bool(value)),
414
520
                Err(e) => Err(e),
415
663
            },
416
659
            |p1| match number(p1) {
417
195
                Ok(value) => Ok(VariableValue::Number(value)),
418
325
                Err(e) => Err(e),
419
659
            },
420
620
            |p1| match quoted_template(p1) {
421
                Ok(value) => Ok(VariableValue::String(value)),
422
325
                Err(e) => Err(e),
423
620
            },
424
620
            |p1| match unquoted_template(p1) {
425
325
                Ok(value) => Ok(VariableValue::String(value)),
426
                Err(e) => Err(e),
427
620
            },
428
555
        ],
429
555
        reader,
430
555
    )
431
555
    .map_err(|e| {
432
        let kind = ParseErrorKind::Expecting {
433
            value: "variable value".to_string(),
434
        };
435
        ParseError::new(e.pos, false, kind)
436
555
    })
437
}
438

            
439
#[cfg(test)]
440
mod tests {
441
    use super::*;
442
    use crate::ast::{LineTerminator, Number, Template, TemplateElement, Whitespace, I64};
443
    use crate::reader::Pos;
444

            
445
    #[test]
446
    fn test_option_insecure() {
447
        let mut reader = Reader::new("insecure: true");
448
        let option = parse(&mut reader).unwrap();
449
        assert_eq!(
450
            option,
451
            EntryOption {
452
                line_terminators: vec![],
453
                space0: Whitespace {
454
                    value: String::new(),
455
                    source_info: SourceInfo {
456
                        start: Pos { line: 1, column: 1 },
457
                        end: Pos { line: 1, column: 1 },
458
                    },
459
                },
460
                space1: Whitespace {
461
                    value: String::new(),
462
                    source_info: SourceInfo {
463
                        start: Pos { line: 1, column: 9 },
464
                        end: Pos { line: 1, column: 9 },
465
                    },
466
                },
467
                space2: Whitespace {
468
                    value: " ".to_string(),
469
                    source_info: SourceInfo {
470
                        start: Pos {
471
                            line: 1,
472
                            column: 10,
473
                        },
474
                        end: Pos {
475
                            line: 1,
476
                            column: 11,
477
                        },
478
                    },
479
                },
480
                kind: OptionKind::Insecure(BooleanOption::Literal(true)),
481
                line_terminator0: LineTerminator {
482
                    space0: Whitespace {
483
                        value: String::new(),
484
                        source_info: SourceInfo {
485
                            start: Pos {
486
                                line: 1,
487
                                column: 15,
488
                            },
489
                            end: Pos {
490
                                line: 1,
491
                                column: 15,
492
                            },
493
                        },
494
                    },
495
                    comment: None,
496
                    newline: Whitespace {
497
                        value: String::new(),
498
                        source_info: SourceInfo {
499
                            start: Pos {
500
                                line: 1,
501
                                column: 15,
502
                            },
503
                            end: Pos {
504
                                line: 1,
505
                                column: 15,
506
                            },
507
                        },
508
                    },
509
                },
510
            }
511
        );
512
    }
513

            
514
    #[test]
515
    fn test_option_insecure_error() {
516
        let mut reader = Reader::new("insecure: error");
517
        let error = parse(&mut reader).err().unwrap();
518
        assert!(!error.recoverable);
519
    }
520

            
521
    #[test]
522
    fn test_option_cacert() {
523
        let mut reader = Reader::new("cacert: /home/foo/cert.pem");
524
        let option = parse(&mut reader).unwrap();
525
        assert_eq!(
526
            option,
527
            EntryOption {
528
                line_terminators: vec![],
529
                space0: Whitespace {
530
                    value: String::new(),
531
                    source_info: SourceInfo {
532
                        start: Pos { line: 1, column: 1 },
533
                        end: Pos { line: 1, column: 1 },
534
                    },
535
                },
536
                space1: Whitespace {
537
                    value: String::new(),
538
                    source_info: SourceInfo {
539
                        start: Pos { line: 1, column: 7 },
540
                        end: Pos { line: 1, column: 7 },
541
                    },
542
                },
543
                space2: Whitespace {
544
                    value: " ".to_string(),
545
                    source_info: SourceInfo {
546
                        start: Pos { line: 1, column: 8 },
547
                        end: Pos { line: 1, column: 9 },
548
                    },
549
                },
550
                kind: OptionKind::CaCertificate(Template {
551
                    delimiter: None,
552
                    elements: vec![TemplateElement::String {
553
                        value: "/home/foo/cert.pem".to_string(),
554
                        encoded: "/home/foo/cert.pem".to_string()
555
                    }],
556
                    source_info: SourceInfo {
557
                        start: Pos { line: 1, column: 9 },
558
                        end: Pos {
559
                            line: 1,
560
                            column: 27,
561
                        },
562
                    },
563
                }),
564
                line_terminator0: LineTerminator {
565
                    space0: Whitespace {
566
                        value: String::new(),
567
                        source_info: SourceInfo {
568
                            start: Pos {
569
                                line: 1,
570
                                column: 27,
571
                            },
572
                            end: Pos {
573
                                line: 1,
574
                                column: 27,
575
                            },
576
                        },
577
                    },
578
                    comment: None,
579
                    newline: Whitespace {
580
                        value: String::new(),
581
                        source_info: SourceInfo {
582
                            start: Pos {
583
                                line: 1,
584
                                column: 27,
585
                            },
586
                            end: Pos {
587
                                line: 1,
588
                                column: 27,
589
                            },
590
                        },
591
                    },
592
                },
593
            }
594
        );
595
    }
596

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

            
604
    #[test]
605
    fn test_option_cert() {
606
        let mut reader = Reader::new("/etc/client-cert.pem #foo");
607

            
608
        assert_eq!(
609
            option_cert(&mut reader).unwrap(),
610
            OptionKind::ClientCert(Template {
611
                delimiter: None,
612
                elements: vec![TemplateElement::String {
613
                    value: "/etc/client-cert.pem".to_string(),
614
                    encoded: "/etc/client-cert.pem".to_string()
615
                }],
616
                source_info: SourceInfo {
617
                    start: Pos { line: 1, column: 1 },
618
                    end: Pos {
619
                        line: 1,
620
                        column: 21,
621
                    },
622
                },
623
            }),
624
        );
625
    }
626

            
627
    #[test]
628
    fn test_option_retry_error() {
629
        let mut reader = Reader::new("retry: ###");
630
        let error = parse(&mut reader).err().unwrap();
631
        assert!(!error.recoverable);
632
        assert_eq!(error.pos, Pos { line: 1, column: 8 });
633
        assert_eq!(
634
            error.kind,
635
            ParseErrorKind::Expecting {
636
                value: "integer >= -1".to_string()
637
            }
638
        );
639
    }
640

            
641
    #[test]
642
    fn test_variable_definition() {
643
        let mut reader = Reader::new("a=1");
644
        assert_eq!(
645
            variable_definition(&mut reader).unwrap(),
646
            VariableDefinition {
647
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 4)),
648
                name: "a".to_string(),
649
                space0: Whitespace {
650
                    value: String::new(),
651
                    source_info: SourceInfo::new(Pos::new(1, 2), Pos::new(1, 2)),
652
                },
653
                space1: Whitespace {
654
                    value: String::new(),
655
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
656
                },
657
                value: VariableValue::Number(Number::Integer(I64::new(1, "1".to_string()))),
658
            }
659
        );
660
    }
661

            
662
    #[test]
663
    fn test_variable_value() {
664
        let mut reader = Reader::new("null");
665
        assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null);
666

            
667
        let mut reader = Reader::new("true");
668
        assert_eq!(
669
            variable_value(&mut reader).unwrap(),
670
            VariableValue::Bool(true)
671
        );
672

            
673
        let mut reader = Reader::new("1");
674
        assert_eq!(
675
            variable_value(&mut reader).unwrap(),
676
            VariableValue::Number(Number::Integer(I64::new(1, "1".to_string())))
677
        );
678

            
679
        let mut reader = Reader::new("toto");
680
        assert_eq!(
681
            variable_value(&mut reader).unwrap(),
682
            VariableValue::String(Template {
683
                delimiter: None,
684
                elements: vec![TemplateElement::String {
685
                    value: "toto".to_string(),
686
                    encoded: "toto".to_string(),
687
                }],
688
                source_info: SourceInfo {
689
                    start: Pos { line: 1, column: 1 },
690
                    end: Pos { line: 1, column: 5 },
691
                },
692
            })
693
        );
694
        let mut reader = Reader::new("\"123\"");
695
        assert_eq!(
696
            variable_value(&mut reader).unwrap(),
697
            VariableValue::String(Template {
698
                delimiter: Some('"'),
699
                elements: vec![TemplateElement::String {
700
                    value: "123".to_string(),
701
                    encoded: "123".to_string(),
702
                }],
703
                source_info: SourceInfo {
704
                    start: Pos { line: 1, column: 1 },
705
                    end: Pos { line: 1, column: 6 },
706
                },
707
            })
708
        );
709
    }
710
}