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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
721
    use super::*;
722

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

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

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

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

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

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

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

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

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

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

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

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