1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2025 Orange
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *          http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
use base64::engine::general_purpose;
19
use base64::Engine;
20
use hurl_core::ast::{
21
    Assert, Base64, Body, BooleanOption, Bytes, Capture, CertificateAttributeName, Comment, Cookie,
22
    CountOption, DurationOption, Entry, EntryOption, File, FilenameParam, Filter, FilterValue, Hex,
23
    HurlFile, JsonListElement, JsonValue, KeyValue, MultilineString, MultilineStringKind,
24
    MultipartParam, NaturalOption, OptionKind, Placeholder, Predicate, PredicateFuncValue,
25
    PredicateValue, Query, QueryValue, Regex, RegexValue, Request, Response, StatusValue,
26
    VersionValue,
27
};
28
use hurl_core::typing::{Count, Duration, ToSource};
29

            
30
use crate::format::serialize_json::JValue;
31

            
32
57
pub fn format(hurl_file: &HurlFile) -> String {
33
57
    hurl_file.to_json().format()
34
}
35

            
36
pub trait ToJson {
37
    fn to_json(&self) -> JValue;
38
}
39

            
40
impl ToJson for HurlFile {
41
57
    fn to_json(&self) -> JValue {
42
57
        JValue::Object(vec![(
43
57
            "entries".to_string(),
44
214
            JValue::List(self.entries.iter().map(|e| e.to_json()).collect()),
45
        )])
46
    }
47
}
48

            
49
impl ToJson for Entry {
50
195
    fn to_json(&self) -> JValue {
51
195
        let mut attributes = vec![("request".to_string(), self.request.to_json())];
52
195
        if let Some(response) = &self.response {
53
87
            attributes.push(("response".to_string(), response.to_json()));
54
        }
55
195
        JValue::Object(attributes)
56
    }
57
}
58

            
59
impl ToJson for Request {
60
195
    fn to_json(&self) -> JValue {
61
195
        let mut attributes = vec![
62
195
            (
63
195
                "method".to_string(),
64
195
                JValue::String(self.method.to_string()),
65
195
            ),
66
195
            ("url".to_string(), JValue::String(self.url.to_string())),
67
        ];
68
195
        add_headers(&mut attributes, &self.headers);
69

            
70
195
        if !self.querystring_params().is_empty() {
71
6
            let params = self
72
6
                .querystring_params()
73
6
                .iter()
74
17
                .map(|p| p.to_json())
75
6
                .collect();
76
6
            attributes.push(("query_string_params".to_string(), JValue::List(params)));
77
        }
78
195
        if !self.form_params().is_empty() {
79
13
            let params = self.form_params().iter().map(|p| p.to_json()).collect();
80
3
            attributes.push(("form_params".to_string(), JValue::List(params)));
81
        }
82
195
        if !self.multipart_form_data().is_empty() {
83
3
            let params = self
84
3
                .multipart_form_data()
85
3
                .iter()
86
10
                .map(|p| p.to_json())
87
3
                .collect();
88
3
            attributes.push(("multipart_form_data".to_string(), JValue::List(params)));
89
        }
90
195
        if !self.cookies().is_empty() {
91
7
            let cookies = self.cookies().iter().map(|c| c.to_json()).collect();
92
3
            attributes.push(("cookies".to_string(), JValue::List(cookies)));
93
        }
94
195
        if !self.options().is_empty() {
95
280
            let options = self.options().iter().map(|c| c.to_json()).collect();
96
12
            attributes.push(("options".to_string(), JValue::List(options)));
97
        }
98
195
        if let Some(body) = &self.body {
99
48
            attributes.push(("body".to_string(), body.to_json()));
100
        }
101

            
102
        // Request comments (can be used to check custom commands)
103
195
        let comments: Vec<_> = self
104
195
            .line_terminators
105
195
            .iter()
106
239
            .filter_map(|l| l.comment.as_ref())
107
195
            .collect();
108
195
        if !comments.is_empty() {
109
33
            let comments = comments.iter().map(|c| c.to_json()).collect();
110
18
            attributes.push(("comments".to_string(), JValue::List(comments)));
111
        }
112

            
113
195
        JValue::Object(attributes)
114
    }
115
}
116

            
117
impl ToJson for Response {
118
    /// Transforms this response to a JSON object.
119
87
    fn to_json(&self) -> JValue {
120
87
        let mut attributes = vec![];
121
87
        if let Some(v) = get_json_version(&self.version.value) {
122
12
            attributes.push(("version".to_string(), JValue::String(v)));
123
        }
124
87
        if let StatusValue::Specific(n) = self.status.value {
125
87
            attributes.push(("status".to_string(), JValue::Number(n.to_string())));
126
        }
127
87
        add_headers(&mut attributes, &self.headers);
128
87
        if !self.captures().is_empty() {
129
21
            let captures = self.captures().iter().map(|c| c.to_json()).collect();
130
9
            attributes.push(("captures".to_string(), JValue::List(captures)));
131
        }
132
87
        if !self.asserts().is_empty() {
133
366
            let asserts = self.asserts().iter().map(|a| a.to_json()).collect();
134
36
            attributes.push(("asserts".to_string(), JValue::List(asserts)));
135
        }
136
87
        if let Some(body) = &self.body {
137
21
            attributes.push(("body".to_string(), body.to_json()));
138
        }
139
87
        JValue::Object(attributes)
140
    }
141
}
142

            
143
282
fn add_headers(attributes: &mut Vec<(String, JValue)>, headers: &[KeyValue]) {
144
282
    if !headers.is_empty() {
145
62
        let headers = JValue::List(headers.iter().map(|h| h.to_json()).collect());
146
24
        attributes.push(("headers".to_string(), headers));
147
    }
148
}
149

            
150
impl ToJson for Body {
151
69
    fn to_json(&self) -> JValue {
152
69
        self.value.to_json()
153
    }
154
}
155

            
156
impl ToJson for Bytes {
157
69
    fn to_json(&self) -> JValue {
158
69
        match self {
159
6
            Bytes::Base64(value) => value.to_json(),
160
3
            Bytes::Hex(value) => value.to_json(),
161
3
            Bytes::File(value) => value.to_json(),
162
6
            Bytes::Json(value) => JValue::Object(vec![
163
6
                ("type".to_string(), JValue::String("json".to_string())),
164
6
                ("value".to_string(), value.to_json()),
165
6
            ]),
166
3
            Bytes::Xml(value) => JValue::Object(vec![
167
3
                ("type".to_string(), JValue::String("xml".to_string())),
168
3
                ("value".to_string(), JValue::String(value.clone())),
169
3
            ]),
170
9
            Bytes::OnelineString(value) => JValue::Object(vec![
171
9
                ("type".to_string(), JValue::String("text".to_string())),
172
9
                ("value".to_string(), JValue::String(value.to_string())),
173
9
            ]),
174
39
            Bytes::MultilineString(multi) => {
175
                // TODO: check these values. Maybe we want to have the same
176
                // export when using:
177
                //
178
                // ~~~
179
                // GET https://foo.com
180
                // ```base64
181
                // SGVsbG8gd29ybGQ=
182
                // ```
183
                //
184
                // or
185
                //
186
                // ~~~
187
                // GET https://foo.com
188
                // base64,SGVsbG8gd29ybGQ=;
189
                // ~~~
190
39
                let lang = match multi {
191
                    MultilineString {
192
                        kind: MultilineStringKind::Text(_),
193
                        ..
194
15
                    } => "text",
195
                    MultilineString {
196
                        kind: MultilineStringKind::Json(_),
197
                        ..
198
9
                    } => "json",
199
                    MultilineString {
200
                        kind: MultilineStringKind::Xml(_),
201
                        ..
202
6
                    } => "xml",
203
                    MultilineString {
204
                        kind: MultilineStringKind::GraphQl(_),
205
                        ..
206
9
                    } => "graphql",
207
                };
208
39
                JValue::Object(vec![
209
39
                    ("type".to_string(), JValue::String(lang.to_string())),
210
39
                    ("value".to_string(), JValue::String(multi.to_string())),
211
39
                ])
212
            }
213
        }
214
    }
215
}
216

            
217
impl ToJson for Base64 {
218
6
    fn to_json(&self) -> JValue {
219
6
        let value = general_purpose::STANDARD.encode(&self.value);
220
6
        JValue::Object(vec![
221
6
            ("encoding".to_string(), JValue::String("base64".to_string())),
222
6
            ("value".to_string(), JValue::String(value)),
223
6
        ])
224
    }
225
}
226

            
227
impl ToJson for Hex {
228
3
    fn to_json(&self) -> JValue {
229
3
        let value = general_purpose::STANDARD.encode(&self.value);
230
3
        JValue::Object(vec![
231
3
            ("encoding".to_string(), JValue::String("base64".to_string())),
232
3
            ("value".to_string(), JValue::String(value)),
233
3
        ])
234
    }
235
}
236

            
237
impl ToJson for File {
238
6
    fn to_json(&self) -> JValue {
239
6
        JValue::Object(vec![
240
6
            ("type".to_string(), JValue::String("file".to_string())),
241
6
            (
242
6
                "filename".to_string(),
243
6
                JValue::String(self.filename.to_string()),
244
6
            ),
245
6
        ])
246
    }
247
}
248

            
249
87
fn get_json_version(version_value: &VersionValue) -> Option<String> {
250
87
    match version_value {
251
3
        VersionValue::Version1 => Some("HTTP/1.0".to_string()),
252
3
        VersionValue::Version11 => Some("HTTP/1.1".to_string()),
253
3
        VersionValue::Version2 => Some("HTTP/2".to_string()),
254
3
        VersionValue::Version3 => Some("HTTP/3".to_string()),
255
75
        VersionValue::VersionAny => None,
256
    }
257
}
258

            
259
impl ToJson for KeyValue {
260
84
    fn to_json(&self) -> JValue {
261
84
        let attributes = vec![
262
84
            ("name".to_string(), JValue::String(self.key.to_string())),
263
84
            ("value".to_string(), JValue::String(self.value.to_string())),
264
        ];
265
84
        JValue::Object(attributes)
266
    }
267
}
268

            
269
impl ToJson for MultipartParam {
270
9
    fn to_json(&self) -> JValue {
271
9
        match self {
272
3
            MultipartParam::Param(param) => param.to_json(),
273
6
            MultipartParam::FilenameParam(param) => param.to_json(),
274
        }
275
    }
276
}
277

            
278
impl ToJson for FilenameParam {
279
6
    fn to_json(&self) -> JValue {
280
6
        let mut attributes = vec![
281
6
            ("name".to_string(), JValue::String(self.key.to_string())),
282
6
            (
283
6
                "filename".to_string(),
284
6
                JValue::String(self.value.filename.to_string()),
285
6
            ),
286
        ];
287
6
        if let Some(content_type) = &self.value.content_type {
288
3
            attributes.push((
289
3
                "content_type".to_string(),
290
3
                JValue::String(content_type.to_string()),
291
3
            ));
292
        }
293
6
        JValue::Object(attributes)
294
    }
295
}
296

            
297
impl ToJson for Cookie {
298
6
    fn to_json(&self) -> JValue {
299
6
        let attributes = vec![
300
6
            ("name".to_string(), JValue::String(self.name.to_string())),
301
6
            ("value".to_string(), JValue::String(self.value.to_string())),
302
        ];
303
6
        JValue::Object(attributes)
304
    }
305
}
306

            
307
impl ToJson for EntryOption {
308
276
    fn to_json(&self) -> JValue {
309
276
        let value = match &self.kind {
310
6
            OptionKind::AwsSigV4(value) => JValue::String(value.to_string()),
311
6
            OptionKind::CaCertificate(filename) => JValue::String(filename.to_string()),
312
9
            OptionKind::ClientCert(filename) => JValue::String(filename.to_string()),
313
6
            OptionKind::ClientKey(filename) => JValue::String(filename.to_string()),
314
6
            OptionKind::Compressed(value) => value.to_json(),
315
6
            OptionKind::ConnectTo(value) => JValue::String(value.to_string()),
316
6
            OptionKind::ConnectTimeout(value) => value.to_json(),
317
12
            OptionKind::Delay(value) => value.to_json(),
318
6
            OptionKind::FollowLocation(value) => value.to_json(),
319
6
            OptionKind::FollowLocationTrusted(value) => value.to_json(),
320
6
            OptionKind::Header(value) => JValue::String(value.to_string()),
321
6
            OptionKind::Http10(value) => value.to_json(),
322
6
            OptionKind::Http11(value) => value.to_json(),
323
6
            OptionKind::Http2(value) => value.to_json(),
324
6
            OptionKind::Http3(value) => value.to_json(),
325
6
            OptionKind::Insecure(value) => value.to_json(),
326
6
            OptionKind::IpV4(value) => value.to_json(),
327
6
            OptionKind::IpV6(value) => value.to_json(),
328
6
            OptionKind::LimitRate(value) => value.to_json(),
329
6
            OptionKind::MaxRedirect(value) => value.to_json(),
330
6
            OptionKind::MaxTime(value) => value.to_json(),
331
6
            OptionKind::NetRc(value) => value.to_json(),
332
6
            OptionKind::NetRcFile(filename) => JValue::String(filename.to_string()),
333
6
            OptionKind::NetRcOptional(value) => value.to_json(),
334
6
            OptionKind::Output(filename) => JValue::String(filename.to_string()),
335
6
            OptionKind::PathAsIs(value) => value.to_json(),
336
6
            OptionKind::PinnedPublicKey(value) => JValue::String(value.to_string()),
337
6
            OptionKind::Proxy(value) => JValue::String(value.to_string()),
338
9
            OptionKind::Repeat(value) => value.to_json(),
339
6
            OptionKind::Resolve(value) => JValue::String(value.to_string()),
340
12
            OptionKind::Retry(value) => value.to_json(),
341
12
            OptionKind::RetryInterval(value) => value.to_json(),
342
6
            OptionKind::Skip(value) => value.to_json(),
343
6
            OptionKind::UnixSocket(value) => JValue::String(value.to_string()),
344
6
            OptionKind::User(value) => JValue::String(value.to_string()),
345
27
            OptionKind::Variable(value) => {
346
27
                JValue::String(format!("{}={}", value.name, value.value.to_source()))
347
            }
348
9
            OptionKind::Verbose(value) => value.to_json(),
349
6
            OptionKind::VeryVerbose(value) => value.to_json(),
350
        };
351

            
352
        // If the value contains the unit such as `{ "value": 10, "unit": "second" }`
353
        // The JSON for this option should still have one level
354
        // for example: { "name": "delay", "value": 10, "unit", "second" }
355
276
        let attributes = if let JValue::Object(mut attributes) = value {
356
18
            attributes.push((
357
18
                "name".to_string(),
358
18
                JValue::String(self.kind.identifier().to_string()),
359
18
            ));
360
18
            attributes
361
        } else {
362
258
            vec![
363
258
                (
364
258
                    "name".to_string(),
365
258
                    JValue::String(self.kind.identifier().to_string()),
366
258
                ),
367
258
                ("value".to_string(), value),
368
            ]
369
        };
370
276
        JValue::Object(attributes)
371
    }
372
}
373

            
374
impl ToJson for BooleanOption {
375
99
    fn to_json(&self) -> JValue {
376
99
        match self {
377
51
            BooleanOption::Literal(value) => JValue::Boolean(*value),
378
48
            BooleanOption::Placeholder(placeholder) => placeholder.to_json(),
379
        }
380
    }
381
}
382

            
383
impl ToJson for CountOption {
384
27
    fn to_json(&self) -> JValue {
385
27
        match self {
386
18
            CountOption::Literal(value) => value.to_json(),
387
9
            CountOption::Placeholder(placeholder) => placeholder.to_json(),
388
        }
389
    }
390
}
391

            
392
impl ToJson for Count {
393
18
    fn to_json(&self) -> JValue {
394
18
        match self {
395
12
            Count::Finite(n) => JValue::Number(n.to_string()),
396
6
            Count::Infinite => JValue::Number("-1".to_string()),
397
        }
398
    }
399
}
400

            
401
impl ToJson for DurationOption {
402
36
    fn to_json(&self) -> JValue {
403
36
        match self {
404
24
            DurationOption::Literal(value) => value.to_json(),
405
12
            DurationOption::Placeholder(placeholder) => placeholder.to_json(),
406
        }
407
    }
408
}
409

            
410
impl ToJson for Duration {
411
24
    fn to_json(&self) -> JValue {
412
24
        if let Some(unit) = self.unit {
413
18
            let mut attributes =
414
18
                vec![("value".to_string(), JValue::Number(self.value.to_string()))];
415
18
            attributes.push(("unit".to_string(), JValue::String(unit.to_string())));
416
18
            JValue::Object(attributes)
417
        } else {
418
6
            JValue::Number(self.value.to_string())
419
        }
420
    }
421
}
422

            
423
impl ToJson for Capture {
424
18
    fn to_json(&self) -> JValue {
425
18
        let mut attributes = vec![
426
18
            ("name".to_string(), JValue::String(self.name.to_string())),
427
18
            ("query".to_string(), self.query.to_json()),
428
        ];
429
18
        if !self.filters.is_empty() {
430
4
            let filters = JValue::List(self.filters.iter().map(|(_, f)| f.to_json()).collect());
431
3
            attributes.push(("filters".to_string(), filters));
432
        }
433
18
        if self.redacted {
434
6
            attributes.push(("redact".to_string(), JValue::Boolean(true)));
435
        }
436
18
        JValue::Object(attributes)
437
    }
438
}
439

            
440
impl ToJson for Assert {
441
354
    fn to_json(&self) -> JValue {
442
354
        let mut attributes = vec![("query".to_string(), self.query.to_json())];
443
354
        if !self.filters.is_empty() {
444
157
            let filters = JValue::List(self.filters.iter().map(|(_, f)| f.to_json()).collect());
445
102
            attributes.push(("filters".to_string(), filters));
446
        }
447
354
        attributes.push(("predicate".to_string(), self.predicate.to_json()));
448
354
        JValue::Object(attributes)
449
    }
450
}
451

            
452
impl ToJson for Query {
453
372
    fn to_json(&self) -> JValue {
454
372
        let attributes = query_value_attributes(&self.value);
455
372
        JValue::Object(attributes)
456
    }
457
}
458

            
459
372
fn query_value_attributes(query_value: &QueryValue) -> Vec<(String, JValue)> {
460
372
    let mut attributes = vec![];
461
372
    let att_type = JValue::String(query_value.identifier().to_string());
462
372
    attributes.push(("type".to_string(), att_type));
463

            
464
372
    match query_value {
465
231
        QueryValue::Jsonpath { expr, .. } => {
466
231
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
467
        }
468
6
        QueryValue::Header { name, .. } => {
469
6
            attributes.push(("name".to_string(), JValue::String(name.to_string())));
470
        }
471
6
        QueryValue::Cookie { expr, .. } => {
472
6
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
473
        }
474
3
        QueryValue::Xpath { expr, .. } => {
475
3
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
476
        }
477
3
        QueryValue::Regex { value, .. } => {
478
3
            attributes.push(("expr".to_string(), value.to_json()));
479
        }
480
9
        QueryValue::Variable { name, .. } => {
481
9
            attributes.push(("name".to_string(), JValue::String(name.to_string())));
482
        }
483
        QueryValue::Certificate {
484
30
            attribute_name: field,
485
            ..
486
30
        } => {
487
30
            attributes.push(("expr".to_string(), field.to_json()));
488
        }
489
84
        _ => {}
490
    };
491
372
    attributes
492
}
493

            
494
impl ToJson for RegexValue {
495
9
    fn to_json(&self) -> JValue {
496
9
        match self {
497
3
            RegexValue::Template(template) => JValue::String(template.to_string()),
498
6
            RegexValue::Regex(regex) => regex.to_json(),
499
        }
500
    }
501
}
502

            
503
impl ToJson for Regex {
504
6
    fn to_json(&self) -> JValue {
505
6
        let attributes = vec![
506
6
            ("type".to_string(), JValue::String("regex".to_string())),
507
6
            ("value".to_string(), JValue::String(self.to_string())),
508
        ];
509
6
        JValue::Object(attributes)
510
    }
511
}
512

            
513
impl ToJson for CertificateAttributeName {
514
30
    fn to_json(&self) -> JValue {
515
30
        JValue::String(self.identifier().to_string())
516
    }
517
}
518

            
519
impl ToJson for Predicate {
520
354
    fn to_json(&self) -> JValue {
521
354
        let mut attributes = vec![];
522
354
        if self.not {
523
3
            attributes.push(("not".to_string(), JValue::Boolean(true)));
524
        }
525
354
        let identifier = self.predicate_func.value.identifier();
526
354
        attributes.push(("type".to_string(), JValue::String(identifier.to_string())));
527

            
528
354
        match &self.predicate_func.value {
529
237
            PredicateFuncValue::Equal { value, .. } => add_predicate_value(&mut attributes, value),
530
9
            PredicateFuncValue::NotEqual { value, .. } => {
531
9
                add_predicate_value(&mut attributes, value);
532
            }
533
9
            PredicateFuncValue::GreaterThan { value, .. } => {
534
9
                add_predicate_value(&mut attributes, value);
535
            }
536
3
            PredicateFuncValue::GreaterThanOrEqual { value, .. } => {
537
3
                add_predicate_value(&mut attributes, value);
538
            }
539
9
            PredicateFuncValue::LessThan { value, .. } => {
540
9
                add_predicate_value(&mut attributes, value);
541
            }
542
3
            PredicateFuncValue::LessThanOrEqual { value, .. } => {
543
3
                add_predicate_value(&mut attributes, value);
544
            }
545
9
            PredicateFuncValue::StartWith { value, .. } => {
546
9
                add_predicate_value(&mut attributes, value);
547
            }
548
6
            PredicateFuncValue::EndWith { value, .. } => {
549
6
                add_predicate_value(&mut attributes, value);
550
            }
551
9
            PredicateFuncValue::Contain { value, .. } => {
552
9
                add_predicate_value(&mut attributes, value);
553
            }
554
3
            PredicateFuncValue::Include { value, .. } => {
555
3
                add_predicate_value(&mut attributes, value);
556
            }
557
9
            PredicateFuncValue::Match { value, .. } => {
558
9
                add_predicate_value(&mut attributes, value);
559
            }
560
            PredicateFuncValue::IsInteger
561
            | PredicateFuncValue::IsFloat
562
            | PredicateFuncValue::IsBoolean
563
            | PredicateFuncValue::IsString
564
            | PredicateFuncValue::IsCollection
565
            | PredicateFuncValue::IsDate
566
            | PredicateFuncValue::IsIsoDate
567
            | PredicateFuncValue::Exist
568
            | PredicateFuncValue::IsEmpty
569
            | PredicateFuncValue::IsNumber
570
            | PredicateFuncValue::IsIpv4
571
            | PredicateFuncValue::IsIpv6
572
48
            | PredicateFuncValue::IsUuid => {}
573
        }
574
354
        JValue::Object(attributes)
575
    }
576
}
577

            
578
306
fn add_predicate_value(attributes: &mut Vec<(String, JValue)>, predicate_value: &PredicateValue) {
579
306
    let (value, encoding) = json_predicate_value(predicate_value);
580
306
    attributes.push(("value".to_string(), value));
581
306
    if let Some(encoding) = encoding {
582
36
        attributes.push(("encoding".to_string(), JValue::String(encoding)));
583
    }
584
}
585

            
586
306
fn json_predicate_value(predicate_value: &PredicateValue) -> (JValue, Option<String>) {
587
306
    match predicate_value {
588
135
        PredicateValue::String(value) => (JValue::String(value.to_string()), None),
589
18
        PredicateValue::MultilineString(value) => (JValue::String(value.value().to_string()), None),
590
3
        PredicateValue::Bool(value) => (JValue::Boolean(*value), None),
591
3
        PredicateValue::Null => (JValue::Null, None),
592
105
        PredicateValue::Number(value) => (JValue::Number(value.to_string()), None),
593
3
        PredicateValue::File(value) => (value.to_json(), None),
594
27
        PredicateValue::Hex(value) => {
595
27
            let base64_string = general_purpose::STANDARD.encode(value.value.clone());
596
27
            (JValue::String(base64_string), Some("base64".to_string()))
597
        }
598
3
        PredicateValue::Base64(value) => {
599
3
            let base64_string = general_purpose::STANDARD.encode(value.value.clone());
600
3
            (JValue::String(base64_string), Some("base64".to_string()))
601
        }
602
3
        PredicateValue::Placeholder(value) => (JValue::String(value.to_string()), None),
603
6
        PredicateValue::Regex(value) => {
604
6
            (JValue::String(value.to_string()), Some("regex".to_string()))
605
        }
606
    }
607
}
608

            
609
impl ToJson for JsonValue {
610
102
    fn to_json(&self) -> JValue {
611
102
        match self {
612
3
            JsonValue::Null => JValue::Null,
613
45
            JsonValue::Number(s) => JValue::Number(s.to_string()),
614
18
            JsonValue::String(s) => JValue::String(s.to_string()),
615
3
            JsonValue::Boolean(v) => JValue::Boolean(*v),
616
15
            JsonValue::List { elements, .. } => {
617
56
                JValue::List(elements.iter().map(|e| e.to_json()).collect())
618
            }
619
15
            JsonValue::Object { elements, .. } => JValue::Object(
620
15
                elements
621
15
                    .iter()
622
50
                    .map(|elem| (elem.name.to_string(), elem.value.to_json()))
623
15
                    .collect(),
624
            ),
625
3
            JsonValue::Placeholder(exp) => JValue::String(format!("{{{{{exp}}}}}")),
626
        }
627
    }
628
}
629

            
630
impl ToJson for JsonListElement {
631
51
    fn to_json(&self) -> JValue {
632
51
        self.value.to_json()
633
    }
634
}
635

            
636
impl ToJson for Filter {
637
126
    fn to_json(&self) -> JValue {
638
126
        self.value.to_json()
639
    }
640
}
641

            
642
impl ToJson for FilterValue {
643
126
    fn to_json(&self) -> JValue {
644
126
        let mut attributes = vec![];
645
126
        let att_name = "type".to_string();
646
126
        let att_value = JValue::String(self.identifier().to_string());
647
126
        attributes.push((att_name, att_value));
648

            
649
126
        match self {
650
6
            FilterValue::Decode { encoding, .. } => {
651
6
                attributes.push(("encoding".to_string(), JValue::String(encoding.to_string())));
652
            }
653
9
            FilterValue::Format { fmt, .. } => {
654
9
                attributes.push(("fmt".to_string(), JValue::String(fmt.to_string())));
655
            }
656
9
            FilterValue::JsonPath { expr, .. } => {
657
9
                attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
658
            }
659
3
            FilterValue::Nth { n, .. } => {
660
3
                attributes.push(("n".to_string(), JValue::Number(n.to_string())));
661
            }
662
3
            FilterValue::Regex { value, .. } => {
663
3
                attributes.push(("expr".to_string(), value.to_json()));
664
            }
665
            FilterValue::Replace {
666
15
                old_value,
667
15
                new_value,
668
                ..
669
15
            } => {
670
15
                attributes.push((
671
15
                    "old_value".to_string(),
672
15
                    JValue::String(old_value.to_string()),
673
15
                ));
674
15
                attributes.push((
675
15
                    "new_value".to_string(),
676
15
                    JValue::String(new_value.to_string()),
677
15
                ));
678
            }
679
            FilterValue::ReplaceRegex {
680
3
                pattern, new_value, ..
681
3
            } => {
682
3
                attributes.push(("pattern".to_string(), pattern.to_json()));
683
3
                attributes.push((
684
3
                    "new_value".to_string(),
685
3
                    JValue::String(new_value.to_string()),
686
3
                ));
687
            }
688
3
            FilterValue::Split { sep, .. } => {
689
3
                attributes.push(("sep".to_string(), JValue::String(sep.to_string())));
690
            }
691
3
            FilterValue::ToDate { fmt, .. } => {
692
3
                attributes.push(("fmt".to_string(), JValue::String(fmt.to_string())));
693
            }
694
3
            FilterValue::UrlQueryParam { param, .. } => {
695
3
                attributes.push(("param".to_string(), JValue::String(param.to_string())));
696
            }
697
3
            FilterValue::XPath { expr, .. } => {
698
3
                attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
699
            }
700
66
            _ => {}
701
        }
702
126
        JValue::Object(attributes)
703
    }
704
}
705

            
706
impl ToJson for Placeholder {
707
72
    fn to_json(&self) -> JValue {
708
72
        JValue::String(format!("{{{{{self}}}}}"))
709
    }
710
}
711

            
712
impl ToJson for Comment {
713
27
    fn to_json(&self) -> JValue {
714
27
        JValue::String(self.value.to_string())
715
    }
716
}
717

            
718
impl ToJson for NaturalOption {
719
6
    fn to_json(&self) -> JValue {
720
6
        match self {
721
3
            NaturalOption::Literal(value) => JValue::Number(value.to_string()),
722
3
            NaturalOption::Placeholder(placeholder) => placeholder.to_json(),
723
        }
724
    }
725
}
726
#[cfg(test)]
727
pub mod tests {
728
    use hurl_core::ast::{
729
        LineTerminator, Method, Number, PredicateFunc, SourceInfo, Status, Template,
730
        TemplateElement, Version, Whitespace, I64,
731
    };
732
    use hurl_core::reader::Pos;
733
    use hurl_core::typing::ToSource;
734

            
735
    use super::*;
736

            
737
    fn whitespace() -> Whitespace {
738
        Whitespace {
739
            value: String::new(),
740
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
741
        }
742
    }
743

            
744
    fn line_terminator() -> LineTerminator {
745
        LineTerminator {
746
            space0: whitespace(),
747
            comment: None,
748
            newline: whitespace(),
749
        }
750
    }
751

            
752
    #[test]
753
    pub fn test_request() {
754
        assert_eq!(
755
            Request {
756
                line_terminators: vec![],
757
                space0: whitespace(),
758
                method: Method::new("GET"),
759
                space1: whitespace(),
760
                url: Template::new(
761
                    None,
762
                    vec![TemplateElement::String {
763
                        value: "http://example.com".to_string(),
764
                        source: "not_used".to_source(),
765
                    }],
766
                    SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
767
                ),
768
                line_terminator0: line_terminator(),
769
                headers: vec![KeyValue {
770
                    line_terminators: vec![],
771
                    space0: whitespace(),
772
                    key: Template::new(
773
                        None,
774
                        vec![TemplateElement::String {
775
                            value: "Foo".to_string(),
776
                            source: "unused".to_source(),
777
                        }],
778
                        SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0))
779
                    ),
780
                    space1: whitespace(),
781
                    space2: whitespace(),
782
                    value: Template::new(
783
                        None,
784
                        vec![TemplateElement::String {
785
                            value: "Bar".to_string(),
786
                            source: "unused".to_source(),
787
                        }],
788
                        SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0))
789
                    ),
790
                    line_terminator0: line_terminator(),
791
                }],
792
                sections: vec![],
793
                body: None,
794
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
795
            }
796
            .to_json(),
797
            JValue::Object(vec![
798
                ("method".to_string(), JValue::String("GET".to_string())),
799
                (
800
                    "url".to_string(),
801
                    JValue::String("http://example.com".to_string())
802
                ),
803
                (
804
                    "headers".to_string(),
805
                    JValue::List(vec![JValue::Object(vec![
806
                        ("name".to_string(), JValue::String("Foo".to_string())),
807
                        ("value".to_string(), JValue::String("Bar".to_string()))
808
                    ])])
809
                )
810
            ])
811
        );
812
    }
813

            
814
    #[test]
815
    pub fn test_response() {
816
        assert_eq!(
817
            Response {
818
                line_terminators: vec![],
819
                version: Version {
820
                    value: VersionValue::Version11,
821
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
822
                },
823
                space0: whitespace(),
824
                status: Status {
825
                    value: StatusValue::Specific(200),
826
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
827
                },
828
                space1: whitespace(),
829
                line_terminator0: line_terminator(),
830
                headers: vec![],
831
                sections: vec![],
832
                body: None,
833
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
834
            }
835
            .to_json(),
836
            JValue::Object(vec![
837
                (
838
                    "version".to_string(),
839
                    JValue::String("HTTP/1.1".to_string())
840
                ),
841
                ("status".to_string(), JValue::Number("200".to_string()))
842
            ])
843
        );
844
        assert_eq!(
845
            Response {
846
                line_terminators: vec![],
847
                version: Version {
848
                    value: VersionValue::VersionAny,
849
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
850
                },
851
                space0: whitespace(),
852
                status: Status {
853
                    value: StatusValue::Any,
854
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
855
                },
856
                space1: whitespace(),
857
                line_terminator0: line_terminator(),
858
                headers: vec![],
859
                sections: vec![],
860
                body: None,
861
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
862
            }
863
            .to_json(),
864
            JValue::Object(vec![])
865
        );
866
    }
867

            
868
    fn header_query() -> Query {
869
        Query {
870
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
871
            value: QueryValue::Header {
872
                space0: whitespace(),
873
                name: Template::new(
874
                    None,
875
                    vec![TemplateElement::String {
876
                        value: "Content-Length".to_string(),
877
                        source: "Content-Length".to_source(),
878
                    }],
879
                    SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
880
                ),
881
            },
882
        }
883
    }
884

            
885
    fn header_capture() -> Capture {
886
        Capture {
887
            line_terminators: vec![],
888
            space0: whitespace(),
889
            name: Template::new(
890
                None,
891
                vec![TemplateElement::String {
892
                    value: "size".to_string(),
893
                    source: "unused".to_source(),
894
                }],
895
                SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
896
            ),
897
            space1: whitespace(),
898
            space2: whitespace(),
899
            query: header_query(),
900
            filters: vec![],
901
            space3: whitespace(),
902
            redacted: false,
903
            line_terminator0: line_terminator(),
904
        }
905
    }
906

            
907
    fn header_assert() -> Assert {
908
        Assert {
909
            line_terminators: vec![],
910
            space0: whitespace(),
911
            query: header_query(),
912
            filters: vec![],
913
            space1: whitespace(),
914
            predicate: equal_int_predicate(10),
915
            line_terminator0: line_terminator(),
916
        }
917
    }
918

            
919
    fn equal_int_predicate(value: i64) -> Predicate {
920
        Predicate {
921
            not: false,
922
            space0: whitespace(),
923
            predicate_func: PredicateFunc {
924
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
925
                value: PredicateFuncValue::Equal {
926
                    space0: whitespace(),
927
                    value: PredicateValue::Number(Number::Integer(I64::new(
928
                        value,
929
                        value.to_string().to_source(),
930
                    ))),
931
                },
932
            },
933
        }
934
    }
935

            
936
    #[test]
937
    pub fn test_query() {
938
        assert_eq!(
939
            header_query().to_json(),
940
            JValue::Object(vec![
941
                ("type".to_string(), JValue::String("header".to_string())),
942
                (
943
                    "name".to_string(),
944
                    JValue::String("Content-Length".to_string())
945
                ),
946
            ])
947
        );
948
    }
949

            
950
    #[test]
951
    pub fn test_capture() {
952
        assert_eq!(
953
            header_capture().to_json(),
954
            JValue::Object(vec![
955
                ("name".to_string(), JValue::String("size".to_string())),
956
                (
957
                    "query".to_string(),
958
                    JValue::Object(vec![
959
                        ("type".to_string(), JValue::String("header".to_string())),
960
                        (
961
                            "name".to_string(),
962
                            JValue::String("Content-Length".to_string())
963
                        ),
964
                    ])
965
                ),
966
            ])
967
        );
968
    }
969

            
970
    #[test]
971
    pub fn test_predicate() {
972
        assert_eq!(
973
            equal_int_predicate(10).to_json(),
974
            JValue::Object(vec![
975
                ("type".to_string(), JValue::String("==".to_string())),
976
                ("value".to_string(), JValue::Number("10".to_string()))
977
            ]),
978
        );
979
    }
980

            
981
    #[test]
982
    pub fn test_assert() {
983
        assert_eq!(
984
            header_assert().to_json(),
985
            JValue::Object(vec![
986
                (
987
                    "query".to_string(),
988
                    JValue::Object(vec![
989
                        ("type".to_string(), JValue::String("header".to_string())),
990
                        (
991
                            "name".to_string(),
992
                            JValue::String("Content-Length".to_string())
993
                        ),
994
                    ])
995
                ),
996
                (
997
                    "predicate".to_string(),
998
                    JValue::Object(vec![
999
                        ("type".to_string(), JValue::String("==".to_string())),
                        ("value".to_string(), JValue::Number("10".to_string()))
                    ])
                )
            ]),
        );
    }
}