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
//! 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::ast::{
24
    Assert, Base64, Body, BooleanOption, Bytes, Capture, Comment, Cookie, CookiePath, CountOption,
25
    DurationOption, Entry, EntryOption, File, FilenameParam, FilenameValue, Filter, FilterValue,
26
    Hex, HurlFile, IntegerValue, JsonValue, KeyValue, LineTerminator, Method, MultilineString,
27
    MultipartParam, NaturalOption, Number, OptionKind, Placeholder, Predicate, PredicateFuncValue,
28
    PredicateValue, Query, QueryValue, Regex, RegexValue, Request, Response, Section, SectionValue,
29
    StatusValue, Template, VariableDefinition, VariableValue, VersionValue, Whitespace, U64,
30
};
31
use crate::typing::{Count, Duration, DurationUnit, SourceString, ToSource};
32

            
33
/// Each method of the `Visitor` trait is a hook to be potentially overridden. Each method's default
34
/// implementation recursively visits the substructure of the input via the corresponding `walk` method;
35
/// e.g., the `visit_item` method by default calls `visit::walk_item`.
36
#[allow(unused_variables)]
37
pub trait Visitor: Sized {
38
3950
    fn visit_assert(&mut self, assert: &Assert) {
39
3950
        walk_assert(self, assert);
40
    }
41

            
42
80
    fn visit_base64(&mut self, value: &Base64) {
43
80
        walk_base64(self, value);
44
    }
45

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

            
48
754
    fn visit_body(&mut self, body: &Body) {
49
754
        walk_body(self, body);
50
    }
51

            
52
    fn visit_bool(&mut self, value: bool) {}
53

            
54
599
    fn visit_bool_option(&mut self, option: &BooleanOption) {
55
599
        walk_bool_option(self, option);
56
    }
57

            
58
308
    fn visit_capture(&mut self, capture: &Capture) {
59
308
        walk_capture(self, capture);
60
    }
61

            
62
69
    fn visit_cookie(&mut self, cookie: &Cookie) {
63
69
        walk_cookie(self, cookie);
64
    }
65

            
66
    fn visit_cookie_path(&mut self, path: &CookiePath) {}
67

            
68
    fn visit_comment(&mut self, comment: &Comment) {}
69

            
70
81
    fn visit_count(&mut self, count: Count) {
71
81
        walk_count(self, count);
72
    }
73

            
74
110
    fn visit_count_option(&mut self, option: &CountOption) {
75
110
        walk_count_option(self, option);
76
    }
77

            
78
74
    fn visit_duration(&mut self, duration: &Duration) {
79
74
        walk_duration(self, duration);
80
    }
81

            
82
106
    fn visit_duration_option(&mut self, option: &DurationOption) {
83
106
        walk_duration_option(self, option);
84
    }
85

            
86
    fn visit_duration_unit(&mut self, unit: DurationUnit) {}
87

            
88
270
    fn visit_entry(&mut self, entry: &Entry) {
89
270
        walk_entry(self, entry);
90
    }
91

            
92
1215
    fn visit_entry_option(&mut self, option: &EntryOption) {
93
1215
        walk_entry_option(self, option);
94
    }
95

            
96
100
    fn visit_file(&mut self, file: &File) {
97
100
        walk_file(self, file);
98
    }
99

            
100
51
    fn visit_filename_param(&mut self, param: &FilenameParam) {
101
51
        walk_filename_param(self, param);
102
    }
103

            
104
51
    fn visit_filename_value(&mut self, value: &FilenameValue) {
105
51
        walk_filename_value(self, value);
106
    }
107

            
108
    fn visit_filename(&mut self, filename: &Template) {}
109

            
110
1654
    fn visit_filter(&mut self, filter: &Filter) {
111
1654
        walk_filter(self, filter);
112
    }
113

            
114
    fn visit_filter_kind(&mut self, kind: &FilterValue) {}
115

            
116
    fn visit_header(&mut self, header: &KeyValue) {
117
        walk_header(self, header);
118
    }
119

            
120
346
    fn visit_hex(&mut self, hex: &Hex) {
121
346
        walk_hex(self, hex);
122
    }
123

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

            
126
72
    fn visit_hurl_file(&mut self, file: &HurlFile) {
127
72
        walk_hurl_file(self, file);
128
    }
129

            
130
218
    fn visit_integer_value(&mut self, n: &IntegerValue) {
131
218
        walk_integer_value(self, n);
132
    }
133

            
134
    fn visit_i64(&mut self, n: i64) {}
135

            
136
    fn visit_json_body(&mut self, json: &JsonValue) {}
137

            
138
1041
    fn visit_kv(&mut self, kv: &KeyValue) {
139
1041
        walk_kv(self, kv);
140
    }
141

            
142
15813
    fn visit_lt(&mut self, lt: &LineTerminator) {
143
15813
        walk_lt(self, lt);
144
    }
145

            
146
    fn visit_literal(&mut self, lit: &'static str) {}
147

            
148
    fn visit_method(&mut self, method: &Method) {}
149

            
150
    fn visit_multiline_string(&mut self, string: &MultilineString) {}
151

            
152
16
    fn visit_natural_option(&mut self, option: &NaturalOption) {
153
16
        walk_natural_option(self, option);
154
    }
155

            
156
    fn visit_not(&mut self, identifier: &'static str) {}
157

            
158
    fn visit_null(&mut self, identifier: &'static str) {}
159

            
160
    fn visit_number(&mut self, number: &Number) {}
161

            
162
    fn visit_placeholder(&mut self, placeholder: &Placeholder) {}
163

            
164
3950
    fn visit_predicate(&mut self, predicate: &Predicate) {
165
3950
        walk_predicate(self, predicate);
166
    }
167

            
168
    fn visit_predicate_kind(&mut self, kind: &PredicateFuncValue) {}
169

            
170
3356
    fn visit_predicate_value(&mut self, value: &PredicateValue) {
171
3356
        walk_predicate_value(self, value);
172
    }
173

            
174
4258
    fn visit_query(&mut self, query: &Query) {
175
4258
        walk_query(self, query);
176
    }
177

            
178
    fn visit_query_kind(&mut self, kind: &QueryValue) {}
179

            
180
270
    fn visit_request(&mut self, request: &Request) {
181
270
        walk_request(self, request);
182
    }
183

            
184
114
    fn visit_response(&mut self, response: &Response) {
185
114
        walk_response(self, response);
186
    }
187

            
188
    fn visit_regex(&mut self, regex: &Regex) {}
189

            
190
1478
    fn visit_section(&mut self, section: &Section) {
191
1478
        walk_section(self, section);
192
    }
193

            
194
    fn visit_status(&mut self, value: &StatusValue) {}
195

            
196
    fn visit_string(&mut self, value: &str) {}
197

            
198
    fn visit_section_header(&mut self, name: &str) {}
199

            
200
1478
    fn visit_section_value(&mut self, section_value: &SectionValue) {
201
1478
        walk_section_value(self, section_value);
202
    }
203

            
204
    fn visit_template(&mut self, template: &Template) {}
205

            
206
    fn visit_url(&mut self, url: &Template) {}
207

            
208
    fn visit_u64(&mut self, n: &U64) {}
209

            
210
    fn visit_usize(&mut self, n: usize) {}
211

            
212
142
    fn visit_variable_def(&mut self, def: &VariableDefinition) {
213
142
        walk_variable_def(self, def);
214
    }
215

            
216
    fn visit_variable_name(&mut self, name: &str) {}
217

            
218
142
    fn visit_variable_value(&mut self, value: &VariableValue) {
219
142
        walk_variable_value(self, value);
220
    }
221

            
222
    fn visit_version(&mut self, value: &VersionValue) {}
223

            
224
    fn visit_xml_body(&mut self, xml: &str) {}
225

            
226
    fn visit_whitespace(&mut self, ws: &Whitespace) {}
227
}
228

            
229
3950
pub fn walk_assert<V: Visitor>(visitor: &mut V, assert: &Assert) {
230
3950
    assert.line_terminators.iter().for_each(|lt| {
231
70
        visitor.visit_lt(lt);
232
70
    });
233
3950
    visitor.visit_whitespace(&assert.space0);
234
3950
    visitor.visit_query(&assert.query);
235
3950
    for (space, filter) in assert.filters.iter() {
236
1596
        visitor.visit_whitespace(space);
237
1596
        visitor.visit_filter(filter);
238
    }
239
3950
    visitor.visit_whitespace(&assert.space1);
240
3950
    visitor.visit_predicate(&assert.predicate);
241
3950
    visitor.visit_lt(&assert.line_terminator0);
242
}
243

            
244
80
pub fn walk_base64<V: Visitor>(visitor: &mut V, base64: &Base64) {
245
80
    visitor.visit_literal("base64,");
246
80
    visitor.visit_whitespace(&base64.space0);
247
80
    visitor.visit_base64_value(&base64.value, &base64.source);
248
80
    visitor.visit_whitespace(&base64.space1);
249
80
    visitor.visit_literal(";");
250
}
251

            
252
754
pub fn walk_body<V: Visitor>(visitor: &mut V, body: &Body) {
253
754
    body.line_terminators.iter().for_each(|lt| {
254
90
        visitor.visit_lt(lt);
255
90
    });
256
754
    visitor.visit_whitespace(&body.space0);
257
754
    match &body.value {
258
76
        Bytes::Json(value) => visitor.visit_json_body(value),
259
28
        Bytes::Xml(value) => visitor.visit_xml_body(value),
260
197
        Bytes::MultilineString(value) => visitor.visit_multiline_string(value),
261
265
        Bytes::OnelineString(value) => visitor.visit_template(value),
262
67
        Bytes::Base64(value) => visitor.visit_base64(value),
263
82
        Bytes::File(value) => visitor.visit_file(value),
264
39
        Bytes::Hex(value) => visitor.visit_hex(value),
265
    }
266
754
    visitor.visit_lt(&body.line_terminator0);
267
}
268

            
269
599
pub fn walk_bool_option<V: Visitor>(visitor: &mut V, option: &BooleanOption) {
270
599
    match option {
271
450
        BooleanOption::Literal(value) => visitor.visit_bool(*value),
272
149
        BooleanOption::Placeholder(value) => visitor.visit_placeholder(value),
273
    }
274
}
275

            
276
308
pub fn walk_capture<V: Visitor>(visitor: &mut V, capture: &Capture) {
277
308
    capture.line_terminators.iter().for_each(|lt| {
278
10
        visitor.visit_lt(lt);
279
10
    });
280
308
    visitor.visit_whitespace(&capture.space0);
281
308
    visitor.visit_template(&capture.name);
282
308
    visitor.visit_whitespace(&capture.space1);
283
308
    visitor.visit_literal(":");
284
308
    visitor.visit_whitespace(&capture.space2);
285
308
    visitor.visit_query(&capture.query);
286
308
    for (space, filter) in capture.filters.iter() {
287
58
        visitor.visit_whitespace(space);
288
58
        visitor.visit_filter(filter);
289
    }
290
308
    if capture.redacted {
291
36
        visitor.visit_whitespace(&capture.space3);
292
36
        // The next node should have been literal to be more correct
293
36
        // we visit a string instead to be comptaible with <= 6.1.1 HTML export
294
36
        // visitor.visit_literal("redact");
295
36
        visitor.visit_string("redact");
296
    }
297
308
    visitor.visit_lt(&capture.line_terminator0);
298
}
299

            
300
69
pub fn walk_cookie<V: Visitor>(visitor: &mut V, cookie: &Cookie) {
301
69
    cookie.line_terminators.iter().for_each(|lt| {
302
        visitor.visit_lt(lt);
303
    });
304
69
    visitor.visit_whitespace(&cookie.space0);
305
69
    visitor.visit_template(&cookie.name);
306
69
    visitor.visit_whitespace(&cookie.space1);
307
69
    visitor.visit_literal(":");
308
69
    visitor.visit_whitespace(&cookie.space2);
309
69
    visitor.visit_template(&cookie.value);
310
69
    visitor.visit_lt(&cookie.line_terminator0);
311
}
312

            
313
81
pub fn walk_count<V: Visitor>(visitor: &mut V, count: Count) {
314
81
    match count {
315
65
        Count::Finite(count) => visitor.visit_usize(count),
316
16
        Count::Infinite => visitor.visit_i64(-1),
317
    }
318
}
319

            
320
110
pub fn walk_count_option<V: Visitor>(visitor: &mut V, option: &CountOption) {
321
110
    match option {
322
81
        CountOption::Literal(value) => visitor.visit_count(*value),
323
29
        CountOption::Placeholder(value) => visitor.visit_placeholder(value),
324
    }
325
}
326

            
327
74
pub fn walk_duration<V: Visitor>(visitor: &mut V, duration: &Duration) {
328
74
    visitor.visit_u64(&duration.value);
329
74
    if let Some(unit) = duration.unit {
330
64
        visitor.visit_duration_unit(unit);
331
    }
332
}
333

            
334
106
pub fn walk_duration_option<V: Visitor>(visitor: &mut V, option: &DurationOption) {
335
106
    match option {
336
74
        DurationOption::Literal(value) => visitor.visit_duration(value),
337
32
        DurationOption::Placeholder(value) => visitor.visit_placeholder(value),
338
    }
339
}
340

            
341
1915
pub fn walk_entry<V: Visitor>(visitor: &mut V, entry: &Entry) {
342
1915
    visitor.visit_request(&entry.request);
343
1915
    if let Some(ref response) = entry.response {
344
1539
        visitor.visit_response(response);
345
    }
346
}
347

            
348
1215
pub fn walk_entry_option<V: Visitor>(visitor: &mut V, option: &EntryOption) {
349
1215
    option.line_terminators.iter().for_each(|lt| {
350
13
        visitor.visit_lt(lt);
351
13
    });
352
1215
    visitor.visit_whitespace(&option.space0);
353
1215
    visitor.visit_string(option.kind.identifier());
354
1215
    visitor.visit_whitespace(&option.space1);
355
1215
    visitor.visit_literal(":");
356
1215
    visitor.visit_whitespace(&option.space2);
357
1215
    match &option.kind {
358
16
        OptionKind::AwsSigV4(value) => visitor.visit_template(value),
359
16
        OptionKind::CaCertificate(filename) => visitor.visit_filename(filename),
360
24
        OptionKind::ClientCert(filename) => visitor.visit_filename(filename),
361
16
        OptionKind::ClientKey(filename) => visitor.visit_filename(filename),
362
111
        OptionKind::Compressed(value) => visitor.visit_bool_option(value),
363
16
        OptionKind::ConnectTo(value) => visitor.visit_template(value),
364
16
        OptionKind::ConnectTimeout(value) => visitor.visit_duration_option(value),
365
32
        OptionKind::Delay(value) => visitor.visit_duration_option(value),
366
109
        OptionKind::FollowLocation(value) => visitor.visit_bool_option(value),
367
21
        OptionKind::FollowLocationTrusted(value) => visitor.visit_bool_option(value),
368
16
        OptionKind::Header(value) => visitor.visit_template(value),
369
46
        OptionKind::Http10(value) => visitor.visit_bool_option(value),
370
36
        OptionKind::Http11(value) => visitor.visit_bool_option(value),
371
16
        OptionKind::Http2(value) => visitor.visit_bool_option(value),
372
16
        OptionKind::Http3(value) => visitor.visit_bool_option(value),
373
29
        OptionKind::Insecure(value) => visitor.visit_bool_option(value),
374
16
        OptionKind::IpV4(value) => visitor.visit_bool_option(value),
375
16
        OptionKind::IpV6(value) => visitor.visit_bool_option(value),
376
16
        OptionKind::LimitRate(value) => visitor.visit_natural_option(value),
377
26
        OptionKind::MaxRedirect(value) => visitor.visit_count_option(value),
378
16
        OptionKind::MaxTime(value) => visitor.visit_duration_option(value),
379
19
        OptionKind::Negotiate(value) => visitor.visit_bool_option(value),
380
16
        OptionKind::NetRc(value) => visitor.visit_bool_option(value),
381
16
        OptionKind::NetRcFile(filename) => visitor.visit_filename(filename),
382
16
        OptionKind::NetRcOptional(value) => visitor.visit_bool_option(value),
383
19
        OptionKind::Ntlm(value) => visitor.visit_bool_option(value),
384
16
        OptionKind::Output(filename) => visitor.visit_filename(filename),
385
16
        OptionKind::PathAsIs(value) => visitor.visit_bool_option(value),
386
16
        OptionKind::PinnedPublicKey(value) => visitor.visit_template(value),
387
26
        OptionKind::Proxy(value) => visitor.visit_template(value),
388
34
        OptionKind::Repeat(value) => visitor.visit_count_option(value),
389
16
        OptionKind::Resolve(value) => visitor.visit_template(value),
390
50
        OptionKind::Retry(value) => visitor.visit_count_option(value),
391
42
        OptionKind::RetryInterval(value) => visitor.visit_duration_option(value),
392
16
        OptionKind::Skip(value) => visitor.visit_bool_option(value),
393
16
        OptionKind::UnixSocket(value) => visitor.visit_filename(value),
394
32
        OptionKind::User(value) => visitor.visit_template(value),
395
142
        OptionKind::Variable(value) => visitor.visit_variable_def(value),
396
60
        OptionKind::Verbose(value) => visitor.visit_bool_option(value),
397
21
        OptionKind::VeryVerbose(value) => visitor.visit_bool_option(value),
398
    };
399
1215
    visitor.visit_lt(&option.line_terminator0);
400
}
401

            
402
100
pub fn walk_file<V: Visitor>(visitor: &mut V, file: &File) {
403
100
    visitor.visit_literal("file,");
404
100
    visitor.visit_whitespace(&file.space0);
405
100
    visitor.visit_filename(&file.filename);
406
100
    visitor.visit_whitespace(&file.space1);
407
100
    visitor.visit_literal(";");
408
}
409

            
410
1654
pub fn walk_filter<V: Visitor>(visitor: &mut V, filter: &Filter) {
411
1654
    visitor.visit_filter_kind(&filter.value);
412
1654
    match &filter.value {
413
51
        FilterValue::Base64Decode => {}
414
23
        FilterValue::Base64Encode => {}
415
38
        FilterValue::Base64UrlSafeDecode => {}
416
23
        FilterValue::Base64UrlSafeEncode => {}
417
325
        FilterValue::Count => {}
418
13
        FilterValue::DaysAfterNow => {}
419
26
        FilterValue::DaysBeforeNow => {}
420
51
        FilterValue::Decode { space0, encoding } => {
421
51
            visitor.visit_whitespace(space0);
422
51
            visitor.visit_template(encoding);
423
        }
424
28
        FilterValue::First => {}
425
26
        FilterValue::Format { space0, fmt } => {
426
26
            visitor.visit_whitespace(space0);
427
26
            visitor.visit_template(fmt);
428
        }
429
64
        FilterValue::DateFormat { space0, fmt } => {
430
64
            visitor.visit_whitespace(space0);
431
64
            visitor.visit_template(fmt);
432
        }
433
23
        FilterValue::HtmlEscape => {}
434
33
        FilterValue::HtmlUnescape => {}
435
44
        FilterValue::JsonPath { space0, expr } => {
436
44
            visitor.visit_whitespace(space0);
437
44
            visitor.visit_template(expr);
438
        }
439
28
        FilterValue::Last => {}
440
80
        FilterValue::Location => {}
441
218
        FilterValue::Nth { space0, n } => {
442
218
            visitor.visit_whitespace(space0);
443
218
            visitor.visit_integer_value(n);
444
        }
445
48
        FilterValue::Regex { space0, value } => {
446
48
            visitor.visit_whitespace(space0);
447
48
            match value {
448
10
                RegexValue::Template(value) => visitor.visit_template(value),
449
38
                RegexValue::Regex(regex) => visitor.visit_regex(regex),
450
            }
451
        }
452
        FilterValue::Replace {
453
55
            space0,
454
55
            old_value,
455
55
            space1,
456
55
            new_value,
457
55
        } => {
458
55
            visitor.visit_whitespace(space0);
459
55
            visitor.visit_template(old_value);
460
55
            visitor.visit_whitespace(space1);
461
55
            visitor.visit_template(new_value);
462
        }
463
        FilterValue::ReplaceRegex {
464
33
            space0,
465
33
            pattern,
466
33
            space1,
467
33
            new_value,
468
        } => {
469
33
            visitor.visit_whitespace(space0);
470
33
            match pattern {
471
10
                RegexValue::Template(value) => visitor.visit_template(value),
472
23
                RegexValue::Regex(regex) => visitor.visit_regex(regex),
473
            }
474
33
            visitor.visit_whitespace(space1);
475
33
            visitor.visit_template(new_value);
476
        }
477
28
        FilterValue::Split { space0, sep } => {
478
28
            visitor.visit_whitespace(space0);
479
28
            visitor.visit_template(sep);
480
        }
481
108
        FilterValue::ToDate { space0, fmt } => {
482
108
            visitor.visit_whitespace(space0);
483
108
            visitor.visit_template(fmt);
484
        }
485
48
        FilterValue::ToFloat => {}
486
41
        FilterValue::ToHex => {}
487
48
        FilterValue::ToInt => {}
488
18
        FilterValue::ToString => {}
489
23
        FilterValue::UrlDecode => {}
490
23
        FilterValue::UrlEncode => {}
491
33
        FilterValue::UrlQueryParam { space0, param } => {
492
33
            visitor.visit_whitespace(space0);
493
33
            visitor.visit_template(param);
494
        }
495
13
        FilterValue::Utf8Decode => {}
496
13
        FilterValue::Utf8Encode => {}
497
28
        FilterValue::XPath { space0, expr } => {
498
28
            visitor.visit_whitespace(space0);
499
28
            visitor.visit_template(expr);
500
        }
501
    }
502
}
503

            
504
51
pub fn walk_filename_param<V: Visitor>(visitor: &mut V, param: &FilenameParam) {
505
51
    param.line_terminators.iter().for_each(|lt| {
506
        visitor.visit_lt(lt);
507
    });
508
51
    visitor.visit_whitespace(&param.space0);
509
51
    visitor.visit_template(&param.key);
510
51
    visitor.visit_whitespace(&param.space1);
511
51
    visitor.visit_literal(":");
512
51
    visitor.visit_whitespace(&param.space2);
513
51
    visitor.visit_filename_value(&param.value);
514
51
    visitor.visit_lt(&param.line_terminator0);
515
}
516

            
517
51
pub fn walk_filename_value<V: Visitor>(visitor: &mut V, value: &FilenameValue) {
518
51
    visitor.visit_literal("file,");
519
51
    visitor.visit_whitespace(&value.space0);
520
51
    visitor.visit_filename(&value.filename);
521
51
    visitor.visit_whitespace(&value.space1);
522
51
    visitor.visit_literal(";");
523
51
    visitor.visit_whitespace(&value.space2);
524
51
    if let Some(content_type) = &value.content_type {
525
18
        visitor.visit_template(content_type);
526
    }
527
}
528

            
529
pub fn walk_header<V: Visitor>(visitor: &mut V, header: &KeyValue) {
530
    visitor.visit_kv(header);
531
}
532

            
533
346
pub fn walk_hex<V: Visitor>(visitor: &mut V, hex: &Hex) {
534
346
    visitor.visit_literal("hex,");
535
346
    visitor.visit_whitespace(&hex.space0);
536
346
    visitor.visit_hex_value(&hex.value, &hex.source);
537
346
    visitor.visit_whitespace(&hex.space1);
538
346
    visitor.visit_literal(";");
539
}
540

            
541
667
pub fn walk_hurl_file<V: Visitor>(visitor: &mut V, file: &HurlFile) {
542
1915
    file.entries.iter().for_each(|e| visitor.visit_entry(e));
543
667
    file.line_terminators.iter().for_each(|lt| {
544
304
        visitor.visit_lt(lt);
545
304
    });
546
}
547

            
548
218
pub fn walk_integer_value<V: Visitor>(visitor: &mut V, n: &IntegerValue) {
549
218
    match n {
550
208
        IntegerValue::Literal(value) => visitor.visit_i64(value.as_i64()),
551
10
        IntegerValue::Placeholder(value) => visitor.visit_placeholder(value),
552
    }
553
}
554

            
555
1041
pub fn walk_kv<V: Visitor>(visitor: &mut V, kv: &KeyValue) {
556
1041
    kv.line_terminators.iter().for_each(|lt| {
557
23
        visitor.visit_lt(lt);
558
23
    });
559
1041
    visitor.visit_whitespace(&kv.space0);
560
1041
    visitor.visit_template(&kv.key);
561
1041
    visitor.visit_whitespace(&kv.space1);
562
1041
    visitor.visit_literal(":");
563
1041
    visitor.visit_whitespace(&kv.space2);
564
1041
    visitor.visit_template(&kv.value);
565
1041
    visitor.visit_lt(&kv.line_terminator0);
566
}
567

            
568
15813
pub fn walk_lt<V: Visitor>(visitor: &mut V, lt: &LineTerminator) {
569
15813
    visitor.visit_whitespace(&lt.space0);
570
15813
    if let Some(ref comment) = lt.comment {
571
1975
        visitor.visit_comment(comment);
572
    }
573
15813
    visitor.visit_whitespace(&lt.newline);
574
}
575

            
576
16
pub fn walk_natural_option<V: Visitor>(visitor: &mut V, option: &NaturalOption) {
577
16
    match option {
578
8
        NaturalOption::Literal(value) => visitor.visit_u64(value),
579
8
        NaturalOption::Placeholder(value) => visitor.visit_placeholder(value),
580
    }
581
}
582

            
583
4258
pub fn walk_query<V: Visitor>(visitor: &mut V, query: &Query) {
584
4258
    visitor.visit_query_kind(&query.value);
585

            
586
4258
    match &query.value {
587
342
        QueryValue::Header { space0, name } => {
588
342
            visitor.visit_whitespace(space0);
589
342
            visitor.visit_template(name);
590
        }
591
89
        QueryValue::Cookie { space0, expr } => {
592
89
            visitor.visit_whitespace(space0);
593
89
            visitor.visit_cookie_path(expr);
594
        }
595
148
        QueryValue::Xpath { space0, expr } => {
596
148
            visitor.visit_whitespace(space0);
597
148
            visitor.visit_template(expr);
598
        }
599
2277
        QueryValue::Jsonpath { space0, expr } => {
600
2277
            visitor.visit_whitespace(space0);
601
2277
            visitor.visit_template(expr);
602
        }
603
43
        QueryValue::Regex { space0, value } => {
604
43
            visitor.visit_whitespace(space0);
605
43
            match value {
606
23
                RegexValue::Template(t) => visitor.visit_template(t),
607
20
                RegexValue::Regex(r) => visitor.visit_regex(r),
608
            }
609
        }
610
239
        QueryValue::Variable { space0, name } => {
611
239
            visitor.visit_whitespace(space0);
612
239
            visitor.visit_template(name);
613
        }
614
        QueryValue::Certificate {
615
88
            space0,
616
88
            attribute_name,
617
88
        } => {
618
88
            visitor.visit_whitespace(space0);
619
88
            visitor.visit_string(attribute_name.to_source().as_str());
620
        }
621
        QueryValue::Body
622
        | QueryValue::Status
623
        | QueryValue::Url
624
        | QueryValue::Duration
625
        | QueryValue::Bytes
626
        | QueryValue::Sha256
627
        | QueryValue::Md5
628
        | QueryValue::Version
629
        | QueryValue::Ip
630
1032
        | QueryValue::Redirects => {}
631
    }
632
}
633

            
634
3950
pub fn walk_predicate<V: Visitor>(visitor: &mut V, pred: &Predicate) {
635
3950
    if pred.not {
636
213
        visitor.visit_not("not");
637
213
        visitor.visit_whitespace(&pred.space0);
638
    }
639
3950
    let kind = &pred.predicate_func.value;
640
3950
    visitor.visit_predicate_kind(kind);
641
3950
    match kind {
642
2614
        PredicateFuncValue::Equal { space0, value } => {
643
2614
            visitor.visit_whitespace(space0);
644
2614
            visitor.visit_predicate_value(value);
645
        }
646
69
        PredicateFuncValue::NotEqual { space0, value } => {
647
69
            visitor.visit_whitespace(space0);
648
69
            visitor.visit_predicate_value(value);
649
        }
650
84
        PredicateFuncValue::GreaterThan { space0, value } => {
651
84
            visitor.visit_whitespace(space0);
652
84
            visitor.visit_predicate_value(value);
653
        }
654
23
        PredicateFuncValue::GreaterThanOrEqual { space0, value } => {
655
23
            visitor.visit_whitespace(space0);
656
23
            visitor.visit_predicate_value(value);
657
        }
658
67
        PredicateFuncValue::LessThan { space0, value } => {
659
67
            visitor.visit_whitespace(space0);
660
67
            visitor.visit_predicate_value(value);
661
        }
662
33
        PredicateFuncValue::LessThanOrEqual { space0, value } => {
663
33
            visitor.visit_whitespace(space0);
664
33
            visitor.visit_predicate_value(value);
665
        }
666
144
        PredicateFuncValue::StartWith { space0, value } => {
667
144
            visitor.visit_whitespace(space0);
668
144
            visitor.visit_predicate_value(value);
669
        }
670
46
        PredicateFuncValue::EndWith { space0, value } => {
671
46
            visitor.visit_whitespace(space0);
672
46
            visitor.visit_predicate_value(value);
673
        }
674
144
        PredicateFuncValue::Contain { space0, value } => {
675
144
            visitor.visit_whitespace(space0);
676
144
            visitor.visit_predicate_value(value);
677
        }
678
8
        PredicateFuncValue::Include { space0, value } => {
679
8
            visitor.visit_whitespace(space0);
680
8
            visitor.visit_predicate_value(value);
681
        }
682
124
        PredicateFuncValue::Match { space0, value } => {
683
124
            visitor.visit_whitespace(space0);
684
124
            visitor.visit_predicate_value(value);
685
        }
686
        PredicateFuncValue::IsInteger
687
        | PredicateFuncValue::IsFloat
688
        | PredicateFuncValue::IsBoolean
689
        | PredicateFuncValue::IsString
690
        | PredicateFuncValue::IsCollection
691
        | PredicateFuncValue::IsDate
692
        | PredicateFuncValue::IsIsoDate
693
        | PredicateFuncValue::Exist
694
        | PredicateFuncValue::IsEmpty
695
        | PredicateFuncValue::IsNumber
696
        | PredicateFuncValue::IsIpv4
697
        | PredicateFuncValue::IsIpv6
698
594
        | PredicateFuncValue::IsUuid => {}
699
    }
700
}
701

            
702
3356
pub fn walk_predicate_value<V: Visitor>(visitor: &mut V, pred_value: &PredicateValue) {
703
3356
    match pred_value {
704
13
        PredicateValue::Base64(value) => visitor.visit_base64(value),
705
63
        PredicateValue::Bool(value) => visitor.visit_bool(*value),
706
18
        PredicateValue::File(value) => visitor.visit_file(value),
707
307
        PredicateValue::Hex(value) => visitor.visit_hex(value),
708
68
        PredicateValue::MultilineString(value) => visitor.visit_multiline_string(value),
709
28
        PredicateValue::Null => visitor.visit_null("null"),
710
998
        PredicateValue::Number(value) => visitor.visit_number(value),
711
133
        PredicateValue::Placeholder(placeholder) => visitor.visit_placeholder(placeholder),
712
86
        PredicateValue::Regex(value) => visitor.visit_regex(value),
713
1642
        PredicateValue::String(value) => visitor.visit_template(value),
714
    }
715
}
716

            
717
1915
pub fn walk_request<V: Visitor>(visitor: &mut V, request: &Request) {
718
2793
    request.line_terminators.iter().for_each(|lt| {
719
2767
        visitor.visit_lt(lt);
720
2767
    });
721
1915
    visitor.visit_whitespace(&request.space0);
722
1915
    visitor.visit_method(&request.method);
723
1915
    visitor.visit_whitespace(&request.space1);
724
1915
    visitor.visit_url(&request.url);
725
1915
    visitor.visit_lt(&request.line_terminator0);
726
1915
    request.headers.iter().for_each(|h| visitor.visit_kv(h));
727
1915
    request
728
1915
        .sections
729
1915
        .iter()
730
1915
        .for_each(|s| visitor.visit_section(s));
731
1915
    if let Some(body) = &request.body {
732
289
        visitor.visit_body(body);
733
    }
734
}
735

            
736
1539
pub fn walk_response<V: Visitor>(visitor: &mut V, response: &Response) {
737
1539
    response.line_terminators.iter().for_each(|lt| {
738
41
        visitor.visit_lt(lt);
739
41
    });
740
1539
    visitor.visit_whitespace(&response.space0);
741
1539
    visitor.visit_version(&response.version.value);
742
1539
    visitor.visit_whitespace(&response.space1);
743
1539
    visitor.visit_status(&response.status.value);
744
1539
    visitor.visit_lt(&response.line_terminator0);
745
1539
    response.headers.iter().for_each(|h| visitor.visit_kv(h));
746
1539
    response
747
1539
        .sections
748
1539
        .iter()
749
1539
        .for_each(|s| visitor.visit_section(s));
750
1539
    if let Some(body) = &response.body {
751
465
        visitor.visit_body(body);
752
    }
753
}
754

            
755
1478
pub fn walk_section<V: Visitor>(visitor: &mut V, section: &Section) {
756
1478
    section.line_terminators.iter().for_each(|lt| {
757
175
        visitor.visit_lt(lt);
758
175
    });
759
1478
    visitor.visit_whitespace(&section.space0);
760
1478
    let name = format!("[{}]", section.identifier());
761
1478
    visitor.visit_section_header(&name);
762
1478
    visitor.visit_lt(&section.line_terminator0);
763
1478
    visitor.visit_section_value(&section.value);
764
}
765

            
766
1478
pub fn walk_section_value<V: Visitor>(visitor: &mut V, section_value: &SectionValue) {
767
18
    match section_value {
768
3950
        SectionValue::Asserts(asserts) => asserts.iter().for_each(|a| visitor.visit_assert(a)),
769
18
        SectionValue::BasicAuth(Some(auth)) => visitor.visit_kv(auth),
770
        SectionValue::BasicAuth(_) => {}
771
308
        SectionValue::Captures(captures) => captures.iter().for_each(|c| visitor.visit_capture(c)),
772
69
        SectionValue::Cookies(cookies) => cookies.iter().for_each(|c| visitor.visit_cookie(c)),
773
92
        SectionValue::FormParams(params, _) => params.iter().for_each(|p| visitor.visit_kv(p)),
774
74
        SectionValue::MultipartFormData(params, _) => params.iter().for_each(|p| match p {
775
23
            MultipartParam::Param(param) => visitor.visit_kv(param),
776
51
            MultipartParam::FilenameParam(param) => visitor.visit_filename_param(param),
777
74
        }),
778
387
        SectionValue::Options(options) => {
779
1215
            options.iter().for_each(|o| visitor.visit_entry_option(o));
780
        }
781
203
        SectionValue::QueryParams(params, _) => params.iter().for_each(|p| visitor.visit_kv(p)),
782
    }
783
}
784

            
785
142
pub fn walk_variable_def<V: Visitor>(visitor: &mut V, def: &VariableDefinition) {
786
142
    visitor.visit_variable_name(&def.name);
787
142
    visitor.visit_whitespace(&def.space0);
788
142
    visitor.visit_literal("=");
789
142
    visitor.visit_whitespace(&def.space1);
790
142
    visitor.visit_variable_value(&def.value);
791
}
792

            
793
142
pub fn walk_variable_value<V: Visitor>(visitor: &mut V, value: &VariableValue) {
794
142
    match value {
795
8
        VariableValue::Null => visitor.visit_null("null"),
796
8
        VariableValue::Bool(value) => visitor.visit_bool(*value),
797
56
        VariableValue::Number(value) => visitor.visit_number(value),
798
70
        VariableValue::String(value) => visitor.visit_template(value),
799
    }
800
}
801

            
802
#[cfg(test)]
803
mod tests {
804
    use crate::ast::visit::Visitor;
805
    use crate::ast::Assert;
806
    use crate::parser;
807

            
808
    #[test]
809
    fn test_walk_assert() {
810
        struct AssertWalker {
811
            count: usize,
812
        }
813

            
814
        impl Visitor for AssertWalker {
815
            fn visit_assert(&mut self, _assert: &Assert) {
816
                self.count += 1;
817
            }
818
        }
819

            
820
        let mut walker = AssertWalker { count: 0 };
821
        let content = r#"
822
GET https://foo.com
823
HTTP 200
824
[Asserts]
825
jsonpath "$.toto[0]" == "tata"
826
jsonpath "$.toto[1]" == "toto"
827
jsonpath "$.toto[2]" == "titi"
828
jsonpath "$.toto[3]" == "tata"
829
jsonpath "$.toto[4]" == "tutu"
830

            
831
GET https://foo.com
832
HTTP 200
833
[Asserts]
834
status == 200
835
header "Location" not exists
836
"#;
837
        let file = parser::parse_hurl_file(content).unwrap();
838
        walker.visit_hurl_file(&file);
839
        assert_eq!(walker.count, 7);
840
    }
841
}