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
//! Walker traverses an AST in depth-first order. Each overridden visit method has full control over
19
//! what happens with its node, it can do its own traversal of the node's children, call `visit::walk_*`
20
//! to apply the default traversal algorithm, or prevent deeper traversal by doing nothing.
21
//!
22
//! Code heavily inspired from <https://github.com/rust-lang/rust/blob/master/compiler/rustc_ast/src/visit.rs>
23
use crate::types::{Count, DurationUnit, SourceString, ToSource};
24

            
25
use super::core::{
26
    Body, Entry, Filter, FilterValue, HurlFile, IntegerValue, Method, Request, Response,
27
    StatusValue, VersionValue,
28
};
29
use super::json::JsonValue;
30
use super::option::{
31
    BooleanOption, CountOption, Duration, DurationOption, EntryOption, NaturalOption, OptionKind,
32
    VariableDefinition, VariableValue, VerbosityOption,
33
};
34
use super::primitive::{
35
    Base64, Bytes, Comment, File, Hex, KeyValue, LineTerminator, MultilineString, Number,
36
    Placeholder, Regex, Template, U64, Whitespace,
37
};
38
use super::section::{
39
    Assert, Capture, Cookie, CookiePath, FilenameParam, FilenameValue, MultipartParam, Predicate,
40
    PredicateFuncValue, PredicateValue, Query, QueryValue, RegexValue, Section, SectionValue,
41
};
42

            
43
/// Each method of the `Visitor` trait is a hook to be potentially overridden. Each method's default
44
/// implementation recursively visits the substructure of the input via the corresponding `walk` method;
45
/// e.g., the `visit_item` method by default calls `visit::walk_item`.
46
#[allow(unused_variables)]
47
pub trait Visitor: Sized {
48
3336
    fn visit_assert(&mut self, assert: &Assert) {
49
3336
        walk_assert(self, assert);
50
    }
51

            
52
62
    fn visit_base64(&mut self, value: &Base64) {
53
62
        walk_base64(self, value);
54
    }
55

            
56
    fn visit_base64_value(&mut self, value: &[u8], source: &SourceString) {}
57

            
58
628
    fn visit_body(&mut self, body: &Body) {
59
628
        walk_body(self, body);
60
    }
61

            
62
    fn visit_bool(&mut self, value: bool) {}
63

            
64
502
    fn visit_bool_option(&mut self, option: &BooleanOption) {
65
502
        walk_bool_option(self, option);
66
    }
67

            
68
248
    fn visit_capture(&mut self, capture: &Capture) {
69
248
        walk_capture(self, capture);
70
    }
71

            
72
54
    fn visit_cookie(&mut self, cookie: &Cookie) {
73
54
        walk_cookie(self, cookie);
74
    }
75

            
76
    fn visit_cookie_path(&mut self, path: &CookiePath) {}
77

            
78
    fn visit_comment(&mut self, comment: &Comment) {}
79

            
80
62
    fn visit_count(&mut self, count: Count) {
81
62
        walk_count(self, count);
82
    }
83

            
84
84
    fn visit_count_option(&mut self, option: &CountOption) {
85
84
        walk_count_option(self, option);
86
    }
87

            
88
68
    fn visit_duration(&mut self, duration: &Duration) {
89
68
        walk_duration(self, duration);
90
    }
91

            
92
92
    fn visit_duration_option(&mut self, option: &DurationOption) {
93
92
        walk_duration_option(self, option);
94
    }
95

            
96
    fn visit_duration_unit(&mut self, unit: DurationUnit) {}
97

            
98
182
    fn visit_entry(&mut self, entry: &Entry) {
99
182
        walk_entry(self, entry);
100
    }
101

            
102
1034
    fn visit_entry_option(&mut self, option: &EntryOption) {
103
1034
        walk_entry_option(self, option);
104
    }
105

            
106
78
    fn visit_file(&mut self, file: &File) {
107
78
        walk_file(self, file);
108
    }
109

            
110
40
    fn visit_filename_param(&mut self, param: &FilenameParam) {
111
40
        walk_filename_param(self, param);
112
    }
113

            
114
40
    fn visit_filename_value(&mut self, value: &FilenameValue) {
115
40
        walk_filename_value(self, value);
116
    }
117

            
118
    fn visit_filename(&mut self, filename: &Template) {}
119

            
120
1330
    fn visit_filter(&mut self, filter: &Filter) {
121
1330
        walk_filter(self, filter);
122
    }
123

            
124
    fn visit_filter_kind(&mut self, kind: &FilterValue) {}
125

            
126
    fn visit_header(&mut self, header: &KeyValue) {
127
        walk_header(self, header);
128
    }
129

            
130
300
    fn visit_hex(&mut self, hex: &Hex) {
131
300
        walk_hex(self, hex);
132
    }
133

            
134
    fn visit_hex_value(&mut self, value: &[u8], source: &SourceString) {}
135

            
136
48
    fn visit_hurl_file(&mut self, file: &HurlFile) {
137
48
        walk_hurl_file(self, file);
138
    }
139

            
140
134
    fn visit_integer_value(&mut self, n: &IntegerValue) {
141
134
        walk_integer_value(self, n);
142
    }
143

            
144
    fn visit_i64(&mut self, n: i64) {}
145

            
146
    fn visit_json_body(&mut self, json: &JsonValue) {}
147

            
148
836
    fn visit_kv(&mut self, kv: &KeyValue) {
149
836
        walk_kv(self, kv);
150
    }
151

            
152
12982
    fn visit_lt(&mut self, lt: &LineTerminator) {
153
12982
        walk_lt(self, lt);
154
    }
155

            
156
    fn visit_literal(&mut self, lit: &'static str) {}
157

            
158
    fn visit_method(&mut self, method: &Method) {}
159

            
160
    fn visit_multiline_string(&mut self, string: &MultilineString) {}
161

            
162
12
    fn visit_natural_option(&mut self, option: &NaturalOption) {
163
12
        walk_natural_option(self, option);
164
    }
165

            
166
    fn visit_not(&mut self, identifier: &'static str) {}
167

            
168
    fn visit_null(&mut self, identifier: &'static str) {}
169

            
170
    fn visit_number(&mut self, number: &Number) {}
171

            
172
    fn visit_placeholder(&mut self, placeholder: &Placeholder) {}
173

            
174
3336
    fn visit_predicate(&mut self, predicate: &Predicate) {
175
3336
        walk_predicate(self, predicate);
176
    }
177

            
178
    fn visit_predicate_kind(&mut self, kind: &PredicateFuncValue) {}
179

            
180
2776
    fn visit_predicate_value(&mut self, value: &PredicateValue) {
181
2776
        walk_predicate_value(self, value);
182
    }
183

            
184
3584
    fn visit_query(&mut self, query: &Query) {
185
3584
        walk_query(self, query);
186
    }
187

            
188
    fn visit_query_kind(&mut self, kind: &QueryValue) {}
189

            
190
182
    fn visit_request(&mut self, request: &Request) {
191
182
        walk_request(self, request);
192
    }
193

            
194
76
    fn visit_response(&mut self, response: &Response) {
195
76
        walk_response(self, response);
196
    }
197

            
198
    fn visit_regex(&mut self, regex: &Regex) {}
199

            
200
1196
    fn visit_section(&mut self, section: &Section) {
201
1196
        walk_section(self, section);
202
    }
203

            
204
    fn visit_status(&mut self, value: &StatusValue) {}
205

            
206
    fn visit_string(&mut self, value: &str) {}
207

            
208
    fn visit_section_header(&mut self, name: &str) {}
209

            
210
1196
    fn visit_section_value(&mut self, section_value: &SectionValue) {
211
1196
        walk_section_value(self, section_value);
212
    }
213

            
214
    fn visit_template(&mut self, template: &Template) {}
215

            
216
    fn visit_url(&mut self, url: &Template) {}
217

            
218
    fn visit_u64(&mut self, n: &U64) {}
219

            
220
    fn visit_usize(&mut self, n: usize) {}
221

            
222
114
    fn visit_variable_def(&mut self, def: &VariableDefinition) {
223
114
        walk_variable_def(self, def);
224
    }
225

            
226
    fn visit_variable_name(&mut self, name: &str) {}
227

            
228
114
    fn visit_variable_value(&mut self, value: &VariableValue) {
229
114
        walk_variable_value(self, value);
230
    }
231

            
232
12
    fn visit_verbosity_option(&mut self, value: &VerbosityOption) {
233
12
        walk_verbosity_option(self, value);
234
    }
235

            
236
    fn visit_version(&mut self, value: &VersionValue) {}
237

            
238
    fn visit_xml_body(&mut self, xml: &str) {}
239

            
240
    fn visit_whitespace(&mut self, ws: &Whitespace) {}
241
}
242

            
243
3336
pub fn walk_assert<V: Visitor>(visitor: &mut V, assert: &Assert) {
244
3336
    assert.line_terminators.iter().for_each(|lt| {
245
80
        visitor.visit_lt(lt);
246
80
    });
247
3336
    visitor.visit_whitespace(&assert.space0);
248
3336
    visitor.visit_query(&assert.query);
249
3336
    for (space, filter) in assert.filters.iter() {
250
1284
        visitor.visit_whitespace(space);
251
1284
        visitor.visit_filter(filter);
252
    }
253
3336
    visitor.visit_whitespace(&assert.space1);
254
3336
    visitor.visit_predicate(&assert.predicate);
255
3336
    visitor.visit_lt(&assert.line_terminator0);
256
}
257

            
258
62
pub fn walk_base64<V: Visitor>(visitor: &mut V, base64: &Base64) {
259
62
    visitor.visit_literal("base64,");
260
62
    visitor.visit_whitespace(&base64.space0);
261
62
    visitor.visit_base64_value(&base64.value, &base64.source);
262
62
    visitor.visit_whitespace(&base64.space1);
263
62
    visitor.visit_literal(";");
264
}
265

            
266
628
pub fn walk_body<V: Visitor>(visitor: &mut V, body: &Body) {
267
628
    body.line_terminators.iter().for_each(|lt| {
268
76
        visitor.visit_lt(lt);
269
76
    });
270
628
    visitor.visit_whitespace(&body.space0);
271
628
    match &body.value {
272
68
        Bytes::Json(value) => visitor.visit_json_body(value),
273
22
        Bytes::Xml(value) => visitor.visit_xml_body(value),
274
182
        Bytes::MultilineString(value) => visitor.visit_multiline_string(value),
275
210
        Bytes::OnelineString(value) => visitor.visit_template(value),
276
52
        Bytes::Base64(value) => visitor.visit_base64(value),
277
64
        Bytes::File(value) => visitor.visit_file(value),
278
30
        Bytes::Hex(value) => visitor.visit_hex(value),
279
    }
280
628
    visitor.visit_lt(&body.line_terminator0);
281
}
282

            
283
502
pub fn walk_bool_option<V: Visitor>(visitor: &mut V, option: &BooleanOption) {
284
502
    match option {
285
378
        BooleanOption::Literal(value) => visitor.visit_bool(*value),
286
124
        BooleanOption::Placeholder(value) => visitor.visit_placeholder(value),
287
    }
288
}
289

            
290
248
pub fn walk_capture<V: Visitor>(visitor: &mut V, capture: &Capture) {
291
248
    capture.line_terminators.iter().for_each(|lt| {
292
8
        visitor.visit_lt(lt);
293
8
    });
294
248
    visitor.visit_whitespace(&capture.space0);
295
248
    visitor.visit_template(&capture.name);
296
248
    visitor.visit_whitespace(&capture.space1);
297
248
    visitor.visit_literal(":");
298
248
    visitor.visit_whitespace(&capture.space2);
299
248
    visitor.visit_query(&capture.query);
300
248
    for (space, filter) in capture.filters.iter() {
301
46
        visitor.visit_whitespace(space);
302
46
        visitor.visit_filter(filter);
303
    }
304
248
    if capture.redacted {
305
28
        visitor.visit_whitespace(&capture.space3);
306
28
        // The next node should have been literal to be more correct
307
28
        // we visit a string instead to be comptaible with <= 6.1.1 HTML export
308
28
        // visitor.visit_literal("redact");
309
28
        visitor.visit_string("redact");
310
    }
311
248
    visitor.visit_lt(&capture.line_terminator0);
312
}
313

            
314
54
pub fn walk_cookie<V: Visitor>(visitor: &mut V, cookie: &Cookie) {
315
54
    cookie.line_terminators.iter().for_each(|lt| {
316
        visitor.visit_lt(lt);
317
    });
318
54
    visitor.visit_whitespace(&cookie.space0);
319
54
    visitor.visit_template(&cookie.name);
320
54
    visitor.visit_whitespace(&cookie.space1);
321
54
    visitor.visit_literal(":");
322
54
    visitor.visit_whitespace(&cookie.space2);
323
54
    visitor.visit_template(&cookie.value);
324
54
    visitor.visit_lt(&cookie.line_terminator0);
325
}
326

            
327
62
pub fn walk_count<V: Visitor>(visitor: &mut V, count: Count) {
328
62
    match count {
329
50
        Count::Finite(count) => visitor.visit_usize(count),
330
12
        Count::Infinite => visitor.visit_i64(-1),
331
    }
332
}
333

            
334
84
pub fn walk_count_option<V: Visitor>(visitor: &mut V, option: &CountOption) {
335
84
    match option {
336
62
        CountOption::Literal(value) => visitor.visit_count(*value),
337
22
        CountOption::Placeholder(value) => visitor.visit_placeholder(value),
338
    }
339
}
340

            
341
68
pub fn walk_duration<V: Visitor>(visitor: &mut V, duration: &Duration) {
342
68
    visitor.visit_u64(&duration.value);
343
68
    if let Some(unit) = duration.unit {
344
60
        visitor.visit_duration_unit(unit);
345
    }
346
}
347

            
348
92
pub fn walk_duration_option<V: Visitor>(visitor: &mut V, option: &DurationOption) {
349
92
    match option {
350
68
        DurationOption::Literal(value) => visitor.visit_duration(value),
351
24
        DurationOption::Placeholder(value) => visitor.visit_placeholder(value),
352
    }
353
}
354

            
355
1522
pub fn walk_entry<V: Visitor>(visitor: &mut V, entry: &Entry) {
356
1522
    visitor.visit_request(&entry.request);
357
1522
    if let Some(ref response) = entry.response {
358
1244
        visitor.visit_response(response);
359
    }
360
}
361

            
362
1034
pub fn walk_entry_option<V: Visitor>(visitor: &mut V, option: &EntryOption) {
363
1034
    option.line_terminators.iter().for_each(|lt| {
364
10
        visitor.visit_lt(lt);
365
10
    });
366
1034
    visitor.visit_whitespace(&option.space0);
367
1034
    visitor.visit_string(option.kind.identifier());
368
1034
    visitor.visit_whitespace(&option.space1);
369
1034
    visitor.visit_literal(":");
370
1034
    visitor.visit_whitespace(&option.space2);
371
1034
    match &option.kind {
372
12
        OptionKind::AwsSigV4(value) => visitor.visit_template(value),
373
12
        OptionKind::CaCertificate(filename) => visitor.visit_filename(filename),
374
18
        OptionKind::ClientCert(filename) => visitor.visit_filename(filename),
375
12
        OptionKind::ClientKey(filename) => visitor.visit_filename(filename),
376
88
        OptionKind::Compressed(value) => visitor.visit_bool_option(value),
377
12
        OptionKind::ConnectTo(value) => visitor.visit_template(value),
378
12
        OptionKind::ConnectTimeout(value) => visitor.visit_duration_option(value),
379
36
        OptionKind::Delay(value) => visitor.visit_duration_option(value),
380
12
        OptionKind::Digest(value) => visitor.visit_bool_option(value),
381
12
        OptionKind::FailWithBody(value) => visitor.visit_bool_option(value),
382
90
        OptionKind::FollowLocation(value) => visitor.visit_bool_option(value),
383
16
        OptionKind::FollowLocationTrusted(value) => visitor.visit_bool_option(value),
384
12
        OptionKind::Header(value) => visitor.visit_template(value),
385
36
        OptionKind::Http10(value) => visitor.visit_bool_option(value),
386
28
        OptionKind::Http11(value) => visitor.visit_bool_option(value),
387
12
        OptionKind::Http2(value) => visitor.visit_bool_option(value),
388
6
        OptionKind::Http2PriorKnowledge(value) => visitor.visit_bool_option(value),
389
12
        OptionKind::Http3(value) => visitor.visit_bool_option(value),
390
22
        OptionKind::Insecure(value) => visitor.visit_bool_option(value),
391
12
        OptionKind::IpV4(value) => visitor.visit_bool_option(value),
392
12
        OptionKind::IpV6(value) => visitor.visit_bool_option(value),
393
12
        OptionKind::LimitRate(value) => visitor.visit_natural_option(value),
394
20
        OptionKind::MaxRedirect(value) => visitor.visit_count_option(value),
395
12
        OptionKind::MaxTime(value) => visitor.visit_duration_option(value),
396
14
        OptionKind::Negotiate(value) => visitor.visit_bool_option(value),
397
12
        OptionKind::NetRc(value) => visitor.visit_bool_option(value),
398
12
        OptionKind::NetRcFile(filename) => visitor.visit_filename(filename),
399
12
        OptionKind::NetRcOptional(value) => visitor.visit_bool_option(value),
400
12
        OptionKind::NoHeader(value) => visitor.visit_template(value),
401
6
        OptionKind::NoJsonpathCoercion(value) => visitor.visit_bool_option(value),
402
14
        OptionKind::Ntlm(value) => visitor.visit_bool_option(value),
403
12
        OptionKind::Output(filename) => visitor.visit_filename(filename),
404
12
        OptionKind::PathAsIs(value) => visitor.visit_bool_option(value),
405
12
        OptionKind::PinnedPublicKey(value) => visitor.visit_template(value),
406
20
        OptionKind::Proxy(value) => visitor.visit_template(value),
407
26
        OptionKind::Repeat(value) => visitor.visit_count_option(value),
408
12
        OptionKind::Resolve(value) => visitor.visit_template(value),
409
38
        OptionKind::Retry(value) => visitor.visit_count_option(value),
410
32
        OptionKind::RetryInterval(value) => visitor.visit_duration_option(value),
411
12
        OptionKind::Skip(value) => visitor.visit_bool_option(value),
412
12
        OptionKind::TlsMax(value) => visitor.visit_template(value),
413
12
        OptionKind::TlsMin(value) => visitor.visit_template(value),
414
12
        OptionKind::UnixSocket(value) => visitor.visit_filename(value),
415
24
        OptionKind::User(value) => visitor.visit_template(value),
416
114
        OptionKind::Variable(value) => visitor.visit_variable_def(value),
417
46
        OptionKind::Verbose(value) => visitor.visit_bool_option(value),
418
12
        OptionKind::Verbosity(value) => visitor.visit_verbosity_option(value),
419
16
        OptionKind::VeryVerbose(value) => visitor.visit_bool_option(value),
420
    };
421
1034
    visitor.visit_lt(&option.line_terminator0);
422
}
423

            
424
78
pub fn walk_file<V: Visitor>(visitor: &mut V, file: &File) {
425
78
    visitor.visit_literal("file,");
426
78
    visitor.visit_whitespace(&file.space0);
427
78
    visitor.visit_filename(&file.filename);
428
78
    visitor.visit_whitespace(&file.space1);
429
78
    visitor.visit_literal(";");
430
}
431

            
432
1330
pub fn walk_filter<V: Visitor>(visitor: &mut V, filter: &Filter) {
433
1330
    visitor.visit_filter_kind(&filter.value);
434
1330
    match &filter.value {
435
40
        FilterValue::Base64Decode => {}
436
18
        FilterValue::Base64Encode => {}
437
30
        FilterValue::Base64UrlSafeDecode => {}
438
18
        FilterValue::Base64UrlSafeEncode => {}
439
40
        FilterValue::CharsetDecode { space0, encoding } => {
440
40
            visitor.visit_whitespace(space0);
441
40
            visitor.visit_template(encoding);
442
        }
443
314
        FilterValue::Count => {}
444
10
        FilterValue::DaysAfterNow => {}
445
20
        FilterValue::DaysBeforeNow => {}
446
6
        FilterValue::Decode { space0, encoding } => {
447
6
            visitor.visit_whitespace(space0);
448
6
            visitor.visit_template(encoding);
449
        }
450
22
        FilterValue::First => {}
451
20
        FilterValue::Format { space0, fmt } => {
452
20
            visitor.visit_whitespace(space0);
453
20
            visitor.visit_template(fmt);
454
        }
455
50
        FilterValue::DateFormat { space0, fmt } => {
456
50
            visitor.visit_whitespace(space0);
457
50
            visitor.visit_template(fmt);
458
        }
459
18
        FilterValue::HtmlEscape => {}
460
26
        FilterValue::HtmlUnescape => {}
461
34
        FilterValue::JsonPath { space0, expr } => {
462
34
            visitor.visit_whitespace(space0);
463
34
            visitor.visit_template(expr);
464
        }
465
22
        FilterValue::Last => {}
466
68
        FilterValue::Location => {}
467
134
        FilterValue::Nth { space0, n } => {
468
134
            visitor.visit_whitespace(space0);
469
134
            visitor.visit_integer_value(n);
470
        }
471
38
        FilterValue::Regex { space0, value } => {
472
38
            visitor.visit_whitespace(space0);
473
38
            match value {
474
8
                RegexValue::Template(value) => visitor.visit_template(value),
475
30
                RegexValue::Regex(regex) => visitor.visit_regex(regex),
476
            }
477
        }
478
        FilterValue::Replace {
479
42
            space0,
480
42
            old_value,
481
42
            space1,
482
42
            new_value,
483
42
        } => {
484
42
            visitor.visit_whitespace(space0);
485
42
            visitor.visit_template(old_value);
486
42
            visitor.visit_whitespace(space1);
487
42
            visitor.visit_template(new_value);
488
        }
489
        FilterValue::ReplaceRegex {
490
26
            space0,
491
26
            pattern,
492
26
            space1,
493
26
            new_value,
494
        } => {
495
26
            visitor.visit_whitespace(space0);
496
26
            match pattern {
497
8
                RegexValue::Template(value) => visitor.visit_template(value),
498
18
                RegexValue::Regex(regex) => visitor.visit_regex(regex),
499
            }
500
26
            visitor.visit_whitespace(space1);
501
26
            visitor.visit_template(new_value);
502
        }
503
22
        FilterValue::Split { space0, sep } => {
504
22
            visitor.visit_whitespace(space0);
505
22
            visitor.visit_template(sep);
506
        }
507
86
        FilterValue::ToDate { space0, fmt } => {
508
86
            visitor.visit_whitespace(space0);
509
86
            visitor.visit_template(fmt);
510
        }
511
38
        FilterValue::ToFloat => {}
512
32
        FilterValue::ToHex => {}
513
38
        FilterValue::ToInt => {}
514
14
        FilterValue::ToString => {}
515
18
        FilterValue::UrlDecode => {}
516
18
        FilterValue::UrlEncode => {}
517
26
        FilterValue::UrlQueryParam { space0, param } => {
518
26
            visitor.visit_whitespace(space0);
519
26
            visitor.visit_template(param);
520
        }
521
10
        FilterValue::Utf8Decode => {}
522
10
        FilterValue::Utf8Encode => {}
523
22
        FilterValue::XPath { space0, expr } => {
524
22
            visitor.visit_whitespace(space0);
525
22
            visitor.visit_template(expr);
526
        }
527
    }
528
}
529

            
530
40
pub fn walk_filename_param<V: Visitor>(visitor: &mut V, param: &FilenameParam) {
531
40
    param.line_terminators.iter().for_each(|lt| {
532
        visitor.visit_lt(lt);
533
    });
534
40
    visitor.visit_whitespace(&param.space0);
535
40
    visitor.visit_template(&param.key);
536
40
    visitor.visit_whitespace(&param.space1);
537
40
    visitor.visit_literal(":");
538
40
    visitor.visit_whitespace(&param.space2);
539
40
    visitor.visit_filename_value(&param.value);
540
40
    visitor.visit_lt(&param.line_terminator0);
541
}
542

            
543
40
pub fn walk_filename_value<V: Visitor>(visitor: &mut V, value: &FilenameValue) {
544
40
    visitor.visit_literal("file,");
545
40
    visitor.visit_whitespace(&value.space0);
546
40
    visitor.visit_filename(&value.filename);
547
40
    visitor.visit_whitespace(&value.space1);
548
40
    visitor.visit_literal(";");
549
40
    visitor.visit_whitespace(&value.space2);
550
40
    if let Some(content_type) = &value.content_type {
551
14
        visitor.visit_template(content_type);
552
    }
553
}
554

            
555
pub fn walk_header<V: Visitor>(visitor: &mut V, header: &KeyValue) {
556
    visitor.visit_kv(header);
557
}
558

            
559
300
pub fn walk_hex<V: Visitor>(visitor: &mut V, hex: &Hex) {
560
300
    visitor.visit_literal("hex,");
561
300
    visitor.visit_whitespace(&hex.space0);
562
300
    visitor.visit_hex_value(&hex.value, &hex.source);
563
300
    visitor.visit_whitespace(&hex.space1);
564
300
    visitor.visit_literal(";");
565
}
566

            
567
528
pub fn walk_hurl_file<V: Visitor>(visitor: &mut V, file: &HurlFile) {
568
1522
    file.entries.iter().for_each(|e| visitor.visit_entry(e));
569
528
    file.line_terminators.iter().for_each(|lt| {
570
236
        visitor.visit_lt(lt);
571
236
    });
572
}
573

            
574
134
pub fn walk_integer_value<V: Visitor>(visitor: &mut V, n: &IntegerValue) {
575
134
    match n {
576
126
        IntegerValue::Literal(value) => visitor.visit_i64(value.as_i64()),
577
8
        IntegerValue::Placeholder(value) => visitor.visit_placeholder(value),
578
    }
579
}
580

            
581
836
pub fn walk_kv<V: Visitor>(visitor: &mut V, kv: &KeyValue) {
582
836
    kv.line_terminators.iter().for_each(|lt| {
583
22
        visitor.visit_lt(lt);
584
22
    });
585
836
    visitor.visit_whitespace(&kv.space0);
586
836
    visitor.visit_template(&kv.key);
587
836
    visitor.visit_whitespace(&kv.space1);
588
836
    visitor.visit_literal(":");
589
836
    visitor.visit_whitespace(&kv.space2);
590
836
    visitor.visit_template(&kv.value);
591
836
    visitor.visit_lt(&kv.line_terminator0);
592
}
593

            
594
12982
pub fn walk_lt<V: Visitor>(visitor: &mut V, lt: &LineTerminator) {
595
12982
    visitor.visit_whitespace(&lt.space0);
596
12982
    if let Some(ref comment) = lt.comment {
597
1676
        visitor.visit_comment(comment);
598
    }
599
12982
    visitor.visit_whitespace(&lt.newline);
600
}
601

            
602
12
pub fn walk_natural_option<V: Visitor>(visitor: &mut V, option: &NaturalOption) {
603
12
    match option {
604
6
        NaturalOption::Literal(value) => visitor.visit_u64(value),
605
6
        NaturalOption::Placeholder(value) => visitor.visit_placeholder(value),
606
    }
607
}
608

            
609
3584
pub fn walk_query<V: Visitor>(visitor: &mut V, query: &Query) {
610
3584
    visitor.visit_query_kind(&query.value);
611

            
612
3584
    match &query.value {
613
288
        QueryValue::Header { space0, name } => {
614
288
            visitor.visit_whitespace(space0);
615
288
            visitor.visit_template(name);
616
        }
617
70
        QueryValue::Cookie { space0, expr } => {
618
70
            visitor.visit_whitespace(space0);
619
70
            visitor.visit_cookie_path(expr);
620
        }
621
118
        QueryValue::Xpath { space0, expr } => {
622
118
            visitor.visit_whitespace(space0);
623
118
            visitor.visit_template(expr);
624
        }
625
1918
        QueryValue::Jsonpath { space0, expr } => {
626
1918
            visitor.visit_whitespace(space0);
627
1918
            visitor.visit_template(expr);
628
        }
629
34
        QueryValue::Regex { space0, value } => {
630
34
            visitor.visit_whitespace(space0);
631
34
            match value {
632
18
                RegexValue::Template(t) => visitor.visit_template(t),
633
16
                RegexValue::Regex(r) => visitor.visit_regex(r),
634
            }
635
        }
636
198
        QueryValue::Variable { space0, name } => {
637
198
            visitor.visit_whitespace(space0);
638
198
            visitor.visit_template(name);
639
        }
640
        QueryValue::Certificate {
641
66
            space0,
642
66
            attribute_name,
643
66
        } => {
644
66
            visitor.visit_whitespace(space0);
645
66
            visitor.visit_string(attribute_name.to_source().as_str());
646
        }
647
        QueryValue::Body
648
        | QueryValue::Status
649
        | QueryValue::Url
650
        | QueryValue::Duration
651
        | QueryValue::Bytes
652
        | QueryValue::RawBytes
653
        | QueryValue::Sha256
654
        | QueryValue::Md5
655
        | QueryValue::Version
656
        | QueryValue::Ip
657
892
        | QueryValue::Redirects => {}
658
    }
659
}
660

            
661
3336
pub fn walk_predicate<V: Visitor>(visitor: &mut V, pred: &Predicate) {
662
3336
    if pred.not {
663
234
        visitor.visit_not("not");
664
234
        visitor.visit_whitespace(&pred.space0);
665
    }
666
3336
    let kind = &pred.predicate_func.value;
667
3336
    visitor.visit_predicate_kind(kind);
668
3336
    match kind {
669
2152
        PredicateFuncValue::Equal { space0, value } => {
670
2152
            visitor.visit_whitespace(space0);
671
2152
            visitor.visit_predicate_value(value);
672
        }
673
66
        PredicateFuncValue::NotEqual { space0, value } => {
674
66
            visitor.visit_whitespace(space0);
675
66
            visitor.visit_predicate_value(value);
676
        }
677
66
        PredicateFuncValue::GreaterThan { space0, value } => {
678
66
            visitor.visit_whitespace(space0);
679
66
            visitor.visit_predicate_value(value);
680
        }
681
18
        PredicateFuncValue::GreaterThanOrEqual { space0, value } => {
682
18
            visitor.visit_whitespace(space0);
683
18
            visitor.visit_predicate_value(value);
684
        }
685
52
        PredicateFuncValue::LessThan { space0, value } => {
686
52
            visitor.visit_whitespace(space0);
687
52
            visitor.visit_predicate_value(value);
688
        }
689
26
        PredicateFuncValue::LessThanOrEqual { space0, value } => {
690
26
            visitor.visit_whitespace(space0);
691
26
            visitor.visit_predicate_value(value);
692
        }
693
142
        PredicateFuncValue::StartWith { space0, value } => {
694
142
            visitor.visit_whitespace(space0);
695
142
            visitor.visit_predicate_value(value);
696
        }
697
36
        PredicateFuncValue::EndWith { space0, value } => {
698
36
            visitor.visit_whitespace(space0);
699
36
            visitor.visit_predicate_value(value);
700
        }
701
114
        PredicateFuncValue::Contain { space0, value } => {
702
114
            visitor.visit_whitespace(space0);
703
114
            visitor.visit_predicate_value(value);
704
        }
705
6
        PredicateFuncValue::Include { space0, value } => {
706
6
            visitor.visit_whitespace(space0);
707
6
            visitor.visit_predicate_value(value);
708
        }
709
98
        PredicateFuncValue::Match { space0, value } => {
710
98
            visitor.visit_whitespace(space0);
711
98
            visitor.visit_predicate_value(value);
712
        }
713
        PredicateFuncValue::Exist
714
        | PredicateFuncValue::IsBoolean
715
        | PredicateFuncValue::IsCollection
716
        | PredicateFuncValue::IsDate
717
        | PredicateFuncValue::IsEmpty
718
        | PredicateFuncValue::IsFloat
719
        | PredicateFuncValue::IsInteger
720
        | PredicateFuncValue::IsIpv4
721
        | PredicateFuncValue::IsIpv6
722
        | PredicateFuncValue::IsIsoDate
723
        | PredicateFuncValue::IsList
724
        | PredicateFuncValue::IsNumber
725
        | PredicateFuncValue::IsObject
726
        | PredicateFuncValue::IsString
727
560
        | PredicateFuncValue::IsUuid => {}
728
    }
729
}
730

            
731
2776
pub fn walk_predicate_value<V: Visitor>(visitor: &mut V, pred_value: &PredicateValue) {
732
2776
    match pred_value {
733
10
        PredicateValue::Base64(value) => visitor.visit_base64(value),
734
50
        PredicateValue::Bool(value) => visitor.visit_bool(*value),
735
14
        PredicateValue::File(value) => visitor.visit_file(value),
736
270
        PredicateValue::Hex(value) => visitor.visit_hex(value),
737
52
        PredicateValue::MultilineString(value) => visitor.visit_multiline_string(value),
738
22
        PredicateValue::Null => visitor.visit_null("null"),
739
884
        PredicateValue::Number(value) => visitor.visit_number(value),
740
106
        PredicateValue::Placeholder(placeholder) => visitor.visit_placeholder(placeholder),
741
68
        PredicateValue::Regex(value) => visitor.visit_regex(value),
742
1300
        PredicateValue::String(value) => visitor.visit_template(value),
743
    }
744
}
745

            
746
1522
pub fn walk_request<V: Visitor>(visitor: &mut V, request: &Request) {
747
2238
    request.line_terminators.iter().for_each(|lt| {
748
2238
        visitor.visit_lt(lt);
749
2238
    });
750
1522
    visitor.visit_whitespace(&request.space0);
751
1522
    visitor.visit_method(&request.method);
752
1522
    visitor.visit_whitespace(&request.space1);
753
1522
    visitor.visit_url(&request.url);
754
1522
    visitor.visit_lt(&request.line_terminator0);
755
1522
    request.headers.iter().for_each(|h| visitor.visit_kv(h));
756
1522
    request
757
1522
        .sections
758
1522
        .iter()
759
1522
        .for_each(|s| visitor.visit_section(s));
760
1522
    if let Some(body) = &request.body {
761
236
        visitor.visit_body(body);
762
    }
763
}
764

            
765
1244
pub fn walk_response<V: Visitor>(visitor: &mut V, response: &Response) {
766
1244
    response.line_terminators.iter().for_each(|lt| {
767
26
        visitor.visit_lt(lt);
768
26
    });
769
1244
    visitor.visit_whitespace(&response.space0);
770
1244
    visitor.visit_version(&response.version.value);
771
1244
    visitor.visit_whitespace(&response.space1);
772
1244
    visitor.visit_status(&response.status.value);
773
1244
    visitor.visit_lt(&response.line_terminator0);
774
1244
    response.headers.iter().for_each(|h| visitor.visit_kv(h));
775
1244
    response
776
1244
        .sections
777
1244
        .iter()
778
1244
        .for_each(|s| visitor.visit_section(s));
779
1244
    if let Some(body) = &response.body {
780
392
        visitor.visit_body(body);
781
    }
782
}
783

            
784
1196
pub fn walk_section<V: Visitor>(visitor: &mut V, section: &Section) {
785
1196
    section.line_terminators.iter().for_each(|lt| {
786
148
        visitor.visit_lt(lt);
787
148
    });
788
1196
    visitor.visit_whitespace(&section.space0);
789
1196
    let name = format!("[{}]", section.identifier());
790
1196
    visitor.visit_section_header(&name);
791
1196
    visitor.visit_lt(&section.line_terminator0);
792
1196
    visitor.visit_section_value(&section.value);
793
}
794

            
795
1196
pub fn walk_section_value<V: Visitor>(visitor: &mut V, section_value: &SectionValue) {
796
14
    match section_value {
797
3336
        SectionValue::Asserts(asserts) => asserts.iter().for_each(|a| visitor.visit_assert(a)),
798
14
        SectionValue::BasicAuth(Some(auth)) => visitor.visit_kv(auth),
799
        SectionValue::BasicAuth(_) => {}
800
248
        SectionValue::Captures(captures) => captures.iter().for_each(|c| visitor.visit_capture(c)),
801
54
        SectionValue::Cookies(cookies) => cookies.iter().for_each(|c| visitor.visit_cookie(c)),
802
80
        SectionValue::FormParams(params, _) => params.iter().for_each(|p| visitor.visit_kv(p)),
803
58
        SectionValue::MultipartFormData(params, _) => params.iter().for_each(|p| match p {
804
18
            MultipartParam::Param(param) => visitor.visit_kv(param),
805
40
            MultipartParam::FilenameParam(param) => visitor.visit_filename_param(param),
806
58
        }),
807
312
        SectionValue::Options(options) => {
808
1034
            options.iter().for_each(|o| visitor.visit_entry_option(o));
809
        }
810
160
        SectionValue::QueryParams(params, _) => params.iter().for_each(|p| visitor.visit_kv(p)),
811
    }
812
}
813

            
814
114
pub fn walk_variable_def<V: Visitor>(visitor: &mut V, def: &VariableDefinition) {
815
114
    visitor.visit_variable_name(&def.name);
816
114
    visitor.visit_whitespace(&def.space0);
817
114
    visitor.visit_literal("=");
818
114
    visitor.visit_whitespace(&def.space1);
819
114
    visitor.visit_variable_value(&def.value);
820
}
821

            
822
114
pub fn walk_variable_value<V: Visitor>(visitor: &mut V, value: &VariableValue) {
823
114
    match value {
824
6
        VariableValue::Null => visitor.visit_null("null"),
825
6
        VariableValue::Bool(value) => visitor.visit_bool(*value),
826
44
        VariableValue::Number(value) => visitor.visit_number(value),
827
58
        VariableValue::String(value) => visitor.visit_template(value),
828
    }
829
}
830

            
831
12
pub fn walk_verbosity_option<V: Visitor>(visitor: &mut V, value: &VerbosityOption) {
832
12
    visitor.visit_string(value.identifier());
833
}
834

            
835
#[cfg(test)]
836
mod tests {
837
    use crate::ast::Assert;
838
    use crate::ast::visit::Visitor;
839
    use crate::parser;
840

            
841
    #[test]
842
    fn test_walk_assert() {
843
        struct AssertWalker {
844
            count: usize,
845
        }
846

            
847
        impl Visitor for AssertWalker {
848
            fn visit_assert(&mut self, _assert: &Assert) {
849
                self.count += 1;
850
            }
851
        }
852

            
853
        let mut walker = AssertWalker { count: 0 };
854
        let content = r#"
855
GET https://foo.com
856
HTTP 200
857
[Asserts]
858
jsonpath "$.toto[0]" == "tata"
859
jsonpath "$.toto[1]" == "toto"
860
jsonpath "$.toto[2]" == "titi"
861
jsonpath "$.toto[3]" == "tata"
862
jsonpath "$.toto[4]" == "tutu"
863

            
864
GET https://foo.com
865
HTTP 200
866
[Asserts]
867
status == 200
868
header "Location" not exists
869
"#;
870
        let file = parser::parse_hurl_file(content).unwrap();
871
        walker.visit_hurl_file(&file);
872
        assert_eq!(walker.count, 7);
873
    }
874
}