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
    Ntlm(BooleanOption),
68
    Output(Template),
69
    PathAsIs(BooleanOption),
70
    PinnedPublicKey(Template),
71
    Proxy(Template),
72
    Repeat(CountOption),
73
    Resolve(Template),
74
    Retry(CountOption),
75
    RetryInterval(DurationOption),
76
    Skip(BooleanOption),
77
    UnixSocket(Template),
78
    User(Template),
79
    Variable(VariableDefinition),
80
    Verbose(BooleanOption),
81
    Verbosity(VerbosityOption),
82
    VeryVerbose(BooleanOption),
83
}
84

            
85
impl OptionKind {
86
    /// Returns the Hurl string identifier of this option.
87
5310
    pub fn identifier(&self) -> &'static str {
88
5310
        match self {
89
50
            OptionKind::AwsSigV4(_) => "aws-sigv4",
90
75
            OptionKind::CaCertificate(_) => "cacert",
91
80
            OptionKind::ClientCert(_) => "cert",
92
60
            OptionKind::ClientKey(_) => "key",
93
420
            OptionKind::Compressed(_) => "compressed",
94
75
            OptionKind::ConnectTo(_) => "connect-to",
95
40
            OptionKind::ConnectTimeout(_) => "connect-timeout",
96
620
            OptionKind::Delay(_) => "delay",
97
45
            OptionKind::Digest(_) => "digest",
98
45
            OptionKind::FailWithBody(_) => "fail-with-body",
99
470
            OptionKind::FollowLocation(_) => "location",
100
60
            OptionKind::FollowLocationTrusted(_) => "location-trusted",
101
80
            OptionKind::Header(_) => "header",
102
130
            OptionKind::Http10(_) => "http1.0",
103
100
            OptionKind::Http11(_) => "http1.1",
104
60
            OptionKind::Http2(_) => "http2",
105
30
            OptionKind::Http2PriorKnowledge(_) => "http2-prior-knowledge",
106
40
            OptionKind::Http3(_) => "http3",
107
110
            OptionKind::Insecure(_) => "insecure",
108
40
            OptionKind::IpV4(_) => "ipv4",
109
40
            OptionKind::IpV6(_) => "ipv6",
110
45
            OptionKind::LimitRate(_) => "limit-rate",
111
95
            OptionKind::MaxRedirect(_) => "max-redirs",
112
40
            OptionKind::MaxTime(_) => "max-time",
113
50
            OptionKind::Negotiate(_) => "negotiate",
114
40
            OptionKind::NetRc(_) => "netrc",
115
45
            OptionKind::NetRcFile(_) => "netrc-file",
116
40
            OptionKind::NetRcOptional(_) => "netrc-optional",
117
65
            OptionKind::NoHeader(_) => "no-header",
118
55
            OptionKind::Ntlm(_) => "ntlm",
119
190
            OptionKind::Output(_) => "output",
120
45
            OptionKind::PathAsIs(_) => "path-as-is",
121
50
            OptionKind::PinnedPublicKey(_) => "pinnedpubkey",
122
80
            OptionKind::Proxy(_) => "proxy",
123
175
            OptionKind::Repeat(_) => "repeat",
124
60
            OptionKind::Resolve(_) => "resolve",
125
175
            OptionKind::Retry(_) => "retry",
126
145
            OptionKind::RetryInterval(_) => "retry-interval",
127
50
            OptionKind::Skip(_) => "skip",
128
40
            OptionKind::UnixSocket(_) => "unix-socket",
129
160
            OptionKind::User(_) => "user",
130
685
            OptionKind::Variable(_) => "variable",
131
195
            OptionKind::Verbose(_) => "verbose",
132
55
            OptionKind::Verbosity(_) => "verbosity",
133
60
            OptionKind::VeryVerbose(_) => "very-verbose",
134
        }
135
    }
136
}
137

            
138
impl fmt::Display for OptionKind {
139
2650
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140
2650
        let value = match self {
141
10
            OptionKind::AwsSigV4(value) => value.to_string(),
142
35
            OptionKind::CaCertificate(filename) => filename.to_string(),
143
20
            OptionKind::ClientCert(filename) => filename.to_string(),
144
20
            OptionKind::ClientKey(filename) => filename.to_string(),
145
285
            OptionKind::Compressed(value) => value.to_string(),
146
35
            OptionKind::ConnectTo(value) => value.to_string(),
147
            OptionKind::ConnectTimeout(value) => value.to_string(),
148
500
            OptionKind::Delay(value) => value.to_string(),
149
5
            OptionKind::Digest(value) => value.to_string(),
150
5
            OptionKind::FailWithBody(value) => value.to_string(),
151
325
            OptionKind::FollowLocation(value) => value.to_string(),
152
15
            OptionKind::FollowLocationTrusted(value) => value.to_string(),
153
40
            OptionKind::Header(value) => value.to_string(),
154
60
            OptionKind::Http10(value) => value.to_string(),
155
40
            OptionKind::Http11(value) => value.to_string(),
156
20
            OptionKind::Http2(value) => value.to_string(),
157
10
            OptionKind::Http2PriorKnowledge(value) => value.to_string(),
158
            OptionKind::Http3(value) => value.to_string(),
159
50
            OptionKind::Insecure(value) => value.to_string(),
160
            OptionKind::IpV4(value) => value.to_string(),
161
            OptionKind::IpV6(value) => value.to_string(),
162
5
            OptionKind::LimitRate(value) => value.to_string(),
163
45
            OptionKind::MaxRedirect(value) => value.to_string(),
164
            OptionKind::MaxTime(value) => value.to_string(),
165
            OptionKind::Negotiate(value) => value.to_string(),
166
            OptionKind::NetRc(value) => value.to_string(),
167
5
            OptionKind::NetRcFile(filename) => filename.to_string(),
168
            OptionKind::NetRcOptional(value) => value.to_string(),
169
25
            OptionKind::NoHeader(value) => value.to_string(),
170
5
            OptionKind::Ntlm(value) => value.to_string(),
171
150
            OptionKind::Output(filename) => filename.to_string(),
172
5
            OptionKind::PathAsIs(value) => value.to_string(),
173
10
            OptionKind::PinnedPublicKey(value) => value.to_string(),
174
30
            OptionKind::Proxy(value) => value.to_string(),
175
105
            OptionKind::Repeat(value) => value.to_string(),
176
20
            OptionKind::Resolve(value) => value.to_string(),
177
70
            OptionKind::Retry(value) => value.to_string(),
178
55
            OptionKind::RetryInterval(value) => value.to_string(),
179
10
            OptionKind::Skip(value) => value.to_string(),
180
            OptionKind::UnixSocket(value) => value.to_string(),
181
90
            OptionKind::User(value) => value.to_string(),
182
430
            OptionKind::Variable(value) => value.to_string(),
183
85
            OptionKind::Verbose(value) => value.to_string(),
184
15
            OptionKind::Verbosity(value) => value.to_string(),
185
15
            OptionKind::VeryVerbose(value) => value.to_string(),
186
        };
187
2650
        write!(f, "{}: {}", self.identifier(), value)
188
    }
189
}
190

            
191
#[derive(Clone, Debug, PartialEq, Eq)]
192
pub enum BooleanOption {
193
    Literal(bool),
194
    Placeholder(Placeholder),
195
}
196

            
197
impl fmt::Display for BooleanOption {
198
935
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199
935
        match self {
200
930
            BooleanOption::Literal(v) => write!(f, "{v}"),
201
5
            BooleanOption::Placeholder(v) => write!(f, "{v}"),
202
        }
203
    }
204
}
205

            
206
#[derive(Clone, Debug, PartialEq, Eq)]
207
pub enum NaturalOption {
208
    Literal(U64),
209
    Placeholder(Placeholder),
210
}
211

            
212
impl fmt::Display for NaturalOption {
213
5
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214
5
        match self {
215
5
            NaturalOption::Literal(v) => write!(f, "{v}"),
216
            NaturalOption::Placeholder(v) => write!(f, "{v}"),
217
        }
218
    }
219
}
220

            
221
#[derive(Clone, Debug, PartialEq, Eq)]
222
pub enum CountOption {
223
    Literal(Count),
224
    Placeholder(Placeholder),
225
}
226

            
227
impl fmt::Display for CountOption {
228
220
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229
220
        match self {
230
210
            CountOption::Literal(v) => write!(f, "{v}"),
231
10
            CountOption::Placeholder(v) => write!(f, "{v}"),
232
        }
233
    }
234
}
235

            
236
#[derive(Clone, Debug, PartialEq, Eq)]
237
pub enum DurationOption {
238
    Literal(Duration),
239
    Placeholder(Placeholder),
240
}
241

            
242
impl fmt::Display for DurationOption {
243
555
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244
555
        match self {
245
550
            DurationOption::Literal(v) => write!(f, "{v}"),
246
5
            DurationOption::Placeholder(v) => write!(f, "{v}"),
247
        }
248
    }
249
}
250

            
251
/// Represent a duration
252
#[derive(Clone, Debug, PartialEq, Eq)]
253
pub struct Duration {
254
    pub value: U64,
255
    pub unit: Option<DurationUnit>,
256
}
257

            
258
impl Duration {
259
760
    pub fn new(value: U64, unit: Option<DurationUnit>) -> Duration {
260
760
        Duration { value, unit }
261
    }
262
}
263

            
264
impl fmt::Display for Duration {
265
550
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266
550
        let unit = if let Some(value) = self.unit {
267
550
            value.to_string()
268
        } else {
269
            String::new()
270
        };
271
550
        write!(f, "{}{unit}", self.value)
272
    }
273
}
274

            
275
#[derive(Clone, Debug, PartialEq, Eq)]
276
pub struct VariableDefinition {
277
    pub source_info: SourceInfo,
278
    pub name: String,
279
    pub space0: Whitespace,
280
    pub space1: Whitespace,
281
    pub value: VariableValue,
282
}
283

            
284
impl fmt::Display for VariableDefinition {
285
430
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286
430
        write!(f, "{}={}", self.name, self.value)
287
    }
288
}
289

            
290
#[derive(Clone, Debug, PartialEq, Eq)]
291
pub enum VariableValue {
292
    Null,
293
    Bool(bool),
294
    Number(Number),
295
    String(Template),
296
}
297

            
298
impl fmt::Display for VariableValue {
299
430
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300
430
        let s = match self {
301
            VariableValue::Null => "null".to_string(),
302
5
            VariableValue::Bool(value) => value.to_string(),
303
155
            VariableValue::Number(n) => n.to_string(),
304
270
            VariableValue::String(s) => s.to_string(),
305
        };
306
430
        write!(f, "{s}")
307
    }
308
}
309

            
310
impl ToSource for VariableValue {
311
90
    fn to_source(&self) -> SourceString {
312
90
        match self {
313
10
            VariableValue::Null => "null".to_source(),
314
10
            VariableValue::Bool(value) => value.to_string().to_source(),
315
20
            VariableValue::Number(value) => value.to_source(),
316
50
            VariableValue::String(value) => value.to_source(),
317
        }
318
    }
319
}
320

            
321
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
322
pub enum VerbosityOption {
323
    Brief,
324
    Verbose,
325
    Debug,
326
}
327

            
328
impl fmt::Display for VerbosityOption {
329
35
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330
35
        write!(f, "{}", self.identifier())
331
    }
332
}
333

            
334
impl VerbosityOption {
335
55
    pub fn identifier(&self) -> &'static str {
336
55
        match self {
337
45
            VerbosityOption::Brief => "brief",
338
5
            VerbosityOption::Verbose => "verbose",
339
5
            VerbosityOption::Debug => "debug",
340
        }
341
    }
342
}