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, Duration, SourceString, ToSource};
21

            
22
use super::primitive::{
23
    LineTerminator, Number, Placeholder, SourceInfo, Template, Whitespace, U64,
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
    Header(Template),
48
    Http10(BooleanOption),
49
    Http11(BooleanOption),
50
    Http2(BooleanOption),
51
    Http3(BooleanOption),
52
    Insecure(BooleanOption),
53
    IpV4(BooleanOption),
54
    IpV6(BooleanOption),
55
    FollowLocation(BooleanOption),
56
    FollowLocationTrusted(BooleanOption),
57
    LimitRate(NaturalOption),
58
    MaxRedirect(CountOption),
59
    MaxTime(DurationOption),
60
    Negotiate(BooleanOption),
61
    NetRc(BooleanOption),
62
    NetRcFile(Template),
63
    NetRcOptional(BooleanOption),
64
    Ntlm(BooleanOption),
65
    Output(Template),
66
    PathAsIs(BooleanOption),
67
    PinnedPublicKey(Template),
68
    Proxy(Template),
69
    Repeat(CountOption),
70
    Resolve(Template),
71
    Retry(CountOption),
72
    RetryInterval(DurationOption),
73
    Skip(BooleanOption),
74
    UnixSocket(Template),
75
    User(Template),
76
    Variable(VariableDefinition),
77
    Verbose(BooleanOption),
78
    Verbosity(VerbosityOption),
79
    VeryVerbose(BooleanOption),
80
}
81

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

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

            
182
#[derive(Clone, Debug, PartialEq, Eq)]
183
pub enum BooleanOption {
184
    Literal(bool),
185
    Placeholder(Placeholder),
186
}
187

            
188
impl fmt::Display for BooleanOption {
189
900
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190
900
        match self {
191
895
            BooleanOption::Literal(v) => write!(f, "{v}"),
192
5
            BooleanOption::Placeholder(v) => write!(f, "{v}"),
193
        }
194
    }
195
}
196

            
197
#[derive(Clone, Debug, PartialEq, Eq)]
198
pub enum NaturalOption {
199
    Literal(U64),
200
    Placeholder(Placeholder),
201
}
202

            
203
impl fmt::Display for NaturalOption {
204
5
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205
5
        match self {
206
5
            NaturalOption::Literal(v) => write!(f, "{v}"),
207
            NaturalOption::Placeholder(v) => write!(f, "{v}"),
208
        }
209
    }
210
}
211

            
212
#[derive(Clone, Debug, PartialEq, Eq)]
213
pub enum CountOption {
214
    Literal(Count),
215
    Placeholder(Placeholder),
216
}
217

            
218
impl fmt::Display for CountOption {
219
205
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220
205
        match self {
221
195
            CountOption::Literal(v) => write!(f, "{v}"),
222
10
            CountOption::Placeholder(v) => write!(f, "{v}"),
223
        }
224
    }
225
}
226

            
227
#[derive(Clone, Debug, PartialEq, Eq)]
228
pub enum DurationOption {
229
    Literal(Duration),
230
    Placeholder(Placeholder),
231
}
232

            
233
impl fmt::Display for DurationOption {
234
290
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235
290
        match self {
236
285
            DurationOption::Literal(v) => write!(f, "{v}"),
237
5
            DurationOption::Placeholder(v) => write!(f, "{v}"),
238
        }
239
    }
240
}
241

            
242
#[derive(Clone, Debug, PartialEq, Eq)]
243
pub struct VariableDefinition {
244
    pub source_info: SourceInfo,
245
    pub name: String,
246
    pub space0: Whitespace,
247
    pub space1: Whitespace,
248
    pub value: VariableValue,
249
}
250

            
251
impl fmt::Display for VariableDefinition {
252
410
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253
410
        write!(f, "{}={}", self.name, self.value)
254
    }
255
}
256

            
257
#[derive(Clone, Debug, PartialEq, Eq)]
258
pub enum VariableValue {
259
    Null,
260
    Bool(bool),
261
    Number(Number),
262
    String(Template),
263
}
264

            
265
impl fmt::Display for VariableValue {
266
410
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267
410
        let s = match self {
268
            VariableValue::Null => "null".to_string(),
269
5
            VariableValue::Bool(value) => value.to_string(),
270
155
            VariableValue::Number(n) => n.to_string(),
271
250
            VariableValue::String(s) => s.to_string(),
272
        };
273
410
        write!(f, "{s}")
274
    }
275
}
276

            
277
impl ToSource for VariableValue {
278
90
    fn to_source(&self) -> SourceString {
279
90
        match self {
280
10
            VariableValue::Null => "null".to_source(),
281
10
            VariableValue::Bool(value) => value.to_string().to_source(),
282
20
            VariableValue::Number(value) => value.to_source(),
283
50
            VariableValue::String(value) => value.to_source(),
284
        }
285
    }
286
}
287

            
288
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
289
pub enum VerbosityOption {
290
    Brief,
291
    Verbose,
292
    Debug,
293
}
294

            
295
impl fmt::Display for VerbosityOption {
296
35
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297
35
        write!(f, "{}", self.identifier())
298
    }
299
}
300

            
301
impl VerbosityOption {
302
55
    pub fn identifier(&self) -> &'static str {
303
55
        match self {
304
45
            VerbosityOption::Brief => "brief",
305
5
            VerbosityOption::Verbose => "verbose",
306
5
            VerbosityOption::Debug => "debug",
307
        }
308
    }
309
}