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
207
            JValue::List(self.entries.iter().map(|e| e.to_json()).collect()),
45
54
        )])
46
    }
47
}
48

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

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

            
70
189
        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
189
        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
189
        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
189
        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
189
        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
189
        if let Some(body) = &self.body {
99
45
            attributes.push(("body".to_string(), body.to_json()));
100
        }
101

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

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

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

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

            
156
impl ToJson for Bytes {
157
66
    fn to_json(&self) -> JValue {
158
66
        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
36
            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
36
                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
6
                    } => "graphql",
207
                };
208
36
                JValue::Object(vec![
209
36
                    ("type".to_string(), JValue::String(lang.to_string())),
210
36
                    ("value".to_string(), JValue::String(multi.to_string())),
211
36
                ])
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
81
fn get_json_version(version_value: &VersionValue) -> Option<String> {
250
81
    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
69
        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 {
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
6
        ];
303
6
        JValue::Object(attributes)
304
    }
305
}
306

            
307
impl ToJson for EntryOption {
308
264
    fn to_json(&self) -> JValue {
309
264
        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::NetRc(value) => value.to_json(),
331
6
            OptionKind::NetRcFile(filename) => JValue::String(filename.to_string()),
332
6
            OptionKind::NetRcOptional(value) => value.to_json(),
333
6
            OptionKind::Output(filename) => JValue::String(filename.to_string()),
334
6
            OptionKind::PathAsIs(value) => value.to_json(),
335
6
            OptionKind::Proxy(value) => JValue::String(value.to_string()),
336
9
            OptionKind::Repeat(value) => value.to_json(),
337
6
            OptionKind::Resolve(value) => JValue::String(value.to_string()),
338
12
            OptionKind::Retry(value) => value.to_json(),
339
12
            OptionKind::RetryInterval(value) => value.to_json(),
340
6
            OptionKind::Skip(value) => value.to_json(),
341
6
            OptionKind::UnixSocket(value) => JValue::String(value.to_string()),
342
6
            OptionKind::User(value) => JValue::String(value.to_string()),
343
27
            OptionKind::Variable(value) => {
344
27
                JValue::String(format!("{}={}", value.name, value.value.to_source()))
345
            }
346
9
            OptionKind::Verbose(value) => value.to_json(),
347
6
            OptionKind::VeryVerbose(value) => value.to_json(),
348
        };
349

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
575
291
fn add_predicate_value(attributes: &mut Vec<(String, JValue)>, predicate_value: &PredicateValue) {
576
291
    let (value, encoding) = json_predicate_value(predicate_value);
577
291
    attributes.push(("value".to_string(), value));
578
291
    if let Some(encoding) = encoding {
579
33
        attributes.push(("encoding".to_string(), JValue::String(encoding)));
580
    }
581
}
582

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

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

            
627
impl ToJson for JsonListElement {
628
51
    fn to_json(&self) -> JValue {
629
51
        self.value.to_json()
630
    }
631
}
632

            
633
impl ToJson for Filter {
634
108
    fn to_json(&self) -> JValue {
635
108
        self.value.to_json()
636
    }
637
}
638

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

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

            
691
impl ToJson for Placeholder {
692
69
    fn to_json(&self) -> JValue {
693
69
        JValue::String(format!("{{{{{}}}}}", self))
694
    }
695
}
696

            
697
impl ToJson for Comment {
698
21
    fn to_json(&self) -> JValue {
699
21
        JValue::String(self.value.to_string())
700
    }
701
}
702

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

            
720
    use super::*;
721

            
722
    fn whitespace() -> Whitespace {
723
        Whitespace {
724
            value: String::new(),
725
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
726
        }
727
    }
728

            
729
    fn line_terminator() -> LineTerminator {
730
        LineTerminator {
731
            space0: whitespace(),
732
            comment: None,
733
            newline: whitespace(),
734
        }
735
    }
736

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

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

            
853
    fn header_query() -> Query {
854
        Query {
855
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
856
            value: QueryValue::Header {
857
                space0: whitespace(),
858
                name: Template::new(
859
                    None,
860
                    vec![TemplateElement::String {
861
                        value: "Content-Length".to_string(),
862
                        source: "Content-Length".to_source(),
863
                    }],
864
                    SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
865
                ),
866
            },
867
        }
868
    }
869

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

            
892
    fn header_assert() -> Assert {
893
        Assert {
894
            line_terminators: vec![],
895
            space0: whitespace(),
896
            query: header_query(),
897
            filters: vec![],
898
            space1: whitespace(),
899
            predicate: equal_int_predicate(10),
900
            line_terminator0: line_terminator(),
901
        }
902
    }
903

            
904
    fn equal_int_predicate(value: i64) -> Predicate {
905
        Predicate {
906
            not: false,
907
            space0: whitespace(),
908
            predicate_func: PredicateFunc {
909
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
910
                value: PredicateFuncValue::Equal {
911
                    space0: whitespace(),
912
                    value: PredicateValue::Number(Number::Integer(I64::new(
913
                        value,
914
                        value.to_string().to_source(),
915
                    ))),
916
                },
917
            },
918
        }
919
    }
920

            
921
    #[test]
922
    pub fn test_query() {
923
        assert_eq!(
924
            header_query().to_json(),
925
            JValue::Object(vec![
926
                ("type".to_string(), JValue::String("header".to_string())),
927
                (
928
                    "name".to_string(),
929
                    JValue::String("Content-Length".to_string())
930
                ),
931
            ])
932
        );
933
    }
934

            
935
    #[test]
936
    pub fn test_capture() {
937
        assert_eq!(
938
            header_capture().to_json(),
939
            JValue::Object(vec![
940
                ("name".to_string(), JValue::String("size".to_string())),
941
                (
942
                    "query".to_string(),
943
                    JValue::Object(vec![
944
                        ("type".to_string(), JValue::String("header".to_string())),
945
                        (
946
                            "name".to_string(),
947
                            JValue::String("Content-Length".to_string())
948
                        ),
949
                    ])
950
                ),
951
            ])
952
        );
953
    }
954

            
955
    #[test]
956
    pub fn test_predicate() {
957
        assert_eq!(
958
            equal_int_predicate(10).to_json(),
959
            JValue::Object(vec![
960
                ("type".to_string(), JValue::String("==".to_string())),
961
                ("value".to_string(), JValue::Number("10".to_string()))
962
            ]),
963
        );
964
    }
965

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