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 std::fmt;
19

            
20
use crate::types::{Count, DurationUnit, SourceString, ToSource};
21

            
22
use super::primitive::{
23
    LineTerminator, Number, Placeholder, SourceInfo, Template, U64, Whitespace,
24
};
25

            
26
#[derive(Clone, Debug, PartialEq, Eq)]
27
pub struct EntryOption {
28
    pub line_terminators: Vec<LineTerminator>,
29
    pub space0: Whitespace,
30
    pub space1: Whitespace,
31
    pub space2: Whitespace,
32
    pub kind: OptionKind,
33
    pub line_terminator0: LineTerminator,
34
}
35

            
36
#[derive(Clone, Debug, PartialEq, Eq)]
37
pub enum OptionKind {
38
    AwsSigV4(Template),
39
    CaCertificate(Template),
40
    ClientCert(Template),
41
    ClientKey(Template),
42
    Compressed(BooleanOption),
43
    ConnectTo(Template),
44
    ConnectTimeout(DurationOption),
45
    Delay(DurationOption),
46
    Digest(BooleanOption),
47
    FailWithBody(BooleanOption),
48
    FollowLocation(BooleanOption),
49
    FollowLocationTrusted(BooleanOption),
50
    Header(Template),
51
    Http10(BooleanOption),
52
    Http11(BooleanOption),
53
    Http2(BooleanOption),
54
    Http2PriorKnowledge(BooleanOption),
55
    Http3(BooleanOption),
56
    Insecure(BooleanOption),
57
    IpV4(BooleanOption),
58
    IpV6(BooleanOption),
59
    LimitRate(NaturalOption),
60
    MaxRedirect(CountOption),
61
    MaxTime(DurationOption),
62
    Negotiate(BooleanOption),
63
    NetRc(BooleanOption),
64
    NetRcFile(Template),
65
    NetRcOptional(BooleanOption),
66
    NoHeader(Template),
67
    NoJsonpathCoercion(BooleanOption),
68
    Ntlm(BooleanOption),
69
    Output(Template),
70
    PathAsIs(BooleanOption),
71
    PinnedPublicKey(Template),
72
    Proxy(Template),
73
    Repeat(CountOption),
74
    Resolve(Template),
75
    Retry(CountOption),
76
    RetryInterval(DurationOption),
77
    Skip(BooleanOption),
78
    TlsMax(Template),
79
    TlsMin(Template),
80
    UnixSocket(Template),
81
    User(Template),
82
    Variable(VariableDefinition),
83
    Verbose(BooleanOption),
84
    Verbosity(VerbosityOption),
85
    VeryVerbose(BooleanOption),
86
}
87

            
88
impl OptionKind {
89
    /// Returns the Hurl string identifier of this option.
90
4508
    pub fn identifier(&self) -> &'static str {
91
4508
        match self {
92
40
            OptionKind::AwsSigV4(_) => "aws-sigv4",
93
60
            OptionKind::CaCertificate(_) => "cacert",
94
64
            OptionKind::ClientCert(_) => "cert",
95
48
            OptionKind::ClientKey(_) => "key",
96
336
            OptionKind::Compressed(_) => "compressed",
97
60
            OptionKind::ConnectTo(_) => "connect-to",
98
32
            OptionKind::ConnectTimeout(_) => "connect-timeout",
99
496
            OptionKind::Delay(_) => "delay",
100
36
            OptionKind::Digest(_) => "digest",
101
36
            OptionKind::FailWithBody(_) => "fail-with-body",
102
376
            OptionKind::FollowLocation(_) => "location",
103
48
            OptionKind::FollowLocationTrusted(_) => "location-trusted",
104
112
            OptionKind::Header(_) => "header",
105
104
            OptionKind::Http10(_) => "http1.0",
106
80
            OptionKind::Http11(_) => "http1.1",
107
64
            OptionKind::Http2(_) => "http2",
108
32
            OptionKind::Http2PriorKnowledge(_) => "http2-prior-knowledge",
109
32
            OptionKind::Http3(_) => "http3",
110
112
            OptionKind::Insecure(_) => "insecure",
111
32
            OptionKind::IpV4(_) => "ipv4",
112
32
            OptionKind::IpV6(_) => "ipv6",
113
36
            OptionKind::LimitRate(_) => "limit-rate",
114
76
            OptionKind::MaxRedirect(_) => "max-redirs",
115
32
            OptionKind::MaxTime(_) => "max-time",
116
40
            OptionKind::Negotiate(_) => "negotiate",
117
32
            OptionKind::NetRc(_) => "netrc",
118
36
            OptionKind::NetRcFile(_) => "netrc-file",
119
32
            OptionKind::NetRcOptional(_) => "netrc-optional",
120
52
            OptionKind::NoHeader(_) => "no-header",
121
24
            OptionKind::NoJsonpathCoercion(_) => "no-jsonpath-coercion",
122
44
            OptionKind::Ntlm(_) => "ntlm",
123
168
            OptionKind::Output(_) => "output",
124
36
            OptionKind::PathAsIs(_) => "path-as-is",
125
40
            OptionKind::PinnedPublicKey(_) => "pinnedpubkey",
126
88
            OptionKind::Proxy(_) => "proxy",
127
140
            OptionKind::Repeat(_) => "repeat",
128
48
            OptionKind::Resolve(_) => "resolve",
129
140
            OptionKind::Retry(_) => "retry",
130
116
            OptionKind::RetryInterval(_) => "retry-interval",
131
52
            OptionKind::Skip(_) => "skip",
132
44
            OptionKind::TlsMax(_) => "tls-max",
133
44
            OptionKind::TlsMin(_) => "tls-min",
134
32
            OptionKind::UnixSocket(_) => "unix-socket",
135
128
            OptionKind::User(_) => "user",
136
548
            OptionKind::Variable(_) => "variable",
137
156
            OptionKind::Verbose(_) => "verbose",
138
44
            OptionKind::Verbosity(_) => "verbosity",
139
48
            OptionKind::VeryVerbose(_) => "very-verbose",
140
        }
141
    }
142
}
143

            
144
impl fmt::Display for OptionKind {
145
2300
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146
2300
        let value = match self {
147
8
            OptionKind::AwsSigV4(value) => value.to_string(),
148
28
            OptionKind::CaCertificate(filename) => filename.to_string(),
149
16
            OptionKind::ClientCert(filename) => filename.to_string(),
150
16
            OptionKind::ClientKey(filename) => filename.to_string(),
151
228
            OptionKind::Compressed(value) => value.to_string(),
152
28
            OptionKind::ConnectTo(value) => value.to_string(),
153
            OptionKind::ConnectTimeout(value) => value.to_string(),
154
400
            OptionKind::Delay(value) => value.to_string(),
155
4
            OptionKind::Digest(value) => value.to_string(),
156
4
            OptionKind::FailWithBody(value) => value.to_string(),
157
260
            OptionKind::FollowLocation(value) => value.to_string(),
158
12
            OptionKind::FollowLocationTrusted(value) => value.to_string(),
159
80
            OptionKind::Header(value) => value.to_string(),
160
48
            OptionKind::Http10(value) => value.to_string(),
161
32
            OptionKind::Http11(value) => value.to_string(),
162
32
            OptionKind::Http2(value) => value.to_string(),
163
16
            OptionKind::Http2PriorKnowledge(value) => value.to_string(),
164
            OptionKind::Http3(value) => value.to_string(),
165
64
            OptionKind::Insecure(value) => value.to_string(),
166
            OptionKind::IpV4(value) => value.to_string(),
167
            OptionKind::IpV6(value) => value.to_string(),
168
4
            OptionKind::LimitRate(value) => value.to_string(),
169
36
            OptionKind::MaxRedirect(value) => value.to_string(),
170
            OptionKind::MaxTime(value) => value.to_string(),
171
            OptionKind::Negotiate(value) => value.to_string(),
172
            OptionKind::NetRc(value) => value.to_string(),
173
4
            OptionKind::NetRcFile(filename) => filename.to_string(),
174
            OptionKind::NetRcOptional(value) => value.to_string(),
175
20
            OptionKind::NoHeader(value) => value.to_string(),
176
8
            OptionKind::NoJsonpathCoercion(value) => value.to_string(),
177
4
            OptionKind::Ntlm(value) => value.to_string(),
178
136
            OptionKind::Output(filename) => filename.to_string(),
179
4
            OptionKind::PathAsIs(value) => value.to_string(),
180
8
            OptionKind::PinnedPublicKey(value) => value.to_string(),
181
48
            OptionKind::Proxy(value) => value.to_string(),
182
84
            OptionKind::Repeat(value) => value.to_string(),
183
16
            OptionKind::Resolve(value) => value.to_string(),
184
56
            OptionKind::Retry(value) => value.to_string(),
185
44
            OptionKind::RetryInterval(value) => value.to_string(),
186
20
            OptionKind::Skip(value) => value.to_string(),
187
12
            OptionKind::TlsMax(value) => value.to_string(),
188
12
            OptionKind::TlsMin(value) => value.to_string(),
189
            OptionKind::UnixSocket(value) => value.to_string(),
190
72
            OptionKind::User(value) => value.to_string(),
191
344
            OptionKind::Variable(value) => value.to_string(),
192
68
            OptionKind::Verbose(value) => value.to_string(),
193
12
            OptionKind::Verbosity(value) => value.to_string(),
194
12
            OptionKind::VeryVerbose(value) => value.to_string(),
195
        };
196
2300
        write!(f, "{}: {}", self.identifier(), value)
197
    }
198
}
199

            
200
#[derive(Clone, Debug, PartialEq, Eq)]
201
pub enum BooleanOption {
202
    Literal(bool),
203
    Placeholder(Placeholder),
204
}
205

            
206
impl fmt::Display for BooleanOption {
207
816
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208
816
        match self {
209
800
            BooleanOption::Literal(v) => write!(f, "{v}"),
210
16
            BooleanOption::Placeholder(v) => write!(f, "{v}"),
211
        }
212
    }
213
}
214

            
215
#[derive(Clone, Debug, PartialEq, Eq)]
216
pub enum NaturalOption {
217
    Literal(U64),
218
    Placeholder(Placeholder),
219
}
220

            
221
impl fmt::Display for NaturalOption {
222
4
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223
4
        match self {
224
4
            NaturalOption::Literal(v) => write!(f, "{v}"),
225
            NaturalOption::Placeholder(v) => write!(f, "{v}"),
226
        }
227
    }
228
}
229

            
230
#[derive(Clone, Debug, PartialEq, Eq)]
231
pub enum CountOption {
232
    Literal(Count),
233
    Placeholder(Placeholder),
234
}
235

            
236
impl fmt::Display for CountOption {
237
176
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238
176
        match self {
239
168
            CountOption::Literal(v) => write!(f, "{v}"),
240
8
            CountOption::Placeholder(v) => write!(f, "{v}"),
241
        }
242
    }
243
}
244

            
245
#[derive(Clone, Debug, PartialEq, Eq)]
246
pub enum DurationOption {
247
    Literal(Duration),
248
    Placeholder(Placeholder),
249
}
250

            
251
impl fmt::Display for DurationOption {
252
444
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253
444
        match self {
254
440
            DurationOption::Literal(v) => write!(f, "{v}"),
255
4
            DurationOption::Placeholder(v) => write!(f, "{v}"),
256
        }
257
    }
258
}
259

            
260
/// Represent a duration
261
#[derive(Clone, Debug, PartialEq, Eq)]
262
pub struct Duration {
263
    pub value: U64,
264
    pub unit: Option<DurationUnit>,
265
}
266

            
267
impl Duration {
268
608
    pub fn new(value: U64, unit: Option<DurationUnit>) -> Duration {
269
608
        Duration { value, unit }
270
    }
271
}
272

            
273
impl fmt::Display for Duration {
274
440
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275
440
        let unit = if let Some(value) = self.unit {
276
440
            value.to_string()
277
        } else {
278
            String::new()
279
        };
280
440
        write!(f, "{}{unit}", self.value)
281
    }
282
}
283

            
284
#[derive(Clone, Debug, PartialEq, Eq)]
285
pub struct VariableDefinition {
286
    pub source_info: SourceInfo,
287
    pub name: String,
288
    pub space0: Whitespace,
289
    pub space1: Whitespace,
290
    pub value: VariableValue,
291
}
292

            
293
impl fmt::Display for VariableDefinition {
294
344
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295
344
        write!(f, "{}={}", self.name, self.value)
296
    }
297
}
298

            
299
#[derive(Clone, Debug, PartialEq, Eq)]
300
pub enum VariableValue {
301
    Null,
302
    Bool(bool),
303
    Number(Number),
304
    String(Template),
305
}
306

            
307
impl fmt::Display for VariableValue {
308
344
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309
344
        let s = match self {
310
            VariableValue::Null => "null".to_string(),
311
4
            VariableValue::Bool(value) => value.to_string(),
312
124
            VariableValue::Number(n) => n.to_string(),
313
216
            VariableValue::String(s) => s.to_string(),
314
        };
315
344
        write!(f, "{s}")
316
    }
317
}
318

            
319
impl ToSource for VariableValue {
320
72
    fn to_source(&self) -> SourceString {
321
72
        match self {
322
8
            VariableValue::Null => "null".to_source(),
323
8
            VariableValue::Bool(value) => value.to_string().to_source(),
324
16
            VariableValue::Number(value) => value.to_source(),
325
40
            VariableValue::String(value) => value.to_source(),
326
        }
327
    }
328
}
329

            
330
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
331
pub enum VerbosityOption {
332
    Brief,
333
    Verbose,
334
    Debug,
335
}
336

            
337
impl fmt::Display for VerbosityOption {
338
28
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339
28
        write!(f, "{}", self.identifier())
340
    }
341
}
342

            
343
impl VerbosityOption {
344
44
    pub fn identifier(&self) -> &'static str {
345
44
        match self {
346
36
            VerbosityOption::Brief => "brief",
347
4
            VerbosityOption::Verbose => "verbose",
348
4
            VerbosityOption::Debug => "debug",
349
        }
350
    }
351
}