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

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

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

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

            
40
impl ToJson for HurlFile {
41
57
    fn to_json(&self) -> JValue {
42
57
        JValue::Object(vec![(
43
57
            "entries".to_string(),
44
214
            JValue::List(self.entries.iter().map(|e| e.to_json()).collect()),
45
57
        )])
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
195
        ];
68
195
        add_headers(&mut attributes, &self.headers);
69
195

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

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

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

            
117
impl ToJson for Response {
118
    /// Transforms this response to a JSON object.
119
87
    fn to_json(&self) -> JValue {
120
87
        let mut attributes = vec![];
121
87
        if let Some(v) = get_json_version(&self.version.value) {
122
12
            attributes.push(("version".to_string(), JValue::String(v)));
123
        }
124
87
        if let StatusValue::Specific(n) = self.status.value {
125
87
            attributes.push(("status".to_string(), JValue::Number(n.to_string())));
126
        }
127
87
        add_headers(&mut attributes, &self.headers);
128
87
        if !self.captures().is_empty() {
129
21
            let captures = self.captures().iter().map(|c| c.to_json()).collect();
130
9
            attributes.push(("captures".to_string(), JValue::List(captures)));
131
        }
132
87
        if !self.asserts().is_empty() {
133
363
            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
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
276
    fn to_json(&self) -> JValue {
309
276
        let value = match &self.kind {
310
6
            OptionKind::AwsSigV4(value) => JValue::String(value.to_string()),
311
6
            OptionKind::CaCertificate(filename) => JValue::String(filename.to_string()),
312
9
            OptionKind::ClientCert(filename) => JValue::String(filename.to_string()),
313
6
            OptionKind::ClientKey(filename) => JValue::String(filename.to_string()),
314
6
            OptionKind::Compressed(value) => value.to_json(),
315
6
            OptionKind::ConnectTo(value) => JValue::String(value.to_string()),
316
6
            OptionKind::ConnectTimeout(value) => value.to_json(),
317
12
            OptionKind::Delay(value) => value.to_json(),
318
6
            OptionKind::FollowLocation(value) => value.to_json(),
319
6
            OptionKind::FollowLocationTrusted(value) => value.to_json(),
320
6
            OptionKind::Header(value) => JValue::String(value.to_string()),
321
6
            OptionKind::Http10(value) => value.to_json(),
322
6
            OptionKind::Http11(value) => value.to_json(),
323
6
            OptionKind::Http2(value) => value.to_json(),
324
6
            OptionKind::Http3(value) => value.to_json(),
325
6
            OptionKind::Insecure(value) => value.to_json(),
326
6
            OptionKind::IpV4(value) => value.to_json(),
327
6
            OptionKind::IpV6(value) => value.to_json(),
328
6
            OptionKind::LimitRate(value) => value.to_json(),
329
6
            OptionKind::MaxRedirect(value) => value.to_json(),
330
6
            OptionKind::MaxTime(value) => value.to_json(),
331
6
            OptionKind::NetRc(value) => value.to_json(),
332
6
            OptionKind::NetRcFile(filename) => JValue::String(filename.to_string()),
333
6
            OptionKind::NetRcOptional(value) => value.to_json(),
334
6
            OptionKind::Output(filename) => JValue::String(filename.to_string()),
335
6
            OptionKind::PathAsIs(value) => value.to_json(),
336
6
            OptionKind::PinnedPublicKey(value) => JValue::String(value.to_string()),
337
6
            OptionKind::Proxy(value) => JValue::String(value.to_string()),
338
9
            OptionKind::Repeat(value) => value.to_json(),
339
6
            OptionKind::Resolve(value) => JValue::String(value.to_string()),
340
12
            OptionKind::Retry(value) => value.to_json(),
341
12
            OptionKind::RetryInterval(value) => value.to_json(),
342
6
            OptionKind::Skip(value) => value.to_json(),
343
6
            OptionKind::UnixSocket(value) => JValue::String(value.to_string()),
344
6
            OptionKind::User(value) => JValue::String(value.to_string()),
345
27
            OptionKind::Variable(value) => {
346
27
                JValue::String(format!("{}={}", value.name, value.value.to_source()))
347
            }
348
9
            OptionKind::Verbose(value) => value.to_json(),
349
6
            OptionKind::VeryVerbose(value) => value.to_json(),
350
        };
351

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
734
    use super::*;
735

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

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

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

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

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

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

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

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

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

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

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

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