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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
223
16
    fn visit_verbosity_option(&mut self, value: &VerbosityOption) {
224
16
        walk_verbosity_option(self, value);
225
    }
226

            
227
    fn visit_version(&mut self, value: &VersionValue) {}
228

            
229
    fn visit_xml_body(&mut self, xml: &str) {}
230

            
231
    fn visit_whitespace(&mut self, ws: &Whitespace) {}
232
}
233

            
234
4214
pub fn walk_assert<V: Visitor>(visitor: &mut V, assert: &Assert) {
235
4214
    assert.line_terminators.iter().for_each(|lt| {
236
85
        visitor.visit_lt(lt);
237
85
    });
238
4214
    visitor.visit_whitespace(&assert.space0);
239
4214
    visitor.visit_query(&assert.query);
240
4214
    for (space, filter) in assert.filters.iter() {
241
1629
        visitor.visit_whitespace(space);
242
1629
        visitor.visit_filter(filter);
243
    }
244
4214
    visitor.visit_whitespace(&assert.space1);
245
4214
    visitor.visit_predicate(&assert.predicate);
246
4214
    visitor.visit_lt(&assert.line_terminator0);
247
}
248

            
249
80
pub fn walk_base64<V: Visitor>(visitor: &mut V, base64: &Base64) {
250
80
    visitor.visit_literal("base64,");
251
80
    visitor.visit_whitespace(&base64.space0);
252
80
    visitor.visit_base64_value(&base64.value, &base64.source);
253
80
    visitor.visit_whitespace(&base64.space1);
254
80
    visitor.visit_literal(";");
255
}
256

            
257
803
pub fn walk_body<V: Visitor>(visitor: &mut V, body: &Body) {
258
803
    body.line_terminators.iter().for_each(|lt| {
259
98
        visitor.visit_lt(lt);
260
98
    });
261
803
    visitor.visit_whitespace(&body.space0);
262
803
    match &body.value {
263
86
        Bytes::Json(value) => visitor.visit_json_body(value),
264
28
        Bytes::Xml(value) => visitor.visit_xml_body(value),
265
236
        Bytes::MultilineString(value) => visitor.visit_multiline_string(value),
266
265
        Bytes::OnelineString(value) => visitor.visit_template(value),
267
67
        Bytes::Base64(value) => visitor.visit_base64(value),
268
82
        Bytes::File(value) => visitor.visit_file(value),
269
39
        Bytes::Hex(value) => visitor.visit_hex(value),
270
    }
271
803
    visitor.visit_lt(&body.line_terminator0);
272
}
273

            
274
652
pub fn walk_bool_option<V: Visitor>(visitor: &mut V, option: &BooleanOption) {
275
652
    match option {
276
487
        BooleanOption::Literal(value) => visitor.visit_bool(*value),
277
165
        BooleanOption::Placeholder(value) => visitor.visit_placeholder(value),
278
    }
279
}
280

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

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

            
318
81
pub fn walk_count<V: Visitor>(visitor: &mut V, count: Count) {
319
81
    match count {
320
65
        Count::Finite(count) => visitor.visit_usize(count),
321
16
        Count::Infinite => visitor.visit_i64(-1),
322
    }
323
}
324

            
325
110
pub fn walk_count_option<V: Visitor>(visitor: &mut V, option: &CountOption) {
326
110
    match option {
327
81
        CountOption::Literal(value) => visitor.visit_count(*value),
328
29
        CountOption::Placeholder(value) => visitor.visit_placeholder(value),
329
    }
330
}
331

            
332
90
pub fn walk_duration<V: Visitor>(visitor: &mut V, duration: &Duration) {
333
90
    visitor.visit_u64(&duration.value);
334
90
    if let Some(unit) = duration.unit {
335
80
        visitor.visit_duration_unit(unit);
336
    }
337
}
338

            
339
122
pub fn walk_duration_option<V: Visitor>(visitor: &mut V, option: &DurationOption) {
340
122
    match option {
341
90
        DurationOption::Literal(value) => visitor.visit_duration(value),
342
32
        DurationOption::Placeholder(value) => visitor.visit_placeholder(value),
343
    }
344
}
345

            
346
1938
pub fn walk_entry<V: Visitor>(visitor: &mut V, entry: &Entry) {
347
1938
    visitor.visit_request(&entry.request);
348
1938
    if let Some(ref response) = entry.response {
349
1564
        visitor.visit_response(response);
350
    }
351
}
352

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

            
414
100
pub fn walk_file<V: Visitor>(visitor: &mut V, file: &File) {
415
100
    visitor.visit_literal("file,");
416
100
    visitor.visit_whitespace(&file.space0);
417
100
    visitor.visit_filename(&file.filename);
418
100
    visitor.visit_whitespace(&file.space1);
419
100
    visitor.visit_literal(";");
420
}
421

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

            
520
51
pub fn walk_filename_param<V: Visitor>(visitor: &mut V, param: &FilenameParam) {
521
51
    param.line_terminators.iter().for_each(|lt| {
522
        visitor.visit_lt(lt);
523
    });
524
51
    visitor.visit_whitespace(&param.space0);
525
51
    visitor.visit_template(&param.key);
526
51
    visitor.visit_whitespace(&param.space1);
527
51
    visitor.visit_literal(":");
528
51
    visitor.visit_whitespace(&param.space2);
529
51
    visitor.visit_filename_value(&param.value);
530
51
    visitor.visit_lt(&param.line_terminator0);
531
}
532

            
533
51
pub fn walk_filename_value<V: Visitor>(visitor: &mut V, value: &FilenameValue) {
534
51
    visitor.visit_literal("file,");
535
51
    visitor.visit_whitespace(&value.space0);
536
51
    visitor.visit_filename(&value.filename);
537
51
    visitor.visit_whitespace(&value.space1);
538
51
    visitor.visit_literal(";");
539
51
    visitor.visit_whitespace(&value.space2);
540
51
    if let Some(content_type) = &value.content_type {
541
18
        visitor.visit_template(content_type);
542
    }
543
}
544

            
545
pub fn walk_header<V: Visitor>(visitor: &mut V, header: &KeyValue) {
546
    visitor.visit_kv(header);
547
}
548

            
549
381
pub fn walk_hex<V: Visitor>(visitor: &mut V, hex: &Hex) {
550
381
    visitor.visit_literal("hex,");
551
381
    visitor.visit_whitespace(&hex.space0);
552
381
    visitor.visit_hex_value(&hex.value, &hex.source);
553
381
    visitor.visit_whitespace(&hex.space1);
554
381
    visitor.visit_literal(";");
555
}
556

            
557
667
pub fn walk_hurl_file<V: Visitor>(visitor: &mut V, file: &HurlFile) {
558
1938
    file.entries.iter().for_each(|e| visitor.visit_entry(e));
559
667
    file.line_terminators.iter().for_each(|lt| {
560
309
        visitor.visit_lt(lt);
561
309
    });
562
}
563

            
564
168
pub fn walk_integer_value<V: Visitor>(visitor: &mut V, n: &IntegerValue) {
565
168
    match n {
566
158
        IntegerValue::Literal(value) => visitor.visit_i64(value.as_i64()),
567
10
        IntegerValue::Placeholder(value) => visitor.visit_placeholder(value),
568
    }
569
}
570

            
571
1062
pub fn walk_kv<V: Visitor>(visitor: &mut V, kv: &KeyValue) {
572
1062
    kv.line_terminators.iter().for_each(|lt| {
573
28
        visitor.visit_lt(lt);
574
28
    });
575
1062
    visitor.visit_whitespace(&kv.space0);
576
1062
    visitor.visit_template(&kv.key);
577
1062
    visitor.visit_whitespace(&kv.space1);
578
1062
    visitor.visit_literal(":");
579
1062
    visitor.visit_whitespace(&kv.space2);
580
1062
    visitor.visit_template(&kv.value);
581
1062
    visitor.visit_lt(&kv.line_terminator0);
582
}
583

            
584
16435
pub fn walk_lt<V: Visitor>(visitor: &mut V, lt: &LineTerminator) {
585
16435
    visitor.visit_whitespace(&lt.space0);
586
16435
    if let Some(ref comment) = lt.comment {
587
2118
        visitor.visit_comment(comment);
588
    }
589
16435
    visitor.visit_whitespace(&lt.newline);
590
}
591

            
592
16
pub fn walk_natural_option<V: Visitor>(visitor: &mut V, option: &NaturalOption) {
593
16
    match option {
594
8
        NaturalOption::Literal(value) => visitor.visit_u64(value),
595
8
        NaturalOption::Placeholder(value) => visitor.visit_placeholder(value),
596
    }
597
}
598

            
599
4522
pub fn walk_query<V: Visitor>(visitor: &mut V, query: &Query) {
600
4522
    visitor.visit_query_kind(&query.value);
601

            
602
4522
    match &query.value {
603
362
        QueryValue::Header { space0, name } => {
604
362
            visitor.visit_whitespace(space0);
605
362
            visitor.visit_template(name);
606
        }
607
89
        QueryValue::Cookie { space0, expr } => {
608
89
            visitor.visit_whitespace(space0);
609
89
            visitor.visit_cookie_path(expr);
610
        }
611
148
        QueryValue::Xpath { space0, expr } => {
612
148
            visitor.visit_whitespace(space0);
613
148
            visitor.visit_template(expr);
614
        }
615
2418
        QueryValue::Jsonpath { space0, expr } => {
616
2418
            visitor.visit_whitespace(space0);
617
2418
            visitor.visit_template(expr);
618
        }
619
43
        QueryValue::Regex { space0, value } => {
620
43
            visitor.visit_whitespace(space0);
621
43
            match value {
622
23
                RegexValue::Template(t) => visitor.visit_template(t),
623
20
                RegexValue::Regex(r) => visitor.visit_regex(r),
624
            }
625
        }
626
249
        QueryValue::Variable { space0, name } => {
627
249
            visitor.visit_whitespace(space0);
628
249
            visitor.visit_template(name);
629
        }
630
        QueryValue::Certificate {
631
88
            space0,
632
88
            attribute_name,
633
88
        } => {
634
88
            visitor.visit_whitespace(space0);
635
88
            visitor.visit_string(attribute_name.to_source().as_str());
636
        }
637
        QueryValue::Body
638
        | QueryValue::Status
639
        | QueryValue::Url
640
        | QueryValue::Duration
641
        | QueryValue::Bytes
642
        | QueryValue::RawBytes
643
        | QueryValue::Sha256
644
        | QueryValue::Md5
645
        | QueryValue::Version
646
        | QueryValue::Ip
647
1125
        | QueryValue::Redirects => {}
648
    }
649
}
650

            
651
4214
pub fn walk_predicate<V: Visitor>(visitor: &mut V, pred: &Predicate) {
652
4214
    if pred.not {
653
293
        visitor.visit_not("not");
654
293
        visitor.visit_whitespace(&pred.space0);
655
    }
656
4214
    let kind = &pred.predicate_func.value;
657
4214
    visitor.visit_predicate_kind(kind);
658
4214
    match kind {
659
2712
        PredicateFuncValue::Equal { space0, value } => {
660
2712
            visitor.visit_whitespace(space0);
661
2712
            visitor.visit_predicate_value(value);
662
        }
663
84
        PredicateFuncValue::NotEqual { space0, value } => {
664
84
            visitor.visit_whitespace(space0);
665
84
            visitor.visit_predicate_value(value);
666
        }
667
84
        PredicateFuncValue::GreaterThan { space0, value } => {
668
84
            visitor.visit_whitespace(space0);
669
84
            visitor.visit_predicate_value(value);
670
        }
671
23
        PredicateFuncValue::GreaterThanOrEqual { space0, value } => {
672
23
            visitor.visit_whitespace(space0);
673
23
            visitor.visit_predicate_value(value);
674
        }
675
67
        PredicateFuncValue::LessThan { space0, value } => {
676
67
            visitor.visit_whitespace(space0);
677
67
            visitor.visit_predicate_value(value);
678
        }
679
33
        PredicateFuncValue::LessThanOrEqual { space0, value } => {
680
33
            visitor.visit_whitespace(space0);
681
33
            visitor.visit_predicate_value(value);
682
        }
683
179
        PredicateFuncValue::StartWith { space0, value } => {
684
179
            visitor.visit_whitespace(space0);
685
179
            visitor.visit_predicate_value(value);
686
        }
687
46
        PredicateFuncValue::EndWith { space0, value } => {
688
46
            visitor.visit_whitespace(space0);
689
46
            visitor.visit_predicate_value(value);
690
        }
691
144
        PredicateFuncValue::Contain { space0, value } => {
692
144
            visitor.visit_whitespace(space0);
693
144
            visitor.visit_predicate_value(value);
694
        }
695
8
        PredicateFuncValue::Include { space0, value } => {
696
8
            visitor.visit_whitespace(space0);
697
8
            visitor.visit_predicate_value(value);
698
        }
699
124
        PredicateFuncValue::Match { space0, value } => {
700
124
            visitor.visit_whitespace(space0);
701
124
            visitor.visit_predicate_value(value);
702
        }
703
        PredicateFuncValue::Exist
704
        | PredicateFuncValue::IsBoolean
705
        | PredicateFuncValue::IsCollection
706
        | PredicateFuncValue::IsDate
707
        | PredicateFuncValue::IsEmpty
708
        | PredicateFuncValue::IsFloat
709
        | PredicateFuncValue::IsInteger
710
        | PredicateFuncValue::IsIpv4
711
        | PredicateFuncValue::IsIpv6
712
        | PredicateFuncValue::IsIsoDate
713
        | PredicateFuncValue::IsList
714
        | PredicateFuncValue::IsNumber
715
        | PredicateFuncValue::IsObject
716
        | PredicateFuncValue::IsString
717
710
        | PredicateFuncValue::IsUuid => {}
718
    }
719
}
720

            
721
3504
pub fn walk_predicate_value<V: Visitor>(visitor: &mut V, pred_value: &PredicateValue) {
722
3504
    match pred_value {
723
13
        PredicateValue::Base64(value) => visitor.visit_base64(value),
724
63
        PredicateValue::Bool(value) => visitor.visit_bool(*value),
725
18
        PredicateValue::File(value) => visitor.visit_file(value),
726
342
        PredicateValue::Hex(value) => visitor.visit_hex(value),
727
68
        PredicateValue::MultilineString(value) => visitor.visit_multiline_string(value),
728
28
        PredicateValue::Null => visitor.visit_null("null"),
729
1103
        PredicateValue::Number(value) => visitor.visit_number(value),
730
133
        PredicateValue::Placeholder(placeholder) => visitor.visit_placeholder(placeholder),
731
86
        PredicateValue::Regex(value) => visitor.visit_regex(value),
732
1650
        PredicateValue::String(value) => visitor.visit_template(value),
733
    }
734
}
735

            
736
1938
pub fn walk_request<V: Visitor>(visitor: &mut V, request: &Request) {
737
2837
    request.line_terminators.iter().for_each(|lt| {
738
2809
        visitor.visit_lt(lt);
739
2809
    });
740
1938
    visitor.visit_whitespace(&request.space0);
741
1938
    visitor.visit_method(&request.method);
742
1938
    visitor.visit_whitespace(&request.space1);
743
1938
    visitor.visit_url(&request.url);
744
1938
    visitor.visit_lt(&request.line_terminator0);
745
1938
    request.headers.iter().for_each(|h| visitor.visit_kv(h));
746
1938
    request
747
1938
        .sections
748
1938
        .iter()
749
1938
        .for_each(|s| visitor.visit_section(s));
750
1938
    if let Some(body) = &request.body {
751
305
        visitor.visit_body(body);
752
    }
753
}
754

            
755
1564
pub fn walk_response<V: Visitor>(visitor: &mut V, response: &Response) {
756
1564
    response.line_terminators.iter().for_each(|lt| {
757
33
        visitor.visit_lt(lt);
758
33
    });
759
1564
    visitor.visit_whitespace(&response.space0);
760
1564
    visitor.visit_version(&response.version.value);
761
1564
    visitor.visit_whitespace(&response.space1);
762
1564
    visitor.visit_status(&response.status.value);
763
1564
    visitor.visit_lt(&response.line_terminator0);
764
1564
    response.headers.iter().for_each(|h| visitor.visit_kv(h));
765
1564
    response
766
1564
        .sections
767
1564
        .iter()
768
1564
        .for_each(|s| visitor.visit_section(s));
769
1564
    if let Some(body) = &response.body {
770
498
        visitor.visit_body(body);
771
    }
772
}
773

            
774
1513
pub fn walk_section<V: Visitor>(visitor: &mut V, section: &Section) {
775
1513
    section.line_terminators.iter().for_each(|lt| {
776
191
        visitor.visit_lt(lt);
777
191
    });
778
1513
    visitor.visit_whitespace(&section.space0);
779
1513
    let name = format!("[{}]", section.identifier());
780
1513
    visitor.visit_section_header(&name);
781
1513
    visitor.visit_lt(&section.line_terminator0);
782
1513
    visitor.visit_section_value(&section.value);
783
}
784

            
785
1513
pub fn walk_section_value<V: Visitor>(visitor: &mut V, section_value: &SectionValue) {
786
18
    match section_value {
787
4214
        SectionValue::Asserts(asserts) => asserts.iter().for_each(|a| visitor.visit_assert(a)),
788
18
        SectionValue::BasicAuth(Some(auth)) => visitor.visit_kv(auth),
789
        SectionValue::BasicAuth(_) => {}
790
308
        SectionValue::Captures(captures) => captures.iter().for_each(|c| visitor.visit_capture(c)),
791
69
        SectionValue::Cookies(cookies) => cookies.iter().for_each(|c| visitor.visit_cookie(c)),
792
102
        SectionValue::FormParams(params, _) => params.iter().for_each(|p| visitor.visit_kv(p)),
793
74
        SectionValue::MultipartFormData(params, _) => params.iter().for_each(|p| match p {
794
23
            MultipartParam::Param(param) => visitor.visit_kv(param),
795
51
            MultipartParam::FilenameParam(param) => visitor.visit_filename_param(param),
796
74
        }),
797
397
        SectionValue::Options(options) => {
798
1337
            options.iter().for_each(|o| visitor.visit_entry_option(o));
799
        }
800
203
        SectionValue::QueryParams(params, _) => params.iter().for_each(|p| visitor.visit_kv(p)),
801
    }
802
}
803

            
804
147
pub fn walk_variable_def<V: Visitor>(visitor: &mut V, def: &VariableDefinition) {
805
147
    visitor.visit_variable_name(&def.name);
806
147
    visitor.visit_whitespace(&def.space0);
807
147
    visitor.visit_literal("=");
808
147
    visitor.visit_whitespace(&def.space1);
809
147
    visitor.visit_variable_value(&def.value);
810
}
811

            
812
147
pub fn walk_variable_value<V: Visitor>(visitor: &mut V, value: &VariableValue) {
813
147
    match value {
814
8
        VariableValue::Null => visitor.visit_null("null"),
815
8
        VariableValue::Bool(value) => visitor.visit_bool(*value),
816
56
        VariableValue::Number(value) => visitor.visit_number(value),
817
75
        VariableValue::String(value) => visitor.visit_template(value),
818
    }
819
}
820

            
821
16
pub fn walk_verbosity_option<V: Visitor>(visitor: &mut V, value: &VerbosityOption) {
822
16
    visitor.visit_string(value.identifier());
823
}
824

            
825
#[cfg(test)]
826
mod tests {
827
    use crate::ast::Assert;
828
    use crate::ast::visit::Visitor;
829
    use crate::parser;
830

            
831
    #[test]
832
    fn test_walk_assert() {
833
        struct AssertWalker {
834
            count: usize,
835
        }
836

            
837
        impl Visitor for AssertWalker {
838
            fn visit_assert(&mut self, _assert: &Assert) {
839
                self.count += 1;
840
            }
841
        }
842

            
843
        let mut walker = AssertWalker { count: 0 };
844
        let content = r#"
845
GET https://foo.com
846
HTTP 200
847
[Asserts]
848
jsonpath "$.toto[0]" == "tata"
849
jsonpath "$.toto[1]" == "toto"
850
jsonpath "$.toto[2]" == "titi"
851
jsonpath "$.toto[3]" == "tata"
852
jsonpath "$.toto[4]" == "tutu"
853

            
854
GET https://foo.com
855
HTTP 200
856
[Asserts]
857
status == 200
858
header "Location" not exists
859
"#;
860
        let file = parser::parse_hurl_file(content).unwrap();
861
        walker.visit_hurl_file(&file);
862
        assert_eq!(walker.count, 7);
863
    }
864
}