1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2025 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::ast::option::EntryOption;
21
use crate::ast::primitive::{
22
    Bytes, KeyValue, LineTerminator, SourceInfo, Template, Whitespace, U64,
23
};
24
use crate::ast::section::{
25
    Assert, Capture, Cookie, MultipartParam, RegexValue, Section, SectionValue,
26
};
27

            
28
/// Represents Hurl AST root node.
29
#[derive(Clone, Debug, PartialEq, Eq)]
30
pub struct HurlFile {
31
    pub entries: Vec<Entry>,
32
    pub line_terminators: Vec<LineTerminator>,
33
}
34

            
35
#[derive(Clone, Debug, PartialEq, Eq)]
36
pub struct Entry {
37
    pub request: Request,
38
    pub response: Option<Response>,
39
}
40

            
41
impl Entry {
42
    /// Returns the source information for this entry.
43
14580
    pub fn source_info(&self) -> SourceInfo {
44
14580
        self.request.space0.source_info
45
    }
46
}
47

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

            
62
impl Request {
63
    /// Returns the query strings params for this request.
64
    ///
65
    /// See <https://developer.mozilla.org/en-US/docs/Web/API/URL/searchParams>.
66
12055
    pub fn querystring_params(&self) -> &[KeyValue] {
67
13640
        for section in &self.sections {
68
1810
            if let SectionValue::QueryParams(params, _) = &section.value {
69
225
                return params;
70
            }
71
        }
72
11830
        &[]
73
    }
74

            
75
    /// Returns the form params for this request.
76
    ///
77
    /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST#url-encoded_form_submission>.
78
12015
    pub fn form_params(&self) -> &[KeyValue] {
79
13735
        for section in &self.sections {
80
1805
            if let SectionValue::FormParams(params, _) = &section.value {
81
85
                return params;
82
            }
83
        }
84
11930
        &[]
85
    }
86

            
87
    /// Returns the multipart form data for this request.
88
    ///
89
    /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST#multipart_form_submission>.
90
11935
    pub fn multipart_form_data(&self) -> &[MultipartParam] {
91
13645
        for section in &self.sections {
92
1820
            if let SectionValue::MultipartFormData(params, _) = &section.value {
93
110
                return params;
94
            }
95
        }
96
11825
        &[]
97
    }
98

            
99
    /// Returns the list of cookies on this request.
100
    ///
101
    /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie>.
102
12015
    pub fn cookies(&self) -> &[Cookie] {
103
13815
        for section in &self.sections {
104
1855
            if let SectionValue::Cookies(cookies) = &section.value {
105
55
                return cookies;
106
            }
107
        }
108
11960
        &[]
109
    }
110

            
111
    /// Returns the basic authentication on this request.
112
    ///
113
    /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication>.
114
11755
    pub fn basic_auth(&self) -> Option<&KeyValue> {
115
13485
        for section in &self.sections {
116
1780
            if let SectionValue::BasicAuth(kv) = &section.value {
117
50
                return kv.as_ref();
118
            }
119
        }
120
11705
        None
121
    }
122

            
123
    /// Returns the options specific for this request.
124
415
    pub fn options(&self) -> &[EntryOption] {
125
490
        for section in &self.sections {
126
225
            if let SectionValue::Options(options) = &section.value {
127
150
                return options;
128
            }
129
        }
130
265
        &[]
131
    }
132
}
133

            
134
#[derive(Clone, Debug, PartialEq, Eq)]
135
pub struct Response {
136
    pub line_terminators: Vec<LineTerminator>,
137
    pub version: Version,
138
    pub space0: Whitespace,
139
    pub status: Status,
140
    pub space1: Whitespace,
141
    pub line_terminator0: LineTerminator,
142
    pub headers: Vec<KeyValue>,
143
    pub sections: Vec<Section>,
144
    pub body: Option<Body>,
145
    pub source_info: SourceInfo,
146
}
147

            
148
impl Response {
149
    /// Returns the captures list of this spec response.
150
12360
    pub fn captures(&self) -> &[Capture] {
151
12360
        for section in self.sections.iter() {
152
7840
            if let SectionValue::Captures(captures) = &section.value {
153
385
                return captures;
154
            }
155
        }
156
11975
        &[]
157
    }
158

            
159
    /// Returns the asserts list of this spec response.
160
22470
    pub fn asserts(&self) -> &[Assert] {
161
22470
        for section in self.sections.iter() {
162
15375
            if let SectionValue::Asserts(asserts) = &section.value {
163
14775
                return asserts;
164
            }
165
        }
166
7695
        &[]
167
    }
168
}
169

            
170
#[derive(Clone, Debug, PartialEq, Eq)]
171
pub struct Method(pub String);
172

            
173
impl fmt::Display for Method {
174
2270
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175
2270
        write!(f, "{}", self.0)
176
    }
177
}
178

            
179
#[derive(Clone, Debug, PartialEq, Eq)]
180
pub struct Version {
181
    pub value: VersionValue,
182
    pub source_info: SourceInfo,
183
}
184

            
185
impl fmt::Display for Version {
186
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187
        write!(f, "{}", self.value)
188
    }
189
}
190

            
191
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
192
pub enum VersionValue {
193
    Version1,
194
    Version11,
195
    Version2,
196
    Version3,
197
    VersionAny,
198
}
199

            
200
impl fmt::Display for VersionValue {
201
12860
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202
12860
        let s = match self {
203
25
            VersionValue::Version1 => "HTTP/1.0",
204
65
            VersionValue::Version11 => "HTTP/1.1",
205
55
            VersionValue::Version2 => "HTTP/2",
206
20
            VersionValue::Version3 => "HTTP/3",
207
12695
            VersionValue::VersionAny => "HTTP",
208
        };
209
12860
        write!(f, "{s}")
210
    }
211
}
212

            
213
#[derive(Clone, Debug, PartialEq, Eq)]
214
pub struct Status {
215
    pub value: StatusValue,
216
    pub source_info: SourceInfo,
217
}
218

            
219
impl fmt::Display for Status {
220
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221
        write!(f, "{}", self.value)
222
    }
223
}
224

            
225
#[derive(Clone, Debug, PartialEq, Eq)]
226
pub enum StatusValue {
227
    Any,
228
    Specific(u64),
229
}
230

            
231
impl fmt::Display for StatusValue {
232
1365
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233
1365
        match self {
234
30
            StatusValue::Any => write!(f, "*"),
235
1335
            StatusValue::Specific(v) => write!(f, "{v}"),
236
        }
237
    }
238
}
239

            
240
#[derive(Clone, Debug, PartialEq, Eq)]
241
pub struct Body {
242
    pub line_terminators: Vec<LineTerminator>,
243
    pub space0: Whitespace,
244
    pub value: Bytes,
245
    pub line_terminator0: LineTerminator,
246
}
247

            
248
/// Check that variable name is not reserved
249
/// (would conflicts with an existing function)
250
740
pub fn is_variable_reserved(name: &str) -> bool {
251
740
    ["getEnv", "newDate", "newUuid"].contains(&name)
252
}
253

            
254
#[derive(Clone, Debug, PartialEq, Eq)]
255
pub struct Filter {
256
    pub source_info: SourceInfo,
257
    pub value: FilterValue,
258
}
259

            
260
#[derive(Clone, Debug, PartialEq, Eq)]
261
pub enum FilterValue {
262
    Base64Decode,
263
    Base64Encode,
264
    Count,
265
    DaysAfterNow,
266
    DaysBeforeNow,
267
    Decode {
268
        space0: Whitespace,
269
        encoding: Template,
270
    },
271
    Format {
272
        space0: Whitespace,
273
        fmt: Template,
274
    },
275
    HtmlEscape,
276
    HtmlUnescape,
277
    JsonPath {
278
        space0: Whitespace,
279
        expr: Template,
280
    },
281
    Nth {
282
        space0: Whitespace,
283
        n: U64,
284
    },
285
    Regex {
286
        space0: Whitespace,
287
        value: RegexValue,
288
    },
289
    Replace {
290
        space0: Whitespace,
291
        old_value: RegexValue,
292
        space1: Whitespace,
293
        new_value: Template,
294
    },
295
    Split {
296
        space0: Whitespace,
297
        sep: Template,
298
    },
299
    ToDate {
300
        space0: Whitespace,
301
        fmt: Template,
302
    },
303
    ToFloat,
304
    ToInt,
305
    UrlDecode,
306
    UrlEncode,
307
    XPath {
308
        space0: Whitespace,
309
        expr: Template,
310
    },
311
}
312

            
313
impl FilterValue {
314
    /// Returns the Hurl identifier for this filter type.
315
1130
    pub fn identifier(&self) -> &'static str {
316
1130
        match self {
317
35
            FilterValue::Base64Decode => "base64Decode",
318
25
            FilterValue::Base64Encode => "base64Encode",
319
270
            FilterValue::Count => "count",
320
20
            FilterValue::DaysAfterNow => "daysAfterNow",
321
40
            FilterValue::DaysBeforeNow => "daysBeforeNow",
322
65
            FilterValue::Decode { .. } => "decode",
323
65
            FilterValue::Format { .. } => "format",
324
30
            FilterValue::HtmlEscape => "htmlEscape",
325
40
            FilterValue::HtmlUnescape => "htmlUnescape",
326
30
            FilterValue::JsonPath { .. } => "jsonpath",
327
125
            FilterValue::Nth { .. } => "nth",
328
50
            FilterValue::Regex { .. } => "regex",
329
85
            FilterValue::Replace { .. } => "replace",
330
25
            FilterValue::Split { .. } => "split",
331
40
            FilterValue::ToDate { .. } => "toDate",
332
40
            FilterValue::ToFloat => "toFloat",
333
55
            FilterValue::ToInt => "toInt",
334
30
            FilterValue::UrlDecode => "urlDecode",
335
30
            FilterValue::UrlEncode => "urlEncode",
336
30
            FilterValue::XPath { .. } => "xpath",
337
        }
338
    }
339
}