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
14345
    pub fn source_info(&self) -> SourceInfo {
40
14345
        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
11920
    pub fn querystring_params(&self) -> Vec<KeyValue> {
60
13470
        for section in &self.sections {
61
1775
            if let SectionValue::QueryParams(params, _) = &section.value {
62
225
                return params.clone();
63
            }
64
        }
65
11695
        vec![]
66
    }
67
11880
    pub fn form_params(&self) -> Vec<KeyValue> {
68
13565
        for section in &self.sections {
69
1770
            if let SectionValue::FormParams(params, _) = &section.value {
70
85
                return params.clone();
71
            }
72
        }
73
11795
        vec![]
74
    }
75
11800
    pub fn multipart_form_data(&self) -> Vec<MultipartParam> {
76
13475
        for section in &self.sections {
77
1785
            if let SectionValue::MultipartFormData(params, _) = &section.value {
78
110
                return params.clone();
79
            }
80
        }
81
11690
        vec![]
82
    }
83

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

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

            
102
415
    pub fn options(&self) -> Vec<EntryOption> {
103
490
        for section in &self.sections {
104
225
            if let SectionValue::Options(options) = &section.value {
105
150
                return options.clone();
106
            }
107
        }
108
265
        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
11120
    pub fn captures(&self) -> &[Capture] {
129
11120
        for section in self.sections.iter() {
130
7405
            if let SectionValue::Captures(captures) = &section.value {
131
315
                return captures;
132
            }
133
        }
134
10805
        &[]
135
    }
136

            
137
    /// Returns the asserts list of this spec response.
138
11130
    pub fn asserts(&self) -> &[Assert] {
139
11130
        for section in self.sections.iter() {
140
7620
            if let SectionValue::Asserts(asserts) = &section.value {
141
7330
                return asserts;
142
            }
143
        }
144
3800
        &[]
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
6010
    pub fn name(&self) -> &str {
204
6010
        match self.value {
205
675
            SectionValue::Asserts(_) => "Asserts",
206
260
            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
3570
            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
    File(File),
430
    Hex(Hex),
431
    MultilineString(MultilineString),
432
    Null,
433
    Number(Number),
434
    Placeholder(Placeholder),
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
#[allow(clippy::large_enum_variant)]
514
#[derive(Clone, Debug, PartialEq, Eq)]
515
pub enum MultilineStringKind {
516
    Text(Text),
517
    Json(Text),
518
    Xml(Text),
519
    GraphQl(GraphQl),
520
}
521

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
689
impl SourceInfo {
690
1123505
    pub fn new(start: Pos, end: Pos) -> SourceInfo {
691
1123505
        SourceInfo { start, end }
692
    }
693
}
694

            
695
#[derive(Clone, Debug, PartialEq, Eq)]
696
pub struct Placeholder {
697
    pub space0: Whitespace,
698
    pub expr: Expr,
699
    pub space1: Whitespace,
700
}
701

            
702
#[derive(Clone, Debug, PartialEq, Eq)]
703
pub struct Expr {
704
    pub source_info: SourceInfo,
705
    pub kind: ExprKind,
706
}
707

            
708
#[derive(Clone, Debug, PartialEq, Eq)]
709
pub enum ExprKind {
710
    Variable(Variable),
711
    Function(Function),
712
}
713

            
714
#[derive(Clone, Debug, PartialEq, Eq)]
715
pub struct Variable {
716
    pub name: String,
717
    pub source_info: SourceInfo,
718
}
719

            
720
#[derive(Clone, Debug, PartialEq, Eq)]
721
pub enum Function {
722
    NewDate,
723
    NewUuid,
724
}
725

            
726
/// Check that variable name is not reserved
727
/// (would conflicts with an existing function)
728
640
pub fn is_variable_reserved(name: &str) -> bool {
729
640
    ["getEnv", "newDate", "newUuid"].contains(&name)
730
}
731

            
732
#[derive(Clone, Debug, PartialEq, Eq)]
733
pub struct EntryOption {
734
    pub line_terminators: Vec<LineTerminator>,
735
    pub space0: Whitespace,
736
    pub space1: Whitespace,
737
    pub space2: Whitespace,
738
    pub kind: OptionKind,
739
    pub line_terminator0: LineTerminator,
740
}
741

            
742
#[derive(Clone, Debug, PartialEq, Eq)]
743
pub enum OptionKind {
744
    AwsSigV4(Template),
745
    CaCertificate(Template),
746
    ClientCert(Template),
747
    ClientKey(Template),
748
    Compressed(BooleanOption),
749
    ConnectTo(Template),
750
    ConnectTimeout(DurationOption),
751
    Delay(DurationOption),
752
    Http10(BooleanOption),
753
    Http11(BooleanOption),
754
    Http2(BooleanOption),
755
    Http3(BooleanOption),
756
    Insecure(BooleanOption),
757
    IpV4(BooleanOption),
758
    IpV6(BooleanOption),
759
    FollowLocation(BooleanOption),
760
    FollowLocationTrusted(BooleanOption),
761
    LimitRate(NaturalOption),
762
    MaxRedirect(CountOption),
763
    NetRc(BooleanOption),
764
    NetRcFile(Template),
765
    NetRcOptional(BooleanOption),
766
    Output(Template),
767
    PathAsIs(BooleanOption),
768
    Proxy(Template),
769
    Repeat(CountOption),
770
    Resolve(Template),
771
    Retry(CountOption),
772
    RetryInterval(DurationOption),
773
    Skip(BooleanOption),
774
    UnixSocket(Template),
775
    User(Template),
776
    Variable(VariableDefinition),
777
    Verbose(BooleanOption),
778
    VeryVerbose(BooleanOption),
779
}
780

            
781
impl OptionKind {
782
3340
    pub fn name(&self) -> &'static str {
783
3340
        match self {
784
40
            OptionKind::AwsSigV4(_) => "aws-sigv4",
785
55
            OptionKind::CaCertificate(_) => "cacert",
786
65
            OptionKind::ClientCert(_) => "cert",
787
50
            OptionKind::ClientKey(_) => "key",
788
410
            OptionKind::Compressed(_) => "compressed",
789
65
            OptionKind::ConnectTo(_) => "connect-to",
790
30
            OptionKind::ConnectTimeout(_) => "connect-timeout",
791
90
            OptionKind::Delay(_) => "delay",
792
390
            OptionKind::FollowLocation(_) => "location",
793
50
            OptionKind::FollowLocationTrusted(_) => "location-trusted",
794
105
            OptionKind::Http10(_) => "http1.0",
795
75
            OptionKind::Http11(_) => "http1.1",
796
40
            OptionKind::Http2(_) => "http2",
797
30
            OptionKind::Http3(_) => "http3",
798
95
            OptionKind::Insecure(_) => "insecure",
799
30
            OptionKind::IpV4(_) => "ipv4",
800
30
            OptionKind::IpV6(_) => "ipv6",
801
35
            OptionKind::LimitRate(_) => "limit-rate",
802
85
            OptionKind::MaxRedirect(_) => "max-redirs",
803
30
            OptionKind::NetRc(_) => "netrc",
804
35
            OptionKind::NetRcFile(_) => "netrc-file",
805
30
            OptionKind::NetRcOptional(_) => "netrc-optional",
806
145
            OptionKind::Output(_) => "output",
807
35
            OptionKind::PathAsIs(_) => "path-as-is",
808
70
            OptionKind::Proxy(_) => "proxy",
809
100
            OptionKind::Repeat(_) => "repeat",
810
50
            OptionKind::Resolve(_) => "resolve",
811
140
            OptionKind::Retry(_) => "retry",
812
110
            OptionKind::RetryInterval(_) => "retry-interval",
813
40
            OptionKind::Skip(_) => "skip",
814
30
            OptionKind::UnixSocket(_) => "unix-socket",
815
100
            OptionKind::User(_) => "user",
816
535
            OptionKind::Variable(_) => "variable",
817
70
            OptionKind::Verbose(_) => "verbose",
818
50
            OptionKind::VeryVerbose(_) => "very-verbose",
819
        }
820
    }
821

            
822
1680
    pub fn value_as_str(&self) -> String {
823
1680
        match self {
824
10
            OptionKind::AwsSigV4(value) => value.to_string(),
825
25
            OptionKind::CaCertificate(filename) => filename.to_string(),
826
20
            OptionKind::ClientCert(filename) => filename.to_string(),
827
20
            OptionKind::ClientKey(filename) => filename.to_string(),
828
285
            OptionKind::Compressed(value) => value.to_string(),
829
35
            OptionKind::ConnectTo(value) => value.to_string(),
830
            OptionKind::ConnectTimeout(value) => value.to_string(),
831
30
            OptionKind::Delay(value) => value.to_string(),
832
275
            OptionKind::FollowLocation(value) => value.to_string(),
833
15
            OptionKind::FollowLocationTrusted(value) => value.to_string(),
834
50
            OptionKind::Http10(value) => value.to_string(),
835
30
            OptionKind::Http11(value) => value.to_string(),
836
10
            OptionKind::Http2(value) => value.to_string(),
837
            OptionKind::Http3(value) => value.to_string(),
838
50
            OptionKind::Insecure(value) => value.to_string(),
839
            OptionKind::IpV4(value) => value.to_string(),
840
            OptionKind::IpV6(value) => value.to_string(),
841
5
            OptionKind::LimitRate(value) => value.to_string(),
842
45
            OptionKind::MaxRedirect(value) => value.to_string(),
843
            OptionKind::NetRc(value) => value.to_string(),
844
5
            OptionKind::NetRcFile(filename) => filename.to_string(),
845
            OptionKind::NetRcOptional(value) => value.to_string(),
846
115
            OptionKind::Output(filename) => filename.to_string(),
847
5
            OptionKind::PathAsIs(value) => value.to_string(),
848
30
            OptionKind::Proxy(value) => value.to_string(),
849
55
            OptionKind::Repeat(value) => value.to_string(),
850
20
            OptionKind::Resolve(value) => value.to_string(),
851
60
            OptionKind::Retry(value) => value.to_string(),
852
40
            OptionKind::RetryInterval(value) => value.to_string(),
853
10
            OptionKind::Skip(value) => value.to_string(),
854
            OptionKind::UnixSocket(value) => value.to_string(),
855
60
            OptionKind::User(value) => value.to_string(),
856
345
            OptionKind::Variable(VariableDefinition { name, value, .. }) => {
857
345
                format!("{name}={value}")
858
            }
859
15
            OptionKind::Verbose(value) => value.to_string(),
860
15
            OptionKind::VeryVerbose(value) => value.to_string(),
861
        }
862
    }
863
}
864

            
865
#[derive(Clone, Debug, PartialEq, Eq)]
866
pub enum BooleanOption {
867
    Literal(bool),
868
    Placeholder(Placeholder),
869
}
870

            
871
#[derive(Clone, Debug, PartialEq, Eq)]
872
pub enum NaturalOption {
873
    Literal(u64),
874
    Placeholder(Placeholder),
875
}
876

            
877
#[derive(Clone, Debug, PartialEq, Eq)]
878
pub enum CountOption {
879
    Literal(Count),
880
    Placeholder(Placeholder),
881
}
882

            
883
#[derive(Clone, Debug, PartialEq, Eq)]
884
pub enum DurationOption {
885
    Literal(Duration),
886
    Placeholder(Placeholder),
887
}
888

            
889
#[derive(Clone, Debug, PartialEq, Eq)]
890
pub struct VariableDefinition {
891
    pub source_info: SourceInfo,
892
    pub name: String,
893
    pub space0: Whitespace,
894
    pub space1: Whitespace,
895
    pub value: VariableValue,
896
}
897

            
898
#[derive(Clone, Debug, PartialEq, Eq)]
899
pub enum VariableValue {
900
    Null,
901
    Bool(bool),
902
    Number(Number),
903
    String(Template),
904
}
905

            
906
#[derive(Clone, Debug, PartialEq, Eq)]
907
pub struct Filter {
908
    pub source_info: SourceInfo,
909
    pub value: FilterValue,
910
}
911

            
912
#[derive(Clone, Debug, PartialEq, Eq)]
913
pub enum FilterValue {
914
    Count,
915
    DaysAfterNow,
916
    DaysBeforeNow,
917
    Decode {
918
        space0: Whitespace,
919
        encoding: Template,
920
    },
921
    Format {
922
        space0: Whitespace,
923
        fmt: Template,
924
    },
925
    HtmlEscape,
926
    HtmlUnescape,
927
    JsonPath {
928
        space0: Whitespace,
929
        expr: Template,
930
    },
931
    Nth {
932
        space0: Whitespace,
933
        n: u64,
934
    },
935
    Regex {
936
        space0: Whitespace,
937
        value: RegexValue,
938
    },
939
    Replace {
940
        space0: Whitespace,
941
        old_value: RegexValue,
942
        space1: Whitespace,
943
        new_value: Template,
944
    },
945
    Split {
946
        space0: Whitespace,
947
        sep: Template,
948
    },
949
    ToDate {
950
        space0: Whitespace,
951
        fmt: Template,
952
    },
953
    ToFloat,
954
    ToInt,
955
    UrlDecode,
956
    UrlEncode,
957
    XPath {
958
        space0: Whitespace,
959
        expr: Template,
960
    },
961
}