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::types::{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
298
            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
384
            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
294
    fn to_json(&self) -> JValue {
309
294
        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
18
            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::Negotiate(value) => value.to_json(),
332
6
            OptionKind::NetRc(value) => value.to_json(),
333
6
            OptionKind::NetRcFile(filename) => JValue::String(filename.to_string()),
334
6
            OptionKind::NetRcOptional(value) => value.to_json(),
335
6
            OptionKind::Ntlm(value) => value.to_json(),
336
6
            OptionKind::Output(filename) => JValue::String(filename.to_string()),
337
6
            OptionKind::PathAsIs(value) => value.to_json(),
338
6
            OptionKind::PinnedPublicKey(value) => JValue::String(value.to_string()),
339
6
            OptionKind::Proxy(value) => JValue::String(value.to_string()),
340
9
            OptionKind::Repeat(value) => value.to_json(),
341
6
            OptionKind::Resolve(value) => JValue::String(value.to_string()),
342
12
            OptionKind::Retry(value) => value.to_json(),
343
12
            OptionKind::RetryInterval(value) => value.to_json(),
344
6
            OptionKind::Skip(value) => value.to_json(),
345
6
            OptionKind::UnixSocket(value) => JValue::String(value.to_string()),
346
6
            OptionKind::User(value) => JValue::String(value.to_string()),
347
27
            OptionKind::Variable(value) => {
348
27
                JValue::String(format!("{}={}", value.name, value.value.to_source()))
349
            }
350
9
            OptionKind::Verbose(value) => value.to_json(),
351
6
            OptionKind::VeryVerbose(value) => value.to_json(),
352
        };
353

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

            
376
impl ToJson for BooleanOption {
377
111
    fn to_json(&self) -> JValue {
378
111
        match self {
379
57
            BooleanOption::Literal(value) => JValue::Boolean(*value),
380
54
            BooleanOption::Placeholder(placeholder) => placeholder.to_json(),
381
        }
382
    }
383
}
384

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
582
318
fn add_predicate_value(attributes: &mut Vec<(String, JValue)>, predicate_value: &PredicateValue) {
583
318
    let (value, encoding) = json_predicate_value(predicate_value);
584
318
    attributes.push(("value".to_string(), value));
585
318
    if let Some(encoding) = encoding {
586
36
        attributes.push(("encoding".to_string(), JValue::String(encoding)));
587
    }
588
}
589

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

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

            
634
impl ToJson for JsonListElement {
635
51
    fn to_json(&self) -> JValue {
636
51
        self.value.to_json()
637
    }
638
}
639

            
640
impl ToJson for Filter {
641
144
    fn to_json(&self) -> JValue {
642
144
        self.value.to_json()
643
    }
644
}
645

            
646
impl ToJson for FilterValue {
647
144
    fn to_json(&self) -> JValue {
648
144
        let mut attributes = vec![];
649
144
        let att_name = "type".to_string();
650
144
        let att_value = JValue::String(self.identifier().to_string());
651
144
        attributes.push((att_name, att_value));
652

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

            
713
impl ToJson for Placeholder {
714
78
    fn to_json(&self) -> JValue {
715
78
        JValue::String(format!("{{{{{self}}}}}"))
716
    }
717
}
718

            
719
impl ToJson for Comment {
720
27
    fn to_json(&self) -> JValue {
721
27
        JValue::String(self.value.to_string())
722
    }
723
}
724

            
725
impl ToJson for NaturalOption {
726
6
    fn to_json(&self) -> JValue {
727
6
        match self {
728
3
            NaturalOption::Literal(value) => JValue::Number(value.to_string()),
729
3
            NaturalOption::Placeholder(placeholder) => placeholder.to_json(),
730
        }
731
    }
732
}
733
#[cfg(test)]
734
pub mod tests {
735
    use hurl_core::ast::{
736
        LineTerminator, Method, Number, PredicateFunc, SourceInfo, Status, Template,
737
        TemplateElement, Version, Whitespace, I64,
738
    };
739
    use hurl_core::reader::Pos;
740
    use hurl_core::types::ToSource;
741

            
742
    use super::*;
743

            
744
    fn whitespace() -> Whitespace {
745
        Whitespace {
746
            value: String::new(),
747
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
748
        }
749
    }
750

            
751
    fn line_terminator() -> LineTerminator {
752
        LineTerminator {
753
            space0: whitespace(),
754
            comment: None,
755
            newline: whitespace(),
756
        }
757
    }
758

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

            
821
    #[test]
822
    pub fn test_response() {
823
        assert_eq!(
824
            Response {
825
                line_terminators: vec![],
826
                version: Version {
827
                    value: VersionValue::Version11,
828
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
829
                },
830
                space0: whitespace(),
831
                status: Status {
832
                    value: StatusValue::Specific(200),
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
                    "version".to_string(),
846
                    JValue::String("HTTP/1.1".to_string())
847
                ),
848
                ("status".to_string(), JValue::Number("200".to_string()))
849
            ])
850
        );
851
        assert_eq!(
852
            Response {
853
                line_terminators: vec![],
854
                version: Version {
855
                    value: VersionValue::VersionAny,
856
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
857
                },
858
                space0: whitespace(),
859
                status: Status {
860
                    value: StatusValue::Any,
861
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
862
                },
863
                space1: whitespace(),
864
                line_terminator0: line_terminator(),
865
                headers: vec![],
866
                sections: vec![],
867
                body: None,
868
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
869
            }
870
            .to_json(),
871
            JValue::Object(vec![])
872
        );
873
    }
874

            
875
    fn header_query() -> Query {
876
        Query {
877
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
878
            value: QueryValue::Header {
879
                space0: whitespace(),
880
                name: Template::new(
881
                    None,
882
                    vec![TemplateElement::String {
883
                        value: "Content-Length".to_string(),
884
                        source: "Content-Length".to_source(),
885
                    }],
886
                    SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
887
                ),
888
            },
889
        }
890
    }
891

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

            
914
    fn header_assert() -> Assert {
915
        Assert {
916
            line_terminators: vec![],
917
            space0: whitespace(),
918
            query: header_query(),
919
            filters: vec![],
920
            space1: whitespace(),
921
            predicate: equal_int_predicate(10),
922
            line_terminator0: line_terminator(),
923
        }
924
    }
925

            
926
    fn equal_int_predicate(value: i64) -> Predicate {
927
        Predicate {
928
            not: false,
929
            space0: whitespace(),
930
            predicate_func: PredicateFunc {
931
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
932
                value: PredicateFuncValue::Equal {
933
                    space0: whitespace(),
934
                    value: PredicateValue::Number(Number::Integer(I64::new(
935
                        value,
936
                        value.to_string().to_source(),
937
                    ))),
938
                },
939
            },
940
        }
941
    }
942

            
943
    #[test]
944
    pub fn test_query() {
945
        assert_eq!(
946
            header_query().to_json(),
947
            JValue::Object(vec![
948
                ("type".to_string(), JValue::String("header".to_string())),
949
                (
950
                    "name".to_string(),
951
                    JValue::String("Content-Length".to_string())
952
                ),
953
            ])
954
        );
955
    }
956

            
957
    #[test]
958
    pub fn test_capture() {
959
        assert_eq!(
960
            header_capture().to_json(),
961
            JValue::Object(vec![
962
                ("name".to_string(), JValue::String("size".to_string())),
963
                (
964
                    "query".to_string(),
965
                    JValue::Object(vec![
966
                        ("type".to_string(), JValue::String("header".to_string())),
967
                        (
968
                            "name".to_string(),
969
                            JValue::String("Content-Length".to_string())
970
                        ),
971
                    ])
972
                ),
973
            ])
974
        );
975
    }
976

            
977
    #[test]
978
    pub fn test_predicate() {
979
        assert_eq!(
980
            equal_int_predicate(10).to_json(),
981
            JValue::Object(vec![
982
                ("type".to_string(), JValue::String("==".to_string())),
983
                ("value".to_string(), JValue::Number("10".to_string()))
984
            ]),
985
        );
986
    }
987

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