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, JsonValue, KeyValue, LineTerminator, Method, MultilineString, MultipartParam,
27
    NaturalOption, Number, OptionKind, Placeholder, Predicate, PredicateFuncValue, PredicateValue,
28
    Query, QueryValue, Regex, RegexValue, Request, Response, Section, SectionValue, StatusValue,
29
    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
3535
    fn visit_assert(&mut self, assert: &Assert) {
39
3535
        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
739
    fn visit_body(&mut self, body: &Body) {
49
739
        walk_body(self, body);
50
    }
51

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

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

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

            
62
24
    fn visit_cookie(&mut self, cookie: &Cookie) {
63
24
        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
76
    fn visit_count(&mut self, count: Count) {
71
76
        walk_count(self, count);
72
    }
73

            
74
105
    fn visit_count_option(&mut self, option: &CountOption) {
75
105
        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
1136
    fn visit_entry_option(&mut self, option: &EntryOption) {
93
1136
        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
1201
    fn visit_filter(&mut self, filter: &Filter) {
111
1201
        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
84
    fn visit_hurl_file(&mut self, file: &HurlFile) {
127
84
        walk_hurl_file(self, file);
128
    }
129

            
130
    fn visit_i64(&mut self, n: i64) {}
131

            
132
    fn visit_json_body(&mut self, json: &JsonValue) {}
133

            
134
1011
    fn visit_kv(&mut self, kv: &KeyValue) {
135
1011
        walk_kv(self, kv);
136
    }
137

            
138
14845
    fn visit_lt(&mut self, lt: &LineTerminator) {
139
14845
        walk_lt(self, lt);
140
    }
141

            
142
    fn visit_literal(&mut self, lit: &'static str) {}
143

            
144
    fn visit_method(&mut self, method: &Method) {}
145

            
146
    fn visit_multiline_string(&mut self, string: &MultilineString) {}
147

            
148
16
    fn visit_natural_option(&mut self, option: &NaturalOption) {
149
16
        walk_natural_option(self, option);
150
    }
151

            
152
    fn visit_not(&mut self, identifier: &'static str) {}
153

            
154
    fn visit_null(&mut self, identifier: &'static str) {}
155

            
156
    fn visit_number(&mut self, number: &Number) {}
157

            
158
    fn visit_placeholder(&mut self, placeholder: &Placeholder) {}
159

            
160
3535
    fn visit_predicate(&mut self, predicate: &Predicate) {
161
3535
        walk_predicate(self, predicate);
162
    }
163

            
164
    fn visit_predicate_kind(&mut self, kind: &PredicateFuncValue) {}
165

            
166
2979
    fn visit_predicate_value(&mut self, value: &PredicateValue) {
167
2979
        walk_predicate_value(self, value);
168
    }
169

            
170
3823
    fn visit_query(&mut self, query: &Query) {
171
3823
        walk_query(self, query);
172
    }
173

            
174
    fn visit_query_kind(&mut self, kind: &QueryValue) {}
175

            
176
270
    fn visit_request(&mut self, request: &Request) {
177
270
        walk_request(self, request);
178
    }
179

            
180
114
    fn visit_response(&mut self, response: &Response) {
181
114
        walk_response(self, response);
182
    }
183

            
184
    fn visit_regex(&mut self, regex: &Regex) {}
185

            
186
1351
    fn visit_section(&mut self, section: &Section) {
187
1351
        walk_section(self, section);
188
    }
189

            
190
    fn visit_status(&mut self, value: &StatusValue) {}
191

            
192
    fn visit_string(&mut self, value: &str) {}
193

            
194
    fn visit_section_header(&mut self, name: &str) {}
195

            
196
1351
    fn visit_section_value(&mut self, section_value: &SectionValue) {
197
1351
        walk_section_value(self, section_value);
198
    }
199

            
200
    fn visit_template(&mut self, template: &Template) {}
201

            
202
    fn visit_url(&mut self, url: &Template) {}
203

            
204
    fn visit_u64(&mut self, n: &U64) {}
205

            
206
    fn visit_usize(&mut self, n: usize) {}
207

            
208
127
    fn visit_variable_def(&mut self, def: &VariableDefinition) {
209
127
        walk_variable_def(self, def);
210
    }
211

            
212
    fn visit_variable_name(&mut self, name: &str) {}
213

            
214
127
    fn visit_variable_value(&mut self, value: &VariableValue) {
215
127
        walk_variable_value(self, value);
216
    }
217

            
218
    fn visit_version(&mut self, value: &VersionValue) {}
219

            
220
    fn visit_xml_body(&mut self, xml: &str) {}
221

            
222
    fn visit_whitespace(&mut self, ws: &Whitespace) {}
223
}
224

            
225
3535
pub fn walk_assert<V: Visitor>(visitor: &mut V, assert: &Assert) {
226
3535
    assert.line_terminators.iter().for_each(|lt| {
227
45
        visitor.visit_lt(lt);
228
3535
    });
229
3535
    visitor.visit_whitespace(&assert.space0);
230
3535
    visitor.visit_query(&assert.query);
231
3535
    for (space, filter) in assert.filters.iter() {
232
1148
        visitor.visit_whitespace(space);
233
1148
        visitor.visit_filter(filter);
234
    }
235
3535
    visitor.visit_whitespace(&assert.space1);
236
3535
    visitor.visit_predicate(&assert.predicate);
237
3535
    visitor.visit_lt(&assert.line_terminator0);
238
}
239

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

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

            
265
546
pub fn walk_bool_option<V: Visitor>(visitor: &mut V, option: &BooleanOption) {
266
546
    match option {
267
413
        BooleanOption::Literal(value) => visitor.visit_bool(*value),
268
133
        BooleanOption::Placeholder(value) => visitor.visit_placeholder(value),
269
    }
270
}
271

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

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

            
309
76
pub fn walk_count<V: Visitor>(visitor: &mut V, count: Count) {
310
76
    match count {
311
60
        Count::Finite(count) => visitor.visit_usize(count),
312
16
        Count::Infinite => visitor.visit_i64(-1),
313
    }
314
}
315

            
316
105
pub fn walk_count_option<V: Visitor>(visitor: &mut V, option: &CountOption) {
317
105
    match option {
318
76
        CountOption::Literal(value) => visitor.visit_count(*value),
319
29
        CountOption::Placeholder(value) => visitor.visit_placeholder(value),
320
    }
321
}
322

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

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

            
337
1880
pub fn walk_entry<V: Visitor>(visitor: &mut V, entry: &Entry) {
338
1880
    visitor.visit_request(&entry.request);
339
1880
    if let Some(ref response) = entry.response {
340
1504
        visitor.visit_response(response);
341
    }
342
}
343

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

            
396
100
pub fn walk_file<V: Visitor>(visitor: &mut V, file: &File) {
397
100
    visitor.visit_literal("file,");
398
100
    visitor.visit_whitespace(&file.space0);
399
100
    visitor.visit_filename(&file.filename);
400
100
    visitor.visit_whitespace(&file.space1);
401
100
    visitor.visit_literal(";");
402
}
403

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

            
492
51
pub fn walk_filename_param<V: Visitor>(visitor: &mut V, param: &FilenameParam) {
493
51
    param.line_terminators.iter().for_each(|lt| {
494
        visitor.visit_lt(lt);
495
51
    });
496
51
    visitor.visit_whitespace(&param.space0);
497
51
    visitor.visit_template(&param.key);
498
51
    visitor.visit_whitespace(&param.space1);
499
51
    visitor.visit_literal(":");
500
51
    visitor.visit_whitespace(&param.space2);
501
51
    visitor.visit_filename_value(&param.value);
502
51
    visitor.visit_lt(&param.line_terminator0);
503
}
504

            
505
51
pub fn walk_filename_value<V: Visitor>(visitor: &mut V, value: &FilenameValue) {
506
51
    visitor.visit_literal("file,");
507
51
    visitor.visit_whitespace(&value.space0);
508
51
    visitor.visit_filename(&value.filename);
509
51
    visitor.visit_whitespace(&value.space1);
510
51
    visitor.visit_literal(";");
511
51
    visitor.visit_whitespace(&value.space2);
512
51
    if let Some(content_type) = &value.content_type {
513
18
        visitor.visit_template(content_type);
514
    }
515
}
516

            
517
pub fn walk_header<V: Visitor>(visitor: &mut V, header: &KeyValue) {
518
    visitor.visit_kv(header);
519
}
520

            
521
346
pub fn walk_hex<V: Visitor>(visitor: &mut V, hex: &Hex) {
522
346
    visitor.visit_literal("hex,");
523
346
    visitor.visit_whitespace(&hex.space0);
524
346
    visitor.visit_hex_value(&hex.value, &hex.source);
525
346
    visitor.visit_whitespace(&hex.space1);
526
346
    visitor.visit_literal(";");
527
}
528

            
529
674
pub fn walk_hurl_file<V: Visitor>(visitor: &mut V, file: &HurlFile) {
530
1880
    file.entries.iter().for_each(|e| visitor.visit_entry(e));
531
674
    file.line_terminators.iter().for_each(|lt| {
532
299
        visitor.visit_lt(lt);
533
674
    });
534
}
535

            
536
1011
pub fn walk_kv<V: Visitor>(visitor: &mut V, kv: &KeyValue) {
537
1011
    kv.line_terminators.iter().for_each(|lt| {
538
18
        visitor.visit_lt(lt);
539
1011
    });
540
1011
    visitor.visit_whitespace(&kv.space0);
541
1011
    visitor.visit_template(&kv.key);
542
1011
    visitor.visit_whitespace(&kv.space1);
543
1011
    visitor.visit_literal(":");
544
1011
    visitor.visit_whitespace(&kv.space2);
545
1011
    visitor.visit_template(&kv.value);
546
1011
    visitor.visit_lt(&kv.line_terminator0);
547
}
548

            
549
14845
pub fn walk_lt<V: Visitor>(visitor: &mut V, lt: &LineTerminator) {
550
14845
    visitor.visit_whitespace(&lt.space0);
551
14845
    if let Some(ref comment) = lt.comment {
552
1853
        visitor.visit_comment(comment);
553
    }
554
14845
    visitor.visit_whitespace(&lt.newline);
555
}
556

            
557
16
pub fn walk_natural_option<V: Visitor>(visitor: &mut V, option: &NaturalOption) {
558
16
    match option {
559
8
        NaturalOption::Literal(value) => visitor.visit_u64(value),
560
8
        NaturalOption::Placeholder(value) => visitor.visit_placeholder(value),
561
    }
562
}
563

            
564
3823
pub fn walk_query<V: Visitor>(visitor: &mut V, query: &Query) {
565
3823
    visitor.visit_query_kind(&query.value);
566
3823

            
567
3823
    match &query.value {
568
327
        QueryValue::Header { space0, name } => {
569
327
            visitor.visit_whitespace(space0);
570
327
            visitor.visit_template(name);
571
        }
572
81
        QueryValue::Cookie { space0, expr } => {
573
81
            visitor.visit_whitespace(space0);
574
81
            visitor.visit_cookie_path(expr);
575
        }
576
148
        QueryValue::Xpath { space0, expr } => {
577
148
            visitor.visit_whitespace(space0);
578
148
            visitor.visit_template(expr);
579
        }
580
2123
        QueryValue::Jsonpath { space0, expr } => {
581
2123
            visitor.visit_whitespace(space0);
582
2123
            visitor.visit_template(expr);
583
        }
584
33
        QueryValue::Regex { space0, value } => {
585
33
            visitor.visit_whitespace(space0);
586
33
            match value {
587
23
                RegexValue::Template(t) => visitor.visit_template(t),
588
10
                RegexValue::Regex(r) => visitor.visit_regex(r),
589
            }
590
        }
591
234
        QueryValue::Variable { space0, name } => {
592
234
            visitor.visit_whitespace(space0);
593
234
            visitor.visit_template(name);
594
        }
595
        QueryValue::Certificate {
596
80
            space0,
597
80
            attribute_name,
598
80
        } => {
599
80
            visitor.visit_whitespace(space0);
600
80
            visitor.visit_string(attribute_name.to_source().as_str());
601
        }
602
        QueryValue::Body
603
        | QueryValue::Status
604
        | QueryValue::Url
605
        | QueryValue::Duration
606
        | QueryValue::Bytes
607
        | QueryValue::Sha256
608
        | QueryValue::Md5
609
        | QueryValue::Version
610
        | QueryValue::Ip
611
797
        | QueryValue::Redirects => {}
612
    }
613
}
614

            
615
3535
pub fn walk_predicate<V: Visitor>(visitor: &mut V, pred: &Predicate) {
616
3535
    if pred.not {
617
203
        visitor.visit_not("not");
618
203
        visitor.visit_whitespace(&pred.space0);
619
    }
620
3535
    let kind = &pred.predicate_func.value;
621
3535
    visitor.visit_predicate_kind(kind);
622
3535
    match kind {
623
2252
        PredicateFuncValue::Equal { space0, value } => {
624
2252
            visitor.visit_whitespace(space0);
625
2252
            visitor.visit_predicate_value(value);
626
        }
627
69
        PredicateFuncValue::NotEqual { space0, value } => {
628
69
            visitor.visit_whitespace(space0);
629
69
            visitor.visit_predicate_value(value);
630
        }
631
84
        PredicateFuncValue::GreaterThan { space0, value } => {
632
84
            visitor.visit_whitespace(space0);
633
84
            visitor.visit_predicate_value(value);
634
        }
635
23
        PredicateFuncValue::GreaterThanOrEqual { space0, value } => {
636
23
            visitor.visit_whitespace(space0);
637
23
            visitor.visit_predicate_value(value);
638
        }
639
67
        PredicateFuncValue::LessThan { space0, value } => {
640
67
            visitor.visit_whitespace(space0);
641
67
            visitor.visit_predicate_value(value);
642
        }
643
33
        PredicateFuncValue::LessThanOrEqual { space0, value } => {
644
33
            visitor.visit_whitespace(space0);
645
33
            visitor.visit_predicate_value(value);
646
        }
647
144
        PredicateFuncValue::StartWith { space0, value } => {
648
144
            visitor.visit_whitespace(space0);
649
144
            visitor.visit_predicate_value(value);
650
        }
651
46
        PredicateFuncValue::EndWith { space0, value } => {
652
46
            visitor.visit_whitespace(space0);
653
46
            visitor.visit_predicate_value(value);
654
        }
655
144
        PredicateFuncValue::Contain { space0, value } => {
656
144
            visitor.visit_whitespace(space0);
657
144
            visitor.visit_predicate_value(value);
658
        }
659
8
        PredicateFuncValue::Include { space0, value } => {
660
8
            visitor.visit_whitespace(space0);
661
8
            visitor.visit_predicate_value(value);
662
        }
663
109
        PredicateFuncValue::Match { space0, value } => {
664
109
            visitor.visit_whitespace(space0);
665
109
            visitor.visit_predicate_value(value);
666
        }
667
        PredicateFuncValue::IsInteger
668
        | PredicateFuncValue::IsFloat
669
        | PredicateFuncValue::IsBoolean
670
        | PredicateFuncValue::IsString
671
        | PredicateFuncValue::IsCollection
672
        | PredicateFuncValue::IsDate
673
        | PredicateFuncValue::IsIsoDate
674
        | PredicateFuncValue::Exist
675
        | PredicateFuncValue::IsEmpty
676
        | PredicateFuncValue::IsNumber
677
        | PredicateFuncValue::IsIpv4
678
556
        | PredicateFuncValue::IsIpv6 => {}
679
    }
680
}
681

            
682
2979
pub fn walk_predicate_value<V: Visitor>(visitor: &mut V, pred_value: &PredicateValue) {
683
2979
    match pred_value {
684
13
        PredicateValue::Base64(value) => visitor.visit_base64(value),
685
63
        PredicateValue::Bool(value) => visitor.visit_bool(*value),
686
18
        PredicateValue::File(value) => visitor.visit_file(value),
687
307
        PredicateValue::Hex(value) => visitor.visit_hex(value),
688
68
        PredicateValue::MultilineString(value) => visitor.visit_multiline_string(value),
689
28
        PredicateValue::Null => visitor.visit_null("null"),
690
883
        PredicateValue::Number(value) => visitor.visit_number(value),
691
133
        PredicateValue::Placeholder(placeholder) => visitor.visit_placeholder(placeholder),
692
71
        PredicateValue::Regex(value) => visitor.visit_regex(value),
693
1395
        PredicateValue::String(value) => visitor.visit_template(value),
694
    }
695
}
696

            
697
1880
pub fn walk_request<V: Visitor>(visitor: &mut V, request: &Request) {
698
2690
    request.line_terminators.iter().for_each(|lt| {
699
2660
        visitor.visit_lt(lt);
700
2690
    });
701
1880
    visitor.visit_whitespace(&request.space0);
702
1880
    visitor.visit_method(&request.method);
703
1880
    visitor.visit_whitespace(&request.space1);
704
1880
    visitor.visit_url(&request.url);
705
1880
    visitor.visit_lt(&request.line_terminator0);
706
1880
    request.headers.iter().for_each(|h| visitor.visit_kv(h));
707
1880
    request
708
1880
        .sections
709
1880
        .iter()
710
1880
        .for_each(|s| visitor.visit_section(s));
711
1880
    if let Some(body) = &request.body {
712
284
        visitor.visit_body(body);
713
    }
714
}
715

            
716
1504
pub fn walk_response<V: Visitor>(visitor: &mut V, response: &Response) {
717
1504
    response.line_terminators.iter().for_each(|lt| {
718
41
        visitor.visit_lt(lt);
719
1504
    });
720
1504
    visitor.visit_whitespace(&response.space0);
721
1504
    visitor.visit_version(&response.version.value);
722
1504
    visitor.visit_whitespace(&response.space1);
723
1504
    visitor.visit_status(&response.status.value);
724
1504
    visitor.visit_lt(&response.line_terminator0);
725
1504
    response.headers.iter().for_each(|h| visitor.visit_kv(h));
726
1504
    response
727
1504
        .sections
728
1504
        .iter()
729
1504
        .for_each(|s| visitor.visit_section(s));
730
1504
    if let Some(body) = &response.body {
731
455
        visitor.visit_body(body);
732
    }
733
}
734

            
735
1351
pub fn walk_section<V: Visitor>(visitor: &mut V, section: &Section) {
736
1351
    section.line_terminators.iter().for_each(|lt| {
737
150
        visitor.visit_lt(lt);
738
1351
    });
739
1351
    visitor.visit_whitespace(&section.space0);
740
1351
    let name = format!("[{}]", section.identifier());
741
1351
    visitor.visit_section_header(&name);
742
1351
    visitor.visit_lt(&section.line_terminator0);
743
1351
    visitor.visit_section_value(&section.value);
744
}
745

            
746
1351
pub fn walk_section_value<V: Visitor>(visitor: &mut V, section_value: &SectionValue) {
747
18
    match section_value {
748
3535
        SectionValue::Asserts(asserts) => asserts.iter().for_each(|a| visitor.visit_assert(a)),
749
18
        SectionValue::BasicAuth(Some(auth)) => visitor.visit_kv(auth),
750
        SectionValue::BasicAuth(_) => {}
751
288
        SectionValue::Captures(captures) => captures.iter().for_each(|c| visitor.visit_capture(c)),
752
24
        SectionValue::Cookies(cookies) => cookies.iter().for_each(|c| visitor.visit_cookie(c)),
753
92
        SectionValue::FormParams(params, _) => params.iter().for_each(|p| visitor.visit_kv(p)),
754
74
        SectionValue::MultipartFormData(params, _) => params.iter().for_each(|p| match p {
755
23
            MultipartParam::Param(param) => visitor.visit_kv(param),
756
51
            MultipartParam::FilenameParam(param) => visitor.visit_filename_param(param),
757
74
        }),
758
355
        SectionValue::Options(options) => {
759
1136
            options.iter().for_each(|o| visitor.visit_entry_option(o));
760
        }
761
193
        SectionValue::QueryParams(params, _) => params.iter().for_each(|p| visitor.visit_kv(p)),
762
    }
763
}
764

            
765
127
pub fn walk_variable_def<V: Visitor>(visitor: &mut V, def: &VariableDefinition) {
766
127
    visitor.visit_variable_name(&def.name);
767
127
    visitor.visit_whitespace(&def.space0);
768
127
    visitor.visit_literal("=");
769
127
    visitor.visit_whitespace(&def.space1);
770
127
    visitor.visit_variable_value(&def.value);
771
}
772

            
773
127
pub fn walk_variable_value<V: Visitor>(visitor: &mut V, value: &VariableValue) {
774
127
    match value {
775
8
        VariableValue::Null => visitor.visit_null("null"),
776
8
        VariableValue::Bool(value) => visitor.visit_bool(*value),
777
46
        VariableValue::Number(value) => visitor.visit_number(value),
778
65
        VariableValue::String(value) => visitor.visit_template(value),
779
    }
780
}
781

            
782
#[cfg(test)]
783
mod tests {
784
    use crate::ast::visit::Visitor;
785
    use crate::ast::Assert;
786
    use crate::parser;
787

            
788
    #[test]
789
    fn test_walk_assert() {
790
        struct AssertWalker {
791
            count: usize,
792
        }
793

            
794
        impl Visitor for AssertWalker {
795
            fn visit_assert(&mut self, _assert: &Assert) {
796
                self.count += 1;
797
            }
798
        }
799

            
800
        let mut walker = AssertWalker { count: 0 };
801
        let content = r#"
802
GET https://foo.com
803
HTTP 200
804
[Asserts]
805
jsonpath "$.toto[0]" == "tata"
806
jsonpath "$.toto[1]" == "toto"
807
jsonpath "$.toto[2]" == "titi"
808
jsonpath "$.toto[3]" == "tata"
809
jsonpath "$.toto[4]" == "tutu"
810

            
811
GET https://foo.com
812
HTTP 200
813
[Asserts]
814
status == 200
815
header "Location" not exists
816
"#;
817
        let file = parser::parse_hurl_file(content).unwrap();
818
        walker.visit_hurl_file(&file);
819
        assert_eq!(walker.count, 7);
820
    }
821
}