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::json;
19
use crate::reader::Pos;
20
use crate::typing::{Count, Duration};
21

            
22
///
23
/// Hurl AST
24
///
25
#[derive(Clone, Debug, PartialEq, Eq)]
26
pub struct HurlFile {
27
    pub entries: Vec<Entry>,
28
    pub line_terminators: Vec<LineTerminator>,
29
}
30

            
31
#[derive(Clone, Debug, PartialEq, Eq)]
32
pub struct Entry {
33
    pub request: Request,
34
    pub response: Option<Response>,
35
}
36

            
37
impl Entry {
38
    /// Returns the source information for this entry.
39
13525
    pub fn source_info(&self) -> SourceInfo {
40
13525
        self.request.space0.source_info
41
    }
42
}
43

            
44
#[derive(Clone, Debug, PartialEq, Eq)]
45
pub struct Request {
46
    pub line_terminators: Vec<LineTerminator>,
47
    pub space0: Whitespace,
48
    pub method: Method,
49
    pub space1: Whitespace,
50
    pub url: Template,
51
    pub line_terminator0: LineTerminator,
52
    pub headers: Vec<Header>,
53
    pub sections: Vec<Section>,
54
    pub body: Option<Body>,
55
    pub source_info: SourceInfo,
56
}
57

            
58
impl Request {
59
11175
    pub fn querystring_params(&self) -> Vec<KeyValue> {
60
12720
        for section in &self.sections {
61
1755
            if let SectionValue::QueryParams(params, _) = &section.value {
62
210
                return params.clone();
63
            }
64
        }
65
10965
        vec![]
66
    }
67
11140
    pub fn form_params(&self) -> Vec<KeyValue> {
68
12810
        for section in &self.sections {
69
1755
            if let SectionValue::FormParams(params, _) = &section.value {
70
85
                return params.clone();
71
            }
72
        }
73
11055
        vec![]
74
    }
75
11060
    pub fn multipart_form_data(&self) -> Vec<MultipartParam> {
76
12720
        for section in &self.sections {
77
1770
            if let SectionValue::MultipartFormData(params, _) = &section.value {
78
110
                return params.clone();
79
            }
80
        }
81
10950
        vec![]
82
    }
83

            
84
11140
    pub fn cookies(&self) -> Vec<Cookie> {
85
12900
        for section in &self.sections {
86
1805
            if let SectionValue::Cookies(cookies) = &section.value {
87
45
                return cookies.clone();
88
            }
89
        }
90
11095
        vec![]
91
    }
92

            
93
10890
    pub fn basic_auth(&self) -> Option<KeyValue> {
94
12575
        for section in &self.sections {
95
1735
            if let SectionValue::BasicAuth(kv) = &section.value {
96
50
                return kv.clone();
97
            }
98
        }
99
10840
        None
100
    }
101

            
102
405
    pub fn options(&self) -> Vec<EntryOption> {
103
475
        for section in &self.sections {
104
220
            if let SectionValue::Options(options) = &section.value {
105
150
                return options.clone();
106
            }
107
        }
108
255
        vec![]
109
    }
110
}
111

            
112
#[derive(Clone, Debug, PartialEq, Eq)]
113
pub struct Response {
114
    pub line_terminators: Vec<LineTerminator>,
115
    pub version: Version,
116
    pub space0: Whitespace,
117
    pub status: Status,
118
    pub space1: Whitespace,
119
    pub line_terminator0: LineTerminator,
120
    pub headers: Vec<Header>,
121
    pub sections: Vec<Section>,
122
    pub body: Option<Body>,
123
    pub source_info: SourceInfo,
124
}
125

            
126
impl Response {
127
    /// Returns the captures list of this spec response.
128
10405
    pub fn captures(&self) -> &[Capture] {
129
10405
        for section in self.sections.iter() {
130
7290
            if let SectionValue::Captures(captures) = &section.value {
131
315
                return captures;
132
            }
133
        }
134
10090
        &[]
135
    }
136

            
137
    /// Returns the asserts list of this spec response.
138
10415
    pub fn asserts(&self) -> &[Assert] {
139
10415
        for section in self.sections.iter() {
140
7505
            if let SectionValue::Asserts(asserts) = &section.value {
141
7215
                return asserts;
142
            }
143
        }
144
3200
        &[]
145
    }
146
}
147

            
148
#[derive(Clone, Debug, PartialEq, Eq)]
149
pub struct Method(pub String);
150

            
151
#[derive(Clone, Debug, PartialEq, Eq)]
152
pub struct Version {
153
    pub value: VersionValue,
154
    pub source_info: SourceInfo,
155
}
156

            
157
#[derive(Clone, Debug, PartialEq, Eq)]
158
pub enum VersionValue {
159
    Version1,
160
    Version11,
161
    Version2,
162
    Version3,
163
    VersionAny,
164
    VersionAnyLegacy,
165
}
166

            
167
#[derive(Clone, Debug, PartialEq, Eq)]
168
pub struct Status {
169
    pub value: StatusValue,
170
    pub source_info: SourceInfo,
171
}
172

            
173
#[derive(Clone, Debug, PartialEq, Eq)]
174
pub enum StatusValue {
175
    Any,
176
    Specific(u64),
177
}
178

            
179
pub type Header = KeyValue;
180

            
181
#[derive(Clone, Debug, PartialEq, Eq)]
182
pub struct Body {
183
    pub line_terminators: Vec<LineTerminator>,
184
    pub space0: Whitespace,
185
    pub value: Bytes,
186
    pub line_terminator0: LineTerminator,
187
}
188

            
189
//
190
// Sections
191
//
192

            
193
#[derive(Clone, Debug, PartialEq, Eq)]
194
pub struct Section {
195
    pub line_terminators: Vec<LineTerminator>,
196
    pub space0: Whitespace,
197
    pub line_terminator0: LineTerminator,
198
    pub value: SectionValue,
199
    pub source_info: SourceInfo,
200
}
201

            
202
impl Section {
203
5905
    pub fn name(&self) -> &str {
204
5905
        match self.value {
205
660
            SectionValue::Asserts(_) => "Asserts",
206
210
            SectionValue::QueryParams(_, true) => "Query",
207
505
            SectionValue::QueryParams(_, false) => "QueryStringParams",
208
170
            SectionValue::BasicAuth(_) => "BasicAuth",
209
75
            SectionValue::FormParams(_, true) => "Form",
210
175
            SectionValue::FormParams(_, false) => "FormParams",
211
165
            SectionValue::Cookies(_) => "Cookies",
212
100
            SectionValue::Captures(_) => "Captures",
213
75
            SectionValue::MultipartFormData(_, true) => "Multipart",
214
240
            SectionValue::MultipartFormData(_, false) => "MultipartFormData",
215
3530
            SectionValue::Options(_) => "Options",
216
        }
217
    }
218
}
219

            
220
#[derive(Clone, Debug, PartialEq, Eq)]
221
#[allow(clippy::large_enum_variant)]
222
pub enum SectionValue {
223
    QueryParams(Vec<KeyValue>, bool), // boolean param indicates if we use the short syntax
224
    BasicAuth(Option<KeyValue>),      // boolean param indicates if we use the short syntax
225
    FormParams(Vec<KeyValue>, bool),
226
    MultipartFormData(Vec<MultipartParam>, bool), // boolean param indicates if we use the short syntax
227
    Cookies(Vec<Cookie>),
228
    Captures(Vec<Capture>),
229
    Asserts(Vec<Assert>),
230
    Options(Vec<EntryOption>),
231
}
232

            
233
#[derive(Clone, Debug, PartialEq, Eq)]
234
pub struct Cookie {
235
    pub line_terminators: Vec<LineTerminator>,
236
    pub space0: Whitespace,
237
    pub name: Template,
238
    pub space1: Whitespace,
239
    pub space2: Whitespace,
240
    pub value: Template,
241
    pub line_terminator0: LineTerminator,
242
}
243

            
244
#[derive(Clone, Debug, PartialEq, Eq)]
245
pub struct KeyValue {
246
    pub line_terminators: Vec<LineTerminator>,
247
    pub space0: Whitespace,
248
    pub key: Template,
249
    pub space1: Whitespace,
250
    pub space2: Whitespace,
251
    pub value: Template,
252
    pub line_terminator0: LineTerminator,
253
}
254

            
255
#[derive(Clone, Debug, PartialEq, Eq)]
256
pub enum MultipartParam {
257
    Param(KeyValue),
258
    FileParam(FileParam),
259
}
260

            
261
#[derive(Clone, Debug, PartialEq, Eq)]
262
pub struct FileParam {
263
    pub line_terminators: Vec<LineTerminator>,
264
    pub space0: Whitespace,
265
    pub key: Template,
266
    pub space1: Whitespace,
267
    pub space2: Whitespace,
268
    pub value: FileValue,
269
    pub line_terminator0: LineTerminator,
270
}
271

            
272
#[derive(Clone, Debug, PartialEq, Eq)]
273
pub struct FileValue {
274
    pub space0: Whitespace,
275
    pub filename: Template,
276
    pub space1: Whitespace,
277
    pub space2: Whitespace,
278
    pub content_type: Option<String>,
279
}
280

            
281
#[derive(Clone, Debug, PartialEq, Eq)]
282
pub struct Capture {
283
    pub line_terminators: Vec<LineTerminator>,
284
    pub space0: Whitespace,
285
    pub name: Template,
286
    pub space1: Whitespace,
287
    pub space2: Whitespace,
288
    pub query: Query,
289
    pub filters: Vec<(Whitespace, Filter)>,
290
    pub line_terminator0: LineTerminator,
291
}
292

            
293
#[derive(Clone, Debug, PartialEq, Eq)]
294
pub struct Assert {
295
    pub line_terminators: Vec<LineTerminator>,
296
    pub space0: Whitespace,
297
    pub query: Query,
298
    pub filters: Vec<(Whitespace, Filter)>,
299
    pub space1: Whitespace,
300
    pub predicate: Predicate,
301
    pub line_terminator0: LineTerminator,
302
}
303

            
304
#[derive(Clone, Debug, PartialEq, Eq)]
305
pub struct Query {
306
    pub source_info: SourceInfo,
307
    pub value: QueryValue,
308
}
309

            
310
#[derive(Clone, Debug, PartialEq, Eq)]
311
#[allow(clippy::large_enum_variant)]
312
pub enum QueryValue {
313
    Status,
314
    Url,
315
    Header {
316
        space0: Whitespace,
317
        name: Template,
318
    },
319
    Cookie {
320
        space0: Whitespace,
321
        expr: CookiePath,
322
    },
323
    Body,
324
    Xpath {
325
        space0: Whitespace,
326
        expr: Template,
327
    },
328
    Jsonpath {
329
        space0: Whitespace,
330
        expr: Template,
331
    },
332
    Regex {
333
        space0: Whitespace,
334
        value: RegexValue,
335
    },
336
    Variable {
337
        space0: Whitespace,
338
        name: Template,
339
    },
340
    Duration,
341
    Bytes,
342
    Sha256,
343
    Md5,
344
    Certificate {
345
        space0: Whitespace,
346
        attribute_name: CertificateAttributeName,
347
    },
348
}
349

            
350
#[derive(Clone, Debug, PartialEq, Eq)]
351
pub enum RegexValue {
352
    Template(Template),
353
    Regex(Regex),
354
}
355

            
356
#[derive(Clone, Debug, PartialEq, Eq)]
357
pub struct CookiePath {
358
    pub name: Template,
359
    pub attribute: Option<CookieAttribute>,
360
}
361

            
362
#[derive(Clone, Debug, PartialEq, Eq)]
363
pub struct CookieAttribute {
364
    pub space0: Whitespace,
365
    pub name: CookieAttributeName,
366
    pub space1: Whitespace,
367
}
368

            
369
#[derive(Clone, Debug, PartialEq, Eq)]
370
pub enum CookieAttributeName {
371
    Value(String),
372
    Expires(String),
373
    MaxAge(String),
374
    Domain(String),
375
    Path(String),
376
    Secure(String),
377
    HttpOnly(String),
378
    SameSite(String),
379
}
380

            
381
impl CookieAttributeName {
382
65
    pub fn value(&self) -> String {
383
65
        match self {
384
            CookieAttributeName::Value(value)
385
15
            | CookieAttributeName::Expires(value)
386
5
            | CookieAttributeName::MaxAge(value)
387
5
            | CookieAttributeName::Domain(value)
388
5
            | CookieAttributeName::Path(value)
389
30
            | CookieAttributeName::Secure(value)
390
5
            | CookieAttributeName::HttpOnly(value)
391
65
            | CookieAttributeName::SameSite(value) => value.to_string(),
392
        }
393
    }
394
}
395

            
396
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
397
pub enum CertificateAttributeName {
398
    Subject,
399
    Issuer,
400
    StartDate,
401
    ExpireDate,
402
    SerialNumber,
403
}
404

            
405
#[derive(Clone, Debug, PartialEq, Eq)]
406
pub struct Predicate {
407
    pub not: bool,
408
    pub space0: Whitespace,
409
    pub predicate_func: PredicateFunc,
410
}
411

            
412
#[derive(Clone, Debug, PartialEq, Eq)]
413
pub struct Not {
414
    pub value: bool,
415
    pub space0: Whitespace,
416
}
417

            
418
#[derive(Clone, Debug, PartialEq, Eq)]
419
pub struct PredicateFunc {
420
    pub source_info: SourceInfo,
421
    pub value: PredicateFuncValue,
422
}
423

            
424
#[derive(Clone, Debug, PartialEq, Eq)]
425
#[allow(clippy::large_enum_variant)]
426
pub enum PredicateValue {
427
    Base64(Base64),
428
    Bool(bool),
429
    Expression(Expr),
430
    File(File),
431
    Hex(Hex),
432
    MultilineString(MultilineString),
433
    Null,
434
    Number(Number),
435
    Regex(Regex),
436
    String(Template),
437
}
438

            
439
#[derive(Clone, Debug, PartialEq, Eq)]
440
#[allow(clippy::large_enum_variant)]
441
pub enum PredicateFuncValue {
442
    Equal {
443
        space0: Whitespace,
444
        value: PredicateValue,
445
        operator: bool,
446
    },
447
    NotEqual {
448
        space0: Whitespace,
449
        value: PredicateValue,
450
        operator: bool,
451
    },
452
    GreaterThan {
453
        space0: Whitespace,
454
        value: PredicateValue,
455
        operator: bool,
456
    },
457
    GreaterThanOrEqual {
458
        space0: Whitespace,
459
        value: PredicateValue,
460
        operator: bool,
461
    },
462
    LessThan {
463
        space0: Whitespace,
464
        value: PredicateValue,
465
        operator: bool,
466
    },
467
    LessThanOrEqual {
468
        space0: Whitespace,
469
        value: PredicateValue,
470
        operator: bool,
471
    },
472
    StartWith {
473
        space0: Whitespace,
474
        value: PredicateValue,
475
    },
476
    EndWith {
477
        space0: Whitespace,
478
        value: PredicateValue,
479
    },
480
    Contain {
481
        space0: Whitespace,
482
        value: PredicateValue,
483
    },
484
    Include {
485
        space0: Whitespace,
486
        value: PredicateValue,
487
    },
488
    Match {
489
        space0: Whitespace,
490
        value: PredicateValue,
491
    },
492
    IsInteger,
493
    IsFloat,
494
    IsBoolean,
495
    IsString,
496
    IsCollection,
497
    IsDate,
498
    IsIsoDate,
499
    Exist,
500
    IsEmpty,
501
    IsNumber,
502
}
503

            
504
//
505
// Primitives
506
//
507
#[derive(Clone, Debug, PartialEq, Eq)]
508
pub struct MultilineString {
509
    pub kind: MultilineStringKind,
510
    pub attributes: Vec<MultilineStringAttribute>,
511
}
512

            
513
#[derive(Clone, Debug, PartialEq, Eq)]
514
pub enum MultilineStringKind {
515
    Text(Text),
516
    Json(Text),
517
    Xml(Text),
518
    GraphQl(GraphQl),
519
}
520

            
521
#[derive(Clone, Debug, PartialEq, Eq)]
522
pub enum MultilineStringAttribute {
523
    Escape,
524
    NoVariable,
525
}
526

            
527
impl MultilineString {
528
270
    pub fn lang(&self) -> &'static str {
529
270
        match self.kind {
530
110
            MultilineStringKind::Text(_) => "",
531
50
            MultilineStringKind::Json(_) => "json",
532
45
            MultilineStringKind::Xml(_) => "xml",
533
65
            MultilineStringKind::GraphQl(_) => "graphql",
534
        }
535
    }
536

            
537
175
    pub fn value(&self) -> Template {
538
175
        match &self.kind {
539
115
            MultilineStringKind::Text(text)
540
20
            | MultilineStringKind::Json(text)
541
155
            | MultilineStringKind::Xml(text) => text.value.clone(),
542
20
            MultilineStringKind::GraphQl(text) => text.value.clone(),
543
        }
544
    }
545
}
546

            
547
#[derive(Clone, Debug, PartialEq, Eq)]
548
pub struct Text {
549
    pub space: Whitespace,
550
    pub newline: Whitespace,
551
    pub value: Template,
552
}
553

            
554
#[derive(Clone, Debug, PartialEq, Eq)]
555
pub struct GraphQl {
556
    pub space: Whitespace,
557
    pub newline: Whitespace,
558
    pub value: Template,
559
    pub variables: Option<GraphQlVariables>,
560
}
561

            
562
#[derive(Clone, Debug, PartialEq, Eq)]
563
pub struct GraphQlVariables {
564
    pub space: Whitespace,
565
    pub value: json::Value,
566
    pub whitespace: Whitespace,
567
}
568

            
569
#[derive(Clone, Debug, PartialEq, Eq)]
570
pub struct Base64 {
571
    pub space0: Whitespace,
572
    pub value: Vec<u8>,
573
    pub encoded: String,
574
    pub space1: Whitespace,
575
}
576

            
577
#[derive(Clone, Debug, PartialEq, Eq)]
578
pub struct File {
579
    pub space0: Whitespace,
580
    pub filename: Template,
581
    pub space1: Whitespace,
582
}
583

            
584
#[derive(Clone, Debug, PartialEq, Eq)]
585
pub struct Template {
586
    pub delimiter: Option<char>,
587
    pub elements: Vec<TemplateElement>,
588
    pub source_info: SourceInfo,
589
}
590

            
591
#[derive(Clone, Debug, PartialEq, Eq)]
592
pub enum TemplateElement {
593
    // TODO: explain the difference between value and encoded
594
    String { value: String, encoded: String },
595
    Expression(Expr),
596
}
597

            
598
#[derive(Clone, Debug, PartialEq, Eq)]
599
pub struct Comment {
600
    pub value: String,
601
    pub source_info: SourceInfo,
602
}
603

            
604
#[derive(Clone, Debug, PartialEq, Eq)]
605
pub struct EncodedString {
606
    pub value: String,
607
    pub encoded: String,
608
    pub quotes: bool,
609
    pub source_info: SourceInfo,
610
}
611

            
612
#[derive(Clone, Debug, PartialEq, Eq)]
613
pub struct Whitespace {
614
    pub value: String,
615
    pub source_info: SourceInfo,
616
}
617

            
618
#[derive(Clone, Debug, PartialEq, Eq)]
619
pub enum Number {
620
    Float(Float),
621
    Integer(i64),
622
    BigInteger(String),
623
}
624

            
625
// keep Number terminology for both Integer and Decimal Numbers
626
// different representation for the same float value
627
// 1.01 and 1.010
628

            
629
#[derive(Clone, Debug)]
630
pub struct Float {
631
    pub value: f64,
632
    pub encoded: String, // as defined in Hurl
633
}
634

            
635
impl PartialEq for Float {
636
    fn eq(&self, other: &Self) -> bool {
637
        self.encoded == other.encoded
638
    }
639
}
640
impl Eq for Float {}
641

            
642
#[derive(Clone, Debug, PartialEq, Eq)]
643
pub struct LineTerminator {
644
    pub space0: Whitespace,
645
    pub comment: Option<Comment>,
646
    pub newline: Whitespace,
647
}
648

            
649
#[derive(Clone, Debug, PartialEq, Eq)]
650
pub enum Bytes {
651
    Json(json::Value),
652
    Xml(String),
653
    MultilineString(MultilineString),
654
    OnelineString(Template),
655
    Base64(Base64),
656
    File(File),
657
    Hex(Hex),
658
}
659

            
660
#[derive(Clone, Debug, PartialEq, Eq)]
661
pub struct Hex {
662
    pub space0: Whitespace,
663
    pub value: Vec<u8>,
664
    pub encoded: String,
665
    pub space1: Whitespace,
666
}
667

            
668
// Literal Regex
669
#[derive(Clone, Debug)]
670
pub struct Regex {
671
    pub inner: regex::Regex,
672
}
673

            
674
impl PartialEq for Regex {
675
    fn eq(&self, other: &Self) -> bool {
676
        self.inner.to_string() == other.inner.to_string()
677
    }
678
}
679
impl Eq for Regex {}
680

            
681
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
682
pub struct SourceInfo {
683
    pub start: Pos,
684
    pub end: Pos,
685
}
686

            
687
impl SourceInfo {
688
1554580
    pub fn new(start: Pos, end: Pos) -> SourceInfo {
689
1554580
        SourceInfo { start, end }
690
    }
691
}
692

            
693
#[derive(Clone, Debug, PartialEq, Eq)]
694
pub struct Expr {
695
    pub space0: Whitespace,
696
    pub variable: Variable,
697
    pub space1: Whitespace,
698
}
699

            
700
#[derive(Clone, Debug, PartialEq, Eq)]
701
pub struct Variable {
702
    pub name: String,
703
    pub source_info: SourceInfo,
704
}
705

            
706
/// Check that variable name is not reserved
707
/// (would conflicts with an existing function)
708
620
pub fn is_variable_reserved(name: &str) -> bool {
709
620
    ["getEnv", "newDate", "newUuid"].contains(&name)
710
}
711

            
712
#[derive(Clone, Debug, PartialEq, Eq)]
713
pub struct EntryOption {
714
    pub line_terminators: Vec<LineTerminator>,
715
    pub space0: Whitespace,
716
    pub space1: Whitespace,
717
    pub space2: Whitespace,
718
    pub kind: OptionKind,
719
    pub line_terminator0: LineTerminator,
720
}
721

            
722
#[derive(Clone, Debug, PartialEq, Eq)]
723
pub enum OptionKind {
724
    AwsSigV4(Template),
725
    CaCertificate(Template),
726
    ClientCert(Template),
727
    ClientKey(Template),
728
    Compressed(BooleanOption),
729
    ConnectTo(Template),
730
    Delay(DurationOption),
731
    Http10(BooleanOption),
732
    Http11(BooleanOption),
733
    Http2(BooleanOption),
734
    Http3(BooleanOption),
735
    Insecure(BooleanOption),
736
    IpV4(BooleanOption),
737
    IpV6(BooleanOption),
738
    FollowLocation(BooleanOption),
739
    FollowLocationTrusted(BooleanOption),
740
    MaxRedirect(CountOption),
741
    NetRc(BooleanOption),
742
    NetRcFile(Template),
743
    NetRcOptional(BooleanOption),
744
    Output(Template),
745
    PathAsIs(BooleanOption),
746
    Proxy(Template),
747
    Repeat(CountOption),
748
    Resolve(Template),
749
    Retry(CountOption),
750
    RetryInterval(DurationOption),
751
    Skip(BooleanOption),
752
    UnixSocket(Template),
753
    User(Template),
754
    Variable(VariableDefinition),
755
    Verbose(BooleanOption),
756
    VeryVerbose(BooleanOption),
757
}
758

            
759
impl OptionKind {
760
3245
    pub fn name(&self) -> &'static str {
761
3245
        match self {
762
40
            OptionKind::AwsSigV4(_) => "aws-sigv4",
763
55
            OptionKind::CaCertificate(_) => "cacert",
764
65
            OptionKind::ClientCert(_) => "cert",
765
50
            OptionKind::ClientKey(_) => "key",
766
410
            OptionKind::Compressed(_) => "compressed",
767
65
            OptionKind::ConnectTo(_) => "connect-to",
768
90
            OptionKind::Delay(_) => "delay",
769
380
            OptionKind::FollowLocation(_) => "location",
770
50
            OptionKind::FollowLocationTrusted(_) => "location-trusted",
771
105
            OptionKind::Http10(_) => "http1.0",
772
75
            OptionKind::Http11(_) => "http1.1",
773
40
            OptionKind::Http2(_) => "http2",
774
30
            OptionKind::Http3(_) => "http3",
775
95
            OptionKind::Insecure(_) => "insecure",
776
30
            OptionKind::IpV4(_) => "ipv4",
777
30
            OptionKind::IpV6(_) => "ipv6",
778
85
            OptionKind::MaxRedirect(_) => "max-redirs",
779
30
            OptionKind::NetRc(_) => "netrc",
780
35
            OptionKind::NetRcFile(_) => "netrc-file",
781
30
            OptionKind::NetRcOptional(_) => "netrc-optional",
782
145
            OptionKind::Output(_) => "output",
783
35
            OptionKind::PathAsIs(_) => "path-as-is",
784
70
            OptionKind::Proxy(_) => "proxy",
785
100
            OptionKind::Repeat(_) => "repeat",
786
50
            OptionKind::Resolve(_) => "resolve",
787
140
            OptionKind::Retry(_) => "retry",
788
110
            OptionKind::RetryInterval(_) => "retry-interval",
789
40
            OptionKind::Skip(_) => "skip",
790
30
            OptionKind::UnixSocket(_) => "unix-socket",
791
100
            OptionKind::User(_) => "user",
792
515
            OptionKind::Variable(_) => "variable",
793
70
            OptionKind::Verbose(_) => "verbose",
794
50
            OptionKind::VeryVerbose(_) => "very-verbose",
795
        }
796
    }
797

            
798
1660
    pub fn value_as_str(&self) -> String {
799
1660
        match self {
800
10
            OptionKind::AwsSigV4(value) => value.to_string(),
801
25
            OptionKind::CaCertificate(filename) => filename.to_string(),
802
20
            OptionKind::ClientCert(filename) => filename.to_string(),
803
20
            OptionKind::ClientKey(filename) => filename.to_string(),
804
285
            OptionKind::Compressed(value) => value.to_string(),
805
35
            OptionKind::ConnectTo(value) => value.to_string(),
806
30
            OptionKind::Delay(value) => value.to_string(),
807
265
            OptionKind::FollowLocation(value) => value.to_string(),
808
15
            OptionKind::FollowLocationTrusted(value) => value.to_string(),
809
50
            OptionKind::Http10(value) => value.to_string(),
810
30
            OptionKind::Http11(value) => value.to_string(),
811
10
            OptionKind::Http2(value) => value.to_string(),
812
            OptionKind::Http3(value) => value.to_string(),
813
50
            OptionKind::Insecure(value) => value.to_string(),
814
            OptionKind::IpV4(value) => value.to_string(),
815
            OptionKind::IpV6(value) => value.to_string(),
816
45
            OptionKind::MaxRedirect(value) => value.to_string(),
817
            OptionKind::NetRc(value) => value.to_string(),
818
5
            OptionKind::NetRcFile(filename) => filename.to_string(),
819
            OptionKind::NetRcOptional(value) => value.to_string(),
820
115
            OptionKind::Output(filename) => filename.to_string(),
821
5
            OptionKind::PathAsIs(value) => value.to_string(),
822
30
            OptionKind::Proxy(value) => value.to_string(),
823
55
            OptionKind::Repeat(value) => value.to_string(),
824
20
            OptionKind::Resolve(value) => value.to_string(),
825
60
            OptionKind::Retry(value) => value.to_string(),
826
40
            OptionKind::RetryInterval(value) => value.to_string(),
827
10
            OptionKind::Skip(value) => value.to_string(),
828
            OptionKind::UnixSocket(value) => value.to_string(),
829
60
            OptionKind::User(value) => value.to_string(),
830
340
            OptionKind::Variable(VariableDefinition { name, value, .. }) => {
831
340
                format!("{name}={value}")
832
            }
833
15
            OptionKind::Verbose(value) => value.to_string(),
834
15
            OptionKind::VeryVerbose(value) => value.to_string(),
835
        }
836
    }
837
}
838

            
839
#[derive(Clone, Debug, PartialEq, Eq)]
840
pub enum BooleanOption {
841
    Literal(bool),
842
    Expression(Expr),
843
}
844

            
845
#[derive(Clone, Debug, PartialEq, Eq)]
846
pub enum NaturalOption {
847
    Literal(u64),
848
    Expression(Expr),
849
}
850

            
851
#[derive(Clone, Debug, PartialEq, Eq)]
852
pub enum CountOption {
853
    Literal(Count),
854
    Expression(Expr),
855
}
856

            
857
#[derive(Clone, Debug, PartialEq, Eq)]
858
pub enum DurationOption {
859
    Literal(Duration),
860
    Expression(Expr),
861
}
862

            
863
#[derive(Clone, Debug, PartialEq, Eq)]
864
pub struct VariableDefinition {
865
    pub name: String,
866
    pub space0: Whitespace,
867
    pub space1: Whitespace,
868
    pub value: VariableValue,
869
}
870

            
871
#[derive(Clone, Debug, PartialEq, Eq)]
872
pub enum VariableValue {
873
    Null,
874
    Bool(bool),
875
    Number(Number),
876
    String(Template),
877
}
878

            
879
#[derive(Clone, Debug, PartialEq, Eq)]
880
pub struct Filter {
881
    pub source_info: SourceInfo,
882
    pub value: FilterValue,
883
}
884

            
885
#[derive(Clone, Debug, PartialEq, Eq)]
886
pub enum FilterValue {
887
    Count,
888
    DaysAfterNow,
889
    DaysBeforeNow,
890
    Decode {
891
        space0: Whitespace,
892
        encoding: Template,
893
    },
894
    Format {
895
        space0: Whitespace,
896
        fmt: Template,
897
    },
898
    HtmlEscape,
899
    HtmlUnescape,
900
    JsonPath {
901
        space0: Whitespace,
902
        expr: Template,
903
    },
904
    Nth {
905
        space0: Whitespace,
906
        n: u64,
907
    },
908
    Regex {
909
        space0: Whitespace,
910
        value: RegexValue,
911
    },
912
    Replace {
913
        space0: Whitespace,
914
        old_value: RegexValue,
915
        space1: Whitespace,
916
        new_value: Template,
917
    },
918
    Split {
919
        space0: Whitespace,
920
        sep: Template,
921
    },
922
    ToDate {
923
        space0: Whitespace,
924
        fmt: Template,
925
    },
926
    ToFloat,
927
    ToInt,
928
    UrlDecode,
929
    UrlEncode,
930
    XPath {
931
        space0: Whitespace,
932
        expr: Template,
933
    },
934
}