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, FileParam, 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
54
pub fn format(hurl_file: &HurlFile) -> String {
33
54
    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
54
    fn to_json(&self) -> JValue {
42
54
        JValue::Object(vec![(
43
54
            "entries".to_string(),
44
192
            JValue::List(self.entries.iter().map(|e| e.to_json()).collect()),
45
54
        )])
46
    }
47
}
48

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

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

            
70
174
        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
174
        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
174
        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
174
        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
174
        if !self.options().is_empty() {
95
268
            let options = self.options().iter().map(|c| c.to_json()).collect();
96
12
            attributes.push(("options".to_string(), JValue::List(options)));
97
        }
98
174
        if let Some(body) = &self.body {
99
39
            attributes.push(("body".to_string(), body.to_json()));
100
        }
101

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

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

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

            
143
252
fn add_headers(attributes: &mut Vec<(String, JValue)>, headers: &[KeyValue]) {
144
252
    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
60
    fn to_json(&self) -> JValue {
152
60
        self.value.to_json()
153
    }
154
}
155

            
156
impl ToJson for Bytes {
157
60
    fn to_json(&self) -> JValue {
158
60
        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
30
            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
30
                let lang = match multi {
191
                    MultilineString {
192
                        kind: MultilineStringKind::Text(_),
193
                        ..
194
12
                    } => "text",
195
                    MultilineString {
196
                        kind: MultilineStringKind::Json(_),
197
                        ..
198
6
                    } => "json",
199
                    MultilineString {
200
                        kind: MultilineStringKind::Xml(_),
201
                        ..
202
6
                    } => "xml",
203
                    MultilineString {
204
                        kind: MultilineStringKind::GraphQl(_),
205
                        ..
206
6
                    } => "graphql",
207
                };
208
30
                JValue::Object(vec![
209
30
                    ("type".to_string(), JValue::String(lang.to_string())),
210
30
                    ("value".to_string(), JValue::String(multi.to_string())),
211
30
                ])
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
78
fn get_json_version(version_value: &VersionValue) -> Option<String> {
250
78
    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
66
        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
84
        ];
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::FileParam(param) => param.to_json(),
274
        }
275
    }
276
}
277

            
278
impl ToJson for FileParam {
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
6
        ];
287
6
        if let Some(content_type) = self.value.content_type.clone() {
288
3
            attributes.push(("content_type".to_string(), JValue::String(content_type)));
289
        }
290
6
        JValue::Object(attributes)
291
    }
292
}
293

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

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

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

            
369
impl ToJson for BooleanOption {
370
99
    fn to_json(&self) -> JValue {
371
99
        match self {
372
51
            BooleanOption::Literal(value) => JValue::Boolean(*value),
373
48
            BooleanOption::Placeholder(placeholder) => placeholder.to_json(),
374
        }
375
    }
376
}
377

            
378
impl ToJson for CountOption {
379
27
    fn to_json(&self) -> JValue {
380
27
        match self {
381
18
            CountOption::Literal(value) => value.to_json(),
382
9
            CountOption::Placeholder(placeholder) => placeholder.to_json(),
383
        }
384
    }
385
}
386

            
387
impl ToJson for Count {
388
18
    fn to_json(&self) -> JValue {
389
18
        match self {
390
12
            Count::Finite(n) => JValue::Number(n.to_string()),
391
6
            Count::Infinite => JValue::Number("-1".to_string()),
392
        }
393
    }
394
}
395

            
396
impl ToJson for DurationOption {
397
30
    fn to_json(&self) -> JValue {
398
30
        match self {
399
21
            DurationOption::Literal(value) => value.to_json(),
400
9
            DurationOption::Placeholder(placeholder) => placeholder.to_json(),
401
        }
402
    }
403
}
404

            
405
impl ToJson for Duration {
406
21
    fn to_json(&self) -> JValue {
407
21
        if let Some(unit) = self.unit {
408
15
            let mut attributes =
409
15
                vec![("value".to_string(), JValue::Number(self.value.to_string()))];
410
15
            attributes.push(("unit".to_string(), JValue::String(unit.to_string())));
411
15
            JValue::Object(attributes)
412
        } else {
413
6
            JValue::Number(self.value.to_string())
414
        }
415
    }
416
}
417

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

            
435
impl ToJson for Assert {
436
321
    fn to_json(&self) -> JValue {
437
321
        let mut attributes = vec![("query".to_string(), self.query.to_json())];
438
321
        if !self.filters.is_empty() {
439
119
            let filters = JValue::List(self.filters.iter().map(|(_, f)| f.to_json()).collect());
440
78
            attributes.push(("filters".to_string(), filters));
441
        }
442
321
        attributes.push(("predicate".to_string(), self.predicate.to_json()));
443
321
        JValue::Object(attributes)
444
    }
445
}
446

            
447
impl ToJson for Query {
448
333
    fn to_json(&self) -> JValue {
449
333
        let attributes = query_value_attributes(&self.value);
450
333
        JValue::Object(attributes)
451
    }
452
}
453

            
454
333
fn query_value_attributes(query_value: &QueryValue) -> Vec<(String, JValue)> {
455
333
    let mut attributes = vec![];
456
333
    let att_type = JValue::String(query_value.identifier().to_string());
457
333
    attributes.push(("type".to_string(), att_type));
458
333

            
459
333
    match query_value {
460
201
        QueryValue::Jsonpath { expr, .. } => {
461
201
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
462
        }
463
6
        QueryValue::Header { name, .. } => {
464
6
            attributes.push(("name".to_string(), JValue::String(name.to_string())));
465
        }
466
6
        QueryValue::Cookie { expr, .. } => {
467
6
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
468
        }
469
3
        QueryValue::Xpath { expr, .. } => {
470
3
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
471
        }
472
3
        QueryValue::Regex { value, .. } => {
473
3
            attributes.push(("expr".to_string(), value.to_json()));
474
        }
475
9
        QueryValue::Variable { name, .. } => {
476
9
            attributes.push(("name".to_string(), JValue::String(name.to_string())));
477
        }
478
        QueryValue::Certificate {
479
30
            attribute_name: field,
480
30
            ..
481
30
        } => {
482
30
            attributes.push(("expr".to_string(), field.to_json()));
483
        }
484
75
        _ => {}
485
    };
486
333
    attributes
487
}
488

            
489
impl ToJson for RegexValue {
490
21
    fn to_json(&self) -> JValue {
491
21
        match self {
492
18
            RegexValue::Template(template) => JValue::String(template.to_string()),
493
3
            RegexValue::Regex(regex) => regex.to_json(),
494
        }
495
    }
496
}
497

            
498
impl ToJson for Regex {
499
3
    fn to_json(&self) -> JValue {
500
3
        let attributes = vec![
501
3
            ("type".to_string(), JValue::String("regex".to_string())),
502
3
            ("value".to_string(), JValue::String(self.to_string())),
503
3
        ];
504
3
        JValue::Object(attributes)
505
    }
506
}
507

            
508
impl ToJson for CertificateAttributeName {
509
30
    fn to_json(&self) -> JValue {
510
30
        JValue::String(self.identifier().to_string())
511
    }
512
}
513

            
514
impl ToJson for Predicate {
515
321
    fn to_json(&self) -> JValue {
516
321
        let mut attributes = vec![];
517
321
        if self.not {
518
3
            attributes.push(("not".to_string(), JValue::Boolean(true)));
519
        }
520
321
        let identifier = self.predicate_func.value.identifier();
521
321
        attributes.push(("type".to_string(), JValue::String(identifier.to_string())));
522
321

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

            
572
276
fn add_predicate_value(attributes: &mut Vec<(String, JValue)>, predicate_value: &PredicateValue) {
573
276
    let (value, encoding) = json_predicate_value(predicate_value);
574
276
    attributes.push(("value".to_string(), value));
575
276
    if let Some(encoding) = encoding {
576
30
        attributes.push(("encoding".to_string(), JValue::String(encoding)));
577
    }
578
}
579

            
580
276
fn json_predicate_value(predicate_value: &PredicateValue) -> (JValue, Option<String>) {
581
276
    match predicate_value {
582
114
        PredicateValue::String(value) => (JValue::String(value.to_string()), None),
583
15
        PredicateValue::MultilineString(value) => (JValue::String(value.value().to_string()), None),
584
3
        PredicateValue::Bool(value) => (JValue::Boolean(*value), None),
585
3
        PredicateValue::Null => (JValue::Null, None),
586
105
        PredicateValue::Number(value) => (JValue::Number(value.to_string()), None),
587
3
        PredicateValue::File(value) => (value.to_json(), None),
588
24
        PredicateValue::Hex(value) => {
589
24
            let base64_string = general_purpose::STANDARD.encode(value.value.clone());
590
24
            (JValue::String(base64_string), Some("base64".to_string()))
591
        }
592
3
        PredicateValue::Base64(value) => {
593
3
            let base64_string = general_purpose::STANDARD.encode(value.value.clone());
594
3
            (JValue::String(base64_string), Some("base64".to_string()))
595
        }
596
3
        PredicateValue::Placeholder(value) => (JValue::String(value.to_string()), None),
597
3
        PredicateValue::Regex(value) => {
598
3
            (JValue::String(value.to_string()), Some("regex".to_string()))
599
        }
600
    }
601
}
602

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

            
624
impl ToJson for JsonListElement {
625
51
    fn to_json(&self) -> JValue {
626
51
        self.value.to_json()
627
    }
628
}
629

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

            
636
impl ToJson for FilterValue {
637
96
    fn to_json(&self) -> JValue {
638
96
        let mut attributes = vec![];
639
96
        let att_name = "type".to_string();
640
96
        let att_value = JValue::String(self.identifier().to_string());
641
96
        attributes.push((att_name, att_value));
642
96

            
643
96
        match self {
644
6
            FilterValue::Decode { encoding, .. } => {
645
6
                attributes.push(("encoding".to_string(), JValue::String(encoding.to_string())));
646
            }
647
9
            FilterValue::Format { fmt, .. } => {
648
9
                attributes.push(("fmt".to_string(), JValue::String(fmt.to_string())));
649
            }
650
3
            FilterValue::JsonPath { expr, .. } => {
651
3
                attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
652
            }
653
3
            FilterValue::Nth { n, .. } => {
654
3
                attributes.push(("n".to_string(), JValue::Number(n.to_string())));
655
            }
656
3
            FilterValue::Regex { value, .. } => {
657
3
                attributes.push(("expr".to_string(), value.to_json()));
658
            }
659
            FilterValue::Replace {
660
15
                old_value,
661
15
                new_value,
662
15
                ..
663
15
            } => {
664
15
                attributes.push(("old_value".to_string(), old_value.to_json()));
665
15
                attributes.push((
666
15
                    "new_value".to_string(),
667
15
                    JValue::String(new_value.to_string()),
668
15
                ));
669
            }
670
3
            FilterValue::Split { sep, .. } => {
671
3
                attributes.push(("sep".to_string(), JValue::String(sep.to_string())));
672
            }
673
3
            FilterValue::ToDate { fmt, .. } => {
674
3
                attributes.push(("fmt".to_string(), JValue::String(fmt.to_string())));
675
            }
676
3
            FilterValue::XPath { expr, .. } => {
677
3
                attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
678
            }
679
48
            _ => {}
680
        }
681
96
        JValue::Object(attributes)
682
    }
683
}
684

            
685
impl ToJson for Placeholder {
686
69
    fn to_json(&self) -> JValue {
687
69
        JValue::String(format!("{{{{{}}}}}", self))
688
    }
689
}
690

            
691
impl ToJson for Comment {
692
12
    fn to_json(&self) -> JValue {
693
12
        JValue::String(self.value.to_string())
694
    }
695
}
696

            
697
impl ToJson for NaturalOption {
698
6
    fn to_json(&self) -> JValue {
699
6
        match self {
700
3
            NaturalOption::Literal(value) => JValue::Number(value.to_string()),
701
3
            NaturalOption::Placeholder(placeholder) => placeholder.to_json(),
702
        }
703
    }
704
}
705
#[cfg(test)]
706
pub mod tests {
707
    use hurl_core::ast::{
708
        LineTerminator, Method, Number, PredicateFunc, SourceInfo, Status, Template,
709
        TemplateElement, Version, Whitespace, I64,
710
    };
711
    use hurl_core::reader::Pos;
712
    use hurl_core::typing::ToSource;
713

            
714
    use super::*;
715

            
716
    fn whitespace() -> Whitespace {
717
        Whitespace {
718
            value: String::new(),
719
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
720
        }
721
    }
722

            
723
    fn line_terminator() -> LineTerminator {
724
        LineTerminator {
725
            space0: whitespace(),
726
            comment: None,
727
            newline: whitespace(),
728
        }
729
    }
730

            
731
    #[test]
732
    pub fn test_request() {
733
        assert_eq!(
734
            Request {
735
                line_terminators: vec![],
736
                space0: whitespace(),
737
                method: Method("GET".to_string()),
738
                space1: whitespace(),
739
                url: Template {
740
                    delimiter: None,
741
                    elements: vec![TemplateElement::String {
742
                        value: "http://example.com".to_string(),
743
                        source: "not_used".to_source(),
744
                    }],
745
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
746
                },
747
                line_terminator0: line_terminator(),
748
                headers: vec![KeyValue {
749
                    line_terminators: vec![],
750
                    space0: whitespace(),
751
                    key: Template {
752
                        delimiter: None,
753
                        elements: vec![TemplateElement::String {
754
                            value: "Foo".to_string(),
755
                            source: "unused".to_source(),
756
                        }],
757
                        source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
758
                    },
759
                    space1: whitespace(),
760
                    space2: whitespace(),
761
                    value: Template {
762
                        delimiter: None,
763
                        elements: vec![TemplateElement::String {
764
                            value: "Bar".to_string(),
765
                            source: "unused".to_source(),
766
                        }],
767
                        source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
768
                    },
769
                    line_terminator0: line_terminator(),
770
                }],
771
                sections: vec![],
772
                body: None,
773
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
774
            }
775
            .to_json(),
776
            JValue::Object(vec![
777
                ("method".to_string(), JValue::String("GET".to_string())),
778
                (
779
                    "url".to_string(),
780
                    JValue::String("http://example.com".to_string())
781
                ),
782
                (
783
                    "headers".to_string(),
784
                    JValue::List(vec![JValue::Object(vec![
785
                        ("name".to_string(), JValue::String("Foo".to_string())),
786
                        ("value".to_string(), JValue::String("Bar".to_string()))
787
                    ])])
788
                )
789
            ])
790
        );
791
    }
792

            
793
    #[test]
794
    pub fn test_response() {
795
        assert_eq!(
796
            Response {
797
                line_terminators: vec![],
798
                version: Version {
799
                    value: VersionValue::Version11,
800
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
801
                },
802
                space0: whitespace(),
803
                status: Status {
804
                    value: StatusValue::Specific(200),
805
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
806
                },
807
                space1: whitespace(),
808
                line_terminator0: line_terminator(),
809
                headers: vec![],
810
                sections: vec![],
811
                body: None,
812
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
813
            }
814
            .to_json(),
815
            JValue::Object(vec![
816
                (
817
                    "version".to_string(),
818
                    JValue::String("HTTP/1.1".to_string())
819
                ),
820
                ("status".to_string(), JValue::Number("200".to_string()))
821
            ])
822
        );
823
        assert_eq!(
824
            Response {
825
                line_terminators: vec![],
826
                version: Version {
827
                    value: VersionValue::VersionAny,
828
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
829
                },
830
                space0: whitespace(),
831
                status: Status {
832
                    value: StatusValue::Any,
833
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
834
                },
835
                space1: whitespace(),
836
                line_terminator0: line_terminator(),
837
                headers: vec![],
838
                sections: vec![],
839
                body: None,
840
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
841
            }
842
            .to_json(),
843
            JValue::Object(vec![])
844
        );
845
    }
846

            
847
    fn header_query() -> Query {
848
        Query {
849
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
850
            value: QueryValue::Header {
851
                space0: whitespace(),
852
                name: Template {
853
                    delimiter: None,
854
                    elements: vec![TemplateElement::String {
855
                        value: "Content-Length".to_string(),
856
                        source: "Content-Length".to_source(),
857
                    }],
858
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
859
                },
860
            },
861
        }
862
    }
863

            
864
    fn header_capture() -> Capture {
865
        Capture {
866
            line_terminators: vec![],
867
            space0: whitespace(),
868
            name: Template {
869
                delimiter: None,
870
                elements: vec![TemplateElement::String {
871
                    value: "size".to_string(),
872
                    source: "unused".to_source(),
873
                }],
874
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
875
            },
876
            space1: whitespace(),
877
            space2: whitespace(),
878
            query: header_query(),
879
            filters: vec![],
880
            space3: whitespace(),
881
            redact: false,
882
            line_terminator0: line_terminator(),
883
        }
884
    }
885

            
886
    fn header_assert() -> Assert {
887
        Assert {
888
            line_terminators: vec![],
889
            space0: whitespace(),
890
            query: header_query(),
891
            filters: vec![],
892
            space1: whitespace(),
893
            predicate: equal_int_predicate(10),
894
            line_terminator0: line_terminator(),
895
        }
896
    }
897

            
898
    fn equal_int_predicate(value: i64) -> Predicate {
899
        Predicate {
900
            not: false,
901
            space0: whitespace(),
902
            predicate_func: PredicateFunc {
903
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
904
                value: PredicateFuncValue::Equal {
905
                    space0: whitespace(),
906
                    value: PredicateValue::Number(Number::Integer(I64::new(
907
                        value,
908
                        value.to_string().to_source(),
909
                    ))),
910
                },
911
            },
912
        }
913
    }
914

            
915
    #[test]
916
    pub fn test_query() {
917
        assert_eq!(
918
            header_query().to_json(),
919
            JValue::Object(vec![
920
                ("type".to_string(), JValue::String("header".to_string())),
921
                (
922
                    "name".to_string(),
923
                    JValue::String("Content-Length".to_string())
924
                ),
925
            ])
926
        );
927
    }
928

            
929
    #[test]
930
    pub fn test_capture() {
931
        assert_eq!(
932
            header_capture().to_json(),
933
            JValue::Object(vec![
934
                ("name".to_string(), JValue::String("size".to_string())),
935
                (
936
                    "query".to_string(),
937
                    JValue::Object(vec![
938
                        ("type".to_string(), JValue::String("header".to_string())),
939
                        (
940
                            "name".to_string(),
941
                            JValue::String("Content-Length".to_string())
942
                        ),
943
                    ])
944
                ),
945
            ])
946
        );
947
    }
948

            
949
    #[test]
950
    pub fn test_predicate() {
951
        assert_eq!(
952
            equal_int_predicate(10).to_json(),
953
            JValue::Object(vec![
954
                ("type".to_string(), JValue::String("==".to_string())),
955
                ("value".to_string(), JValue::Number("10".to_string()))
956
            ]),
957
        );
958
    }
959

            
960
    #[test]
961
    pub fn test_assert() {
962
        assert_eq!(
963
            header_assert().to_json(),
964
            JValue::Object(vec![
965
                (
966
                    "query".to_string(),
967
                    JValue::Object(vec![
968
                        ("type".to_string(), JValue::String("header".to_string())),
969
                        (
970
                            "name".to_string(),
971
                            JValue::String("Content-Length".to_string())
972
                        ),
973
                    ])
974
                ),
975
                (
976
                    "predicate".to_string(),
977
                    JValue::Object(vec![
978
                        ("type".to_string(), JValue::String("==".to_string())),
979
                        ("value".to_string(), JValue::Number("10".to_string()))
980
                    ])
981
                )
982
            ]),
983
        );
984
    }
985
}