1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2024 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
use hurl_core::typing::{Count, Duration};
22

            
23
use super::serialize_json::*;
24

            
25
48
pub fn format(hurl_file: &HurlFile) -> String {
26
48
    hurl_file.to_json().format()
27
}
28

            
29
pub trait ToJson {
30
    fn to_json(&self) -> JValue;
31
}
32

            
33
impl ToJson for HurlFile {
34
48
    fn to_json(&self) -> JValue {
35
48
        JValue::Object(vec![(
36
48
            "entries".to_string(),
37
184
            JValue::List(self.entries.iter().map(|e| e.to_json()).collect()),
38
48
        )])
39
    }
40
}
41

            
42
impl ToJson for Entry {
43
168
    fn to_json(&self) -> JValue {
44
168
        let mut attributes = vec![("request".to_string(), self.request.to_json())];
45
168
        if let Some(response) = &self.response {
46
78
            attributes.push(("response".to_string(), response.to_json()));
47
        }
48
168
        JValue::Object(attributes)
49
    }
50
}
51

            
52
impl ToJson for Request {
53
168
    fn to_json(&self) -> JValue {
54
168
        let mut attributes = vec![
55
168
            (
56
168
                "method".to_string(),
57
168
                JValue::String(self.method.to_string()),
58
168
            ),
59
168
            ("url".to_string(), JValue::String(self.url.to_string())),
60
168
        ];
61
168
        add_headers(&mut attributes, &self.headers);
62
168

            
63
168
        if !self.querystring_params().is_empty() {
64
3
            let params = self
65
3
                .querystring_params()
66
3
                .iter()
67
10
                .map(|p| p.to_json())
68
3
                .collect();
69
3
            attributes.push(("query_string_params".to_string(), JValue::List(params)));
70
        }
71
168
        if !self.form_params().is_empty() {
72
13
            let params = self.form_params().iter().map(|p| p.to_json()).collect();
73
3
            attributes.push(("form_params".to_string(), JValue::List(params)));
74
        }
75
168
        if !self.multipart_form_data().is_empty() {
76
3
            let params = self
77
3
                .multipart_form_data()
78
3
                .iter()
79
10
                .map(|p| p.to_json())
80
3
                .collect();
81
3
            attributes.push(("multipart_form_data".to_string(), JValue::List(params)));
82
        }
83
168
        if !self.cookies().is_empty() {
84
7
            let cookies = self.cookies().iter().map(|c| c.to_json()).collect();
85
3
            attributes.push(("cookies".to_string(), JValue::List(cookies)));
86
        }
87
168
        if !self.options().is_empty() {
88
247
            let options = self.options().iter().map(|c| c.to_json()).collect();
89
12
            attributes.push(("options".to_string(), JValue::List(options)));
90
        }
91
168
        if let Some(body) = &self.body {
92
39
            attributes.push(("body".to_string(), body.to_json()));
93
        }
94

            
95
        // Request comments (can be used to check custom commands)
96
168
        let comments: Vec<_> = self
97
168
            .line_terminators
98
168
            .iter()
99
205
            .filter_map(|l| l.comment.as_ref())
100
168
            .collect();
101
168
        if !comments.is_empty() {
102
18
            let comments = comments.iter().map(|c| c.to_json()).collect();
103
9
            attributes.push(("comments".to_string(), JValue::List(comments)));
104
        }
105

            
106
168
        JValue::Object(attributes)
107
    }
108
}
109

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

            
136
246
fn add_headers(attributes: &mut Vec<(String, JValue)>, headers: &[Header]) {
137
246
    if !headers.is_empty() {
138
62
        let headers = JValue::List(headers.iter().map(|h| h.to_json()).collect());
139
24
        attributes.push(("headers".to_string(), headers));
140
    }
141
}
142

            
143
impl ToJson for Body {
144
60
    fn to_json(&self) -> JValue {
145
60
        self.value.to_json()
146
    }
147
}
148

            
149
impl ToJson for Bytes {
150
60
    fn to_json(&self) -> JValue {
151
60
        match self {
152
6
            Bytes::Base64(value) => value.to_json(),
153
3
            Bytes::Hex(value) => value.to_json(),
154
3
            Bytes::File(value) => value.to_json(),
155
6
            Bytes::Json(value) => JValue::Object(vec![
156
6
                ("type".to_string(), JValue::String("json".to_string())),
157
6
                ("value".to_string(), value.to_json()),
158
6
            ]),
159
3
            Bytes::Xml(value) => JValue::Object(vec![
160
3
                ("type".to_string(), JValue::String("xml".to_string())),
161
3
                ("value".to_string(), JValue::String(value.clone())),
162
3
            ]),
163
9
            Bytes::OnelineString(value) => JValue::Object(vec![
164
9
                ("type".to_string(), JValue::String("text".to_string())),
165
9
                ("value".to_string(), JValue::String(value.to_string())),
166
9
            ]),
167
30
            Bytes::MultilineString(multi) => {
168
                // TODO: check these values. Maybe we want to have the same
169
                // export when using:
170
                //
171
                // ~~~
172
                // GET https://foo.com
173
                // ```base64
174
                // SGVsbG8gd29ybGQ=
175
                // ```
176
                //
177
                // or
178
                //
179
                // ~~~
180
                // GET https://foo.com
181
                // base64,SGVsbG8gd29ybGQ=;
182
                // ~~~
183
30
                let lang = match multi {
184
                    MultilineString {
185
                        kind: MultilineStringKind::Text(_),
186
                        ..
187
12
                    } => "text",
188
                    MultilineString {
189
                        kind: MultilineStringKind::Json(_),
190
                        ..
191
6
                    } => "json",
192
                    MultilineString {
193
                        kind: MultilineStringKind::Xml(_),
194
                        ..
195
6
                    } => "xml",
196
                    MultilineString {
197
                        kind: MultilineStringKind::GraphQl(_),
198
                        ..
199
6
                    } => "graphql",
200
                };
201
30
                JValue::Object(vec![
202
30
                    ("type".to_string(), JValue::String(lang.to_string())),
203
30
                    ("value".to_string(), JValue::String(multi.to_string())),
204
30
                ])
205
            }
206
        }
207
    }
208
}
209

            
210
impl ToJson for Base64 {
211
6
    fn to_json(&self) -> JValue {
212
6
        let value = general_purpose::STANDARD.encode(&self.value);
213
6
        JValue::Object(vec![
214
6
            ("encoding".to_string(), JValue::String("base64".to_string())),
215
6
            ("value".to_string(), JValue::String(value)),
216
6
        ])
217
    }
218
}
219

            
220
impl ToJson for Hex {
221
3
    fn to_json(&self) -> JValue {
222
3
        let value = general_purpose::STANDARD.encode(&self.value);
223
3
        JValue::Object(vec![
224
3
            ("encoding".to_string(), JValue::String("base64".to_string())),
225
3
            ("value".to_string(), JValue::String(value)),
226
3
        ])
227
    }
228
}
229

            
230
impl ToJson for File {
231
6
    fn to_json(&self) -> JValue {
232
6
        JValue::Object(vec![
233
6
            ("type".to_string(), JValue::String("file".to_string())),
234
6
            (
235
6
                "filename".to_string(),
236
6
                JValue::String(self.filename.to_string()),
237
6
            ),
238
6
        ])
239
    }
240
}
241

            
242
78
fn get_json_version(version_value: &VersionValue) -> Option<String> {
243
78
    match version_value {
244
3
        VersionValue::Version1 => Some("HTTP/1.0".to_string()),
245
3
        VersionValue::Version11 => Some("HTTP/1.1".to_string()),
246
3
        VersionValue::Version2 => Some("HTTP/2".to_string()),
247
3
        VersionValue::Version3 => Some("HTTP/3".to_string()),
248
63
        VersionValue::VersionAny => None,
249
3
        VersionValue::VersionAnyLegacy => None,
250
    }
251
}
252

            
253
impl ToJson for KeyValue {
254
78
    fn to_json(&self) -> JValue {
255
78
        let attributes = vec![
256
78
            ("name".to_string(), JValue::String(self.key.to_string())),
257
78
            ("value".to_string(), JValue::String(self.value.to_string())),
258
78
        ];
259
78
        JValue::Object(attributes)
260
    }
261
}
262

            
263
impl ToJson for MultipartParam {
264
9
    fn to_json(&self) -> JValue {
265
9
        match self {
266
3
            MultipartParam::Param(param) => param.to_json(),
267
6
            MultipartParam::FileParam(param) => param.to_json(),
268
        }
269
    }
270
}
271

            
272
impl ToJson for FileParam {
273
6
    fn to_json(&self) -> JValue {
274
6
        let mut attributes = vec![
275
6
            ("name".to_string(), JValue::String(self.key.to_string())),
276
6
            (
277
6
                "filename".to_string(),
278
6
                JValue::String(self.value.filename.to_string()),
279
6
            ),
280
6
        ];
281
6
        if let Some(content_type) = self.value.content_type.clone() {
282
3
            attributes.push(("content_type".to_string(), JValue::String(content_type)));
283
        }
284
6
        JValue::Object(attributes)
285
    }
286
}
287

            
288
impl ToJson for Cookie {
289
6
    fn to_json(&self) -> JValue {
290
6
        let attributes = vec![
291
6
            ("name".to_string(), JValue::String(self.name.to_string())),
292
6
            ("value".to_string(), JValue::String(self.value.to_string())),
293
6
        ];
294
6
        JValue::Object(attributes)
295
    }
296
}
297

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

            
338
        // If the value contains the unit such as `{ "value": 10, "unit": "second" }`
339
        // The JSON for this option should still have one level
340
        // for example: { "name": "delay", "value": 10, "unit", "second" }
341
243
        let attributes = if let JValue::Object(mut attributes) = value {
342
12
            attributes.push((
343
12
                "name".to_string(),
344
12
                JValue::String(self.kind.name().to_string()),
345
12
            ));
346
12
            attributes
347
        } else {
348
231
            vec![
349
231
                (
350
231
                    "name".to_string(),
351
231
                    JValue::String(self.kind.name().to_string()),
352
231
                ),
353
231
                ("value".to_string(), value),
354
231
            ]
355
        };
356
243
        JValue::Object(attributes)
357
    }
358
}
359

            
360
impl ToJson for BooleanOption {
361
99
    fn to_json(&self) -> JValue {
362
99
        match self {
363
51
            BooleanOption::Literal(value) => JValue::Boolean(*value),
364
48
            BooleanOption::Expression(expr) => expr.to_json(),
365
        }
366
    }
367
}
368

            
369
impl ToJson for CountOption {
370
27
    fn to_json(&self) -> JValue {
371
27
        match self {
372
18
            CountOption::Literal(value) => value.to_json(),
373
9
            CountOption::Expression(expr) => expr.to_json(),
374
        }
375
    }
376
}
377

            
378
impl ToJson for Count {
379
18
    fn to_json(&self) -> JValue {
380
18
        match self {
381
12
            Count::Finite(n) => JValue::Number(n.to_string()),
382
6
            Count::Infinite => JValue::Number("-1".to_string()),
383
        }
384
    }
385
}
386

            
387
impl ToJson for DurationOption {
388
24
    fn to_json(&self) -> JValue {
389
24
        match self {
390
18
            DurationOption::Literal(value) => value.to_json(),
391
6
            DurationOption::Expression(expr) => expr.to_json(),
392
        }
393
    }
394
}
395

            
396
impl ToJson for Duration {
397
18
    fn to_json(&self) -> JValue {
398
18
        if let Some(unit) = self.unit {
399
12
            let mut attributes =
400
12
                vec![("value".to_string(), JValue::Number(self.value.to_string()))];
401
12
            attributes.push(("unit".to_string(), JValue::String(unit.to_string())));
402
12
            JValue::Object(attributes)
403
        } else {
404
6
            JValue::Number(self.value.to_string())
405
        }
406
    }
407
}
408

            
409
impl ToJson for Capture {
410
6
    fn to_json(&self) -> JValue {
411
6
        let mut attributes = vec![
412
6
            ("name".to_string(), JValue::String(self.name.to_string())),
413
6
            ("query".to_string(), self.query.to_json()),
414
6
        ];
415
6
        if !self.filters.is_empty() {
416
4
            let filters = JValue::List(self.filters.iter().map(|(_, f)| f.to_json()).collect());
417
3
            attributes.push(("filters".to_string(), filters));
418
        }
419
6
        JValue::Object(attributes)
420
    }
421
}
422

            
423
impl ToJson for Assert {
424
303
    fn to_json(&self) -> JValue {
425
303
        let mut attributes = vec![("query".to_string(), self.query.to_json())];
426
303
        if !self.filters.is_empty() {
427
111
            let filters = JValue::List(self.filters.iter().map(|(_, f)| f.to_json()).collect());
428
72
            attributes.push(("filters".to_string(), filters));
429
        }
430
303
        attributes.push(("predicate".to_string(), self.predicate.to_json()));
431
303
        JValue::Object(attributes)
432
    }
433
}
434

            
435
impl ToJson for Query {
436
309
    fn to_json(&self) -> JValue {
437
309
        let attributes = query_value_attributes(&self.value);
438
309
        JValue::Object(attributes)
439
    }
440
}
441

            
442
309
fn query_value_attributes(query_value: &QueryValue) -> Vec<(String, JValue)> {
443
309
    let mut attributes = vec![];
444
309
    match query_value {
445
3
        QueryValue::Status => {
446
3
            attributes.push(("type".to_string(), JValue::String("status".to_string())));
447
        }
448
3
        QueryValue::Url => {
449
3
            attributes.push(("type".to_string(), JValue::String("url".to_string())));
450
        }
451
30
        QueryValue::Body => {
452
30
            attributes.push(("type".to_string(), JValue::String("body".to_string())));
453
        }
454
189
        QueryValue::Jsonpath { expr, .. } => {
455
189
            attributes.push(("type".to_string(), JValue::String("jsonpath".to_string())));
456
189
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
457
        }
458
6
        QueryValue::Header { name, .. } => {
459
6
            attributes.push(("type".to_string(), JValue::String("header".to_string())));
460
6
            attributes.push(("name".to_string(), JValue::String(name.to_string())));
461
        }
462
6
        QueryValue::Cookie { expr, .. } => {
463
6
            attributes.push(("type".to_string(), JValue::String("cookie".to_string())));
464
6
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
465
        }
466
3
        QueryValue::Xpath { expr, .. } => {
467
3
            attributes.push(("type".to_string(), JValue::String("xpath".to_string())));
468
3
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
469
        }
470
3
        QueryValue::Regex { value, .. } => {
471
3
            attributes.push(("type".to_string(), JValue::String("regex".to_string())));
472
3
            attributes.push(("expr".to_string(), value.to_json()));
473
        }
474
9
        QueryValue::Variable { name, .. } => {
475
9
            attributes.push(("type".to_string(), JValue::String("variable".to_string())));
476
9
            attributes.push(("name".to_string(), JValue::String(name.to_string())));
477
        }
478
3
        QueryValue::Duration => {
479
3
            attributes.push(("type".to_string(), JValue::String("duration".to_string())));
480
        }
481
18
        QueryValue::Bytes => {
482
18
            attributes.push(("type".to_string(), JValue::String("bytes".to_string())));
483
        }
484
3
        QueryValue::Sha256 => {
485
3
            attributes.push(("type".to_string(), JValue::String("sha256".to_string())));
486
        }
487
3
        QueryValue::Md5 => {
488
3
            attributes.push(("type".to_string(), JValue::String("md5".to_string())));
489
        }
490
        QueryValue::Certificate {
491
30
            attribute_name: field,
492
30
            ..
493
30
        } => {
494
30
            attributes.push((
495
30
                "type".to_string(),
496
30
                JValue::String("certificate".to_string()),
497
30
            ));
498
30
            attributes.push(("expr".to_string(), field.to_json()));
499
        }
500
    };
501
309
    attributes
502
}
503

            
504
impl ToJson for RegexValue {
505
21
    fn to_json(&self) -> JValue {
506
21
        match self {
507
18
            RegexValue::Template(template) => JValue::String(template.to_string()),
508
3
            RegexValue::Regex(regex) => regex.to_json(),
509
        }
510
    }
511
}
512

            
513
impl ToJson for Regex {
514
3
    fn to_json(&self) -> JValue {
515
3
        let attributes = vec![
516
3
            ("type".to_string(), JValue::String("regex".to_string())),
517
3
            ("value".to_string(), JValue::String(self.to_string())),
518
3
        ];
519
3
        JValue::Object(attributes)
520
    }
521
}
522

            
523
impl ToJson for CertificateAttributeName {
524
30
    fn to_json(&self) -> JValue {
525
30
        let value = match self {
526
3
            CertificateAttributeName::Subject => "Subject",
527
3
            CertificateAttributeName::Issuer => "Issuer",
528
9
            CertificateAttributeName::StartDate => "Start-Date",
529
12
            CertificateAttributeName::ExpireDate => "Expire-Date",
530
3
            CertificateAttributeName::SerialNumber => "Serial-Number",
531
        };
532
30
        JValue::String(value.to_string())
533
    }
534
}
535

            
536
impl ToJson for Predicate {
537
303
    fn to_json(&self) -> JValue {
538
303
        let mut attributes = vec![];
539
303
        if self.not {
540
3
            attributes.push(("not".to_string(), JValue::Boolean(true)));
541
        }
542
303
        match self.predicate_func.value.clone() {
543
201
            PredicateFuncValue::Equal { value, .. } => {
544
201
                attributes.push(("type".to_string(), JValue::String("equal".to_string())));
545
201
                add_predicate_value(&mut attributes, value);
546
            }
547
9
            PredicateFuncValue::NotEqual { value, .. } => {
548
9
                attributes.push(("type".to_string(), JValue::String("not-equal".to_string())));
549
9
                add_predicate_value(&mut attributes, value);
550
            }
551
9
            PredicateFuncValue::GreaterThan { value, .. } => {
552
9
                attributes.push(("type".to_string(), JValue::String("greater".to_string())));
553
9
                add_predicate_value(&mut attributes, value);
554
            }
555
3
            PredicateFuncValue::GreaterThanOrEqual { value, .. } => {
556
3
                attributes.push((
557
3
                    "type".to_string(),
558
3
                    JValue::String("greater-or-equal".to_string()),
559
3
                ));
560
3
                add_predicate_value(&mut attributes, value);
561
            }
562
9
            PredicateFuncValue::LessThan { value, .. } => {
563
9
                attributes.push(("type".to_string(), JValue::String("less".to_string())));
564
9
                add_predicate_value(&mut attributes, value);
565
            }
566
3
            PredicateFuncValue::LessThanOrEqual { value, .. } => {
567
3
                attributes.push((
568
3
                    "type".to_string(),
569
3
                    JValue::String("less-or-equal".to_string()),
570
3
                ));
571
3
                add_predicate_value(&mut attributes, value);
572
            }
573
9
            PredicateFuncValue::StartWith { value, .. } => {
574
9
                attributes.push(("type".to_string(), JValue::String("start-with".to_string())));
575
9
                add_predicate_value(&mut attributes, value);
576
            }
577
6
            PredicateFuncValue::EndWith { value, .. } => {
578
6
                attributes.push(("type".to_string(), JValue::String("end-with".to_string())));
579
6
                add_predicate_value(&mut attributes, value);
580
            }
581
6
            PredicateFuncValue::Contain { value, .. } => {
582
6
                attributes.push(("type".to_string(), JValue::String("contain".to_string())));
583
6
                add_predicate_value(&mut attributes, value);
584
            }
585
3
            PredicateFuncValue::Include { value, .. } => {
586
3
                attributes.push(("type".to_string(), JValue::String("include".to_string())));
587
3
                add_predicate_value(&mut attributes, value);
588
            }
589
6
            PredicateFuncValue::Match { value, .. } => {
590
6
                attributes.push(("type".to_string(), JValue::String("match".to_string())));
591
6
                add_predicate_value(&mut attributes, value);
592
            }
593
3
            PredicateFuncValue::IsInteger => {
594
3
                attributes.push(("type".to_string(), JValue::String("isInteger".to_string())));
595
            }
596
3
            PredicateFuncValue::IsFloat => {
597
3
                attributes.push(("type".to_string(), JValue::String("isFloat".to_string())));
598
            }
599
3
            PredicateFuncValue::IsBoolean => {
600
3
                attributes.push(("type".to_string(), JValue::String("isBoolean".to_string())));
601
            }
602
3
            PredicateFuncValue::IsString => {
603
3
                attributes.push(("type".to_string(), JValue::String("isString".to_string())));
604
            }
605
3
            PredicateFuncValue::IsCollection => {
606
3
                attributes.push((
607
3
                    "type".to_string(),
608
3
                    JValue::String("isCollection".to_string()),
609
3
                ));
610
            }
611
9
            PredicateFuncValue::IsDate => {
612
9
                attributes.push(("type".to_string(), JValue::String("isDate".to_string())));
613
            }
614
3
            PredicateFuncValue::IsIsoDate => {
615
3
                attributes.push(("type".to_string(), JValue::String("isIsoDate".to_string())));
616
            }
617
6
            PredicateFuncValue::Exist => {
618
6
                attributes.push(("type".to_string(), JValue::String("exist".to_string())));
619
            }
620
3
            PredicateFuncValue::IsEmpty => {
621
3
                attributes.push(("type".to_string(), JValue::String("isEmpty".to_string())));
622
            }
623
3
            PredicateFuncValue::IsNumber => {
624
3
                attributes.push(("type".to_string(), JValue::String("isNumber".to_string())));
625
            }
626
        }
627
303
        JValue::Object(attributes)
628
    }
629
}
630

            
631
264
fn add_predicate_value(attributes: &mut Vec<(String, JValue)>, predicate_value: PredicateValue) {
632
264
    let (value, encoding) = json_predicate_value(predicate_value);
633
264
    attributes.push(("value".to_string(), value));
634
264
    if let Some(encoding) = encoding {
635
27
        attributes.push(("encoding".to_string(), JValue::String(encoding)));
636
    }
637
}
638

            
639
264
fn json_predicate_value(predicate_value: PredicateValue) -> (JValue, Option<String>) {
640
264
    match predicate_value {
641
105
        PredicateValue::String(value) => (JValue::String(value.to_string()), None),
642
15
        PredicateValue::MultilineString(value) => (JValue::String(value.value().to_string()), None),
643
3
        PredicateValue::Bool(value) => (JValue::Boolean(value), None),
644
3
        PredicateValue::Null => (JValue::Null, None),
645
105
        PredicateValue::Number(value) => (JValue::Number(value.to_string()), None),
646
3
        PredicateValue::File(value) => (value.to_json(), None),
647
21
        PredicateValue::Hex(value) => {
648
21
            let base64_string = general_purpose::STANDARD.encode(value.value);
649
21
            (JValue::String(base64_string), Some("base64".to_string()))
650
        }
651
3
        PredicateValue::Base64(value) => {
652
3
            let base64_string = general_purpose::STANDARD.encode(value.value);
653
3
            (JValue::String(base64_string), Some("base64".to_string()))
654
        }
655
3
        PredicateValue::Expression(value) => (JValue::String(value.to_string()), None),
656
3
        PredicateValue::Regex(value) => {
657
3
            (JValue::String(value.to_string()), Some("regex".to_string()))
658
        }
659
    }
660
}
661

            
662
impl ToJson for JsonValue {
663
102
    fn to_json(&self) -> JValue {
664
102
        match self {
665
3
            JsonValue::Null => JValue::Null,
666
45
            JsonValue::Number(s) => JValue::Number(s.to_string()),
667
18
            JsonValue::String(s) => JValue::String(s.to_string()),
668
3
            JsonValue::Boolean(v) => JValue::Boolean(*v),
669
15
            JsonValue::List { elements, .. } => {
670
56
                JValue::List(elements.iter().map(|e| e.to_json()).collect())
671
            }
672
15
            JsonValue::Object { elements, .. } => JValue::Object(
673
15
                elements
674
15
                    .iter()
675
50
                    .map(|elem| (elem.name.to_string(), elem.value.to_json()))
676
15
                    .collect(),
677
15
            ),
678
3
            JsonValue::Expression(exp) => JValue::String(format!("{{{{{exp}}}}}")),
679
        }
680
    }
681
}
682

            
683
impl ToJson for JsonListElement {
684
51
    fn to_json(&self) -> JValue {
685
51
        self.value.to_json()
686
    }
687
}
688

            
689
impl ToJson for Filter {
690
90
    fn to_json(&self) -> JValue {
691
90
        self.value.to_json()
692
    }
693
}
694

            
695
impl ToJson for FilterValue {
696
90
    fn to_json(&self) -> JValue {
697
90
        let mut attributes = vec![];
698
90
        match self {
699
15
            FilterValue::Count => {
700
15
                attributes.push(("type".to_string(), JValue::String("count".to_string())));
701
            }
702
3
            FilterValue::DaysAfterNow => {
703
3
                attributes.push((
704
3
                    "type".to_string(),
705
3
                    JValue::String("daysAfterNow".to_string()),
706
3
                ));
707
            }
708
6
            FilterValue::DaysBeforeNow => {
709
6
                attributes.push((
710
6
                    "type".to_string(),
711
6
                    JValue::String("daysBeforeNow".to_string()),
712
6
                ));
713
            }
714
6
            FilterValue::Decode { encoding, .. } => {
715
6
                attributes.push(("type".to_string(), JValue::String("decode".to_string())));
716
6
                attributes.push(("encoding".to_string(), JValue::String(encoding.to_string())));
717
            }
718
9
            FilterValue::Format { fmt, .. } => {
719
9
                attributes.push(("type".to_string(), JValue::String("format".to_string())));
720
9
                attributes.push(("fmt".to_string(), JValue::String(fmt.to_string())));
721
            }
722
3
            FilterValue::JsonPath { expr, .. } => {
723
3
                attributes.push(("type".to_string(), JValue::String("jsonpath".to_string())));
724
3
                attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
725
            }
726
3
            FilterValue::Nth { n, .. } => {
727
3
                attributes.push(("type".to_string(), JValue::String("nth".to_string())));
728
3
                attributes.push(("n".to_string(), JValue::Number(n.to_string())));
729
            }
730
3
            FilterValue::HtmlEscape => {
731
3
                attributes.push(("type".to_string(), JValue::String("htmlEscape".to_string())));
732
            }
733
3
            FilterValue::HtmlUnescape => {
734
3
                attributes.push((
735
3
                    "type".to_string(),
736
3
                    JValue::String("htmlUnescape".to_string()),
737
3
                ));
738
            }
739
3
            FilterValue::Regex { value, .. } => {
740
3
                attributes.push(("type".to_string(), JValue::String("regex".to_string())));
741
3
                attributes.push(("expr".to_string(), value.to_json()));
742
            }
743
            FilterValue::Replace {
744
15
                old_value,
745
15
                new_value,
746
15
                ..
747
15
            } => {
748
15
                attributes.push(("type".to_string(), JValue::String("replace".to_string())));
749
15
                attributes.push(("old_value".to_string(), old_value.to_json()));
750
15
                attributes.push((
751
15
                    "new_value".to_string(),
752
15
                    JValue::String(new_value.to_string()),
753
15
                ));
754
            }
755
3
            FilterValue::UrlEncode => {
756
3
                attributes.push(("type".to_string(), JValue::String("urlEncode".to_string())));
757
            }
758
3
            FilterValue::UrlDecode => {
759
3
                attributes.push(("type".to_string(), JValue::String("urlDecode".to_string())));
760
            }
761
3
            FilterValue::Split { sep, .. } => {
762
3
                attributes.push(("type".to_string(), JValue::String("split".to_string())));
763
3
                attributes.push(("sep".to_string(), JValue::String(sep.to_string())));
764
            }
765
3
            FilterValue::ToDate { fmt, .. } => {
766
3
                attributes.push(("type".to_string(), JValue::String("toDate".to_string())));
767
3
                attributes.push(("fmt".to_string(), JValue::String(fmt.to_string())));
768
            }
769
3
            FilterValue::ToFloat => {
770
3
                attributes.push(("type".to_string(), JValue::String("toFloat".to_string())));
771
            }
772
3
            FilterValue::ToInt => {
773
3
                attributes.push(("type".to_string(), JValue::String("toInt".to_string())));
774
            }
775
3
            FilterValue::XPath { expr, .. } => {
776
3
                attributes.push(("type".to_string(), JValue::String("xpath".to_string())));
777
3
                attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
778
            }
779
        }
780
90
        JValue::Object(attributes)
781
    }
782
}
783

            
784
impl ToJson for Expr {
785
63
    fn to_json(&self) -> JValue {
786
63
        JValue::String(format!("{{{{{}}}}}", self))
787
    }
788
}
789

            
790
impl ToJson for Comment {
791
15
    fn to_json(&self) -> JValue {
792
15
        JValue::String(self.value.to_string())
793
    }
794
}
795

            
796
#[cfg(test)]
797
pub mod tests {
798
    use hurl_core::reader::Pos;
799

            
800
    use super::*;
801

            
802
    fn whitespace() -> Whitespace {
803
        Whitespace {
804
            value: String::new(),
805
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
806
        }
807
    }
808

            
809
    fn line_terminator() -> LineTerminator {
810
        LineTerminator {
811
            space0: whitespace(),
812
            comment: None,
813
            newline: whitespace(),
814
        }
815
    }
816

            
817
    #[test]
818
    pub fn test_request() {
819
        assert_eq!(
820
            Request {
821
                line_terminators: vec![],
822
                space0: whitespace(),
823
                method: Method("GET".to_string()),
824
                space1: whitespace(),
825
                url: Template {
826
                    delimiter: None,
827
                    elements: vec![TemplateElement::String {
828
                        value: "http://example.com".to_string(),
829
                        encoded: "not_used".to_string(),
830
                    }],
831
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
832
                },
833
                line_terminator0: line_terminator(),
834
                headers: vec![KeyValue {
835
                    line_terminators: vec![],
836
                    space0: whitespace(),
837
                    key: Template {
838
                        delimiter: None,
839
                        elements: vec![TemplateElement::String {
840
                            value: "Foo".to_string(),
841
                            encoded: "unused".to_string(),
842
                        }],
843
                        source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
844
                    },
845
                    space1: whitespace(),
846
                    space2: whitespace(),
847
                    value: Template {
848
                        delimiter: None,
849
                        elements: vec![TemplateElement::String {
850
                            value: "Bar".to_string(),
851
                            encoded: "unused".to_string(),
852
                        }],
853
                        source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
854
                    },
855
                    line_terminator0: line_terminator(),
856
                }],
857
                sections: vec![],
858
                body: None,
859
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
860
            }
861
            .to_json(),
862
            JValue::Object(vec![
863
                ("method".to_string(), JValue::String("GET".to_string())),
864
                (
865
                    "url".to_string(),
866
                    JValue::String("http://example.com".to_string())
867
                ),
868
                (
869
                    "headers".to_string(),
870
                    JValue::List(vec![JValue::Object(vec![
871
                        ("name".to_string(), JValue::String("Foo".to_string())),
872
                        ("value".to_string(), JValue::String("Bar".to_string()))
873
                    ])])
874
                )
875
            ])
876
        );
877
    }
878

            
879
    #[test]
880
    pub fn test_response() {
881
        assert_eq!(
882
            Response {
883
                line_terminators: vec![],
884
                version: Version {
885
                    value: VersionValue::Version11,
886
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
887
                },
888
                space0: whitespace(),
889
                status: Status {
890
                    value: StatusValue::Specific(200),
891
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
892
                },
893
                space1: whitespace(),
894
                line_terminator0: line_terminator(),
895
                headers: vec![],
896
                sections: vec![],
897
                body: None,
898
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
899
            }
900
            .to_json(),
901
            JValue::Object(vec![
902
                (
903
                    "version".to_string(),
904
                    JValue::String("HTTP/1.1".to_string())
905
                ),
906
                ("status".to_string(), JValue::Number("200".to_string()))
907
            ])
908
        );
909
        assert_eq!(
910
            Response {
911
                line_terminators: vec![],
912
                version: Version {
913
                    value: VersionValue::VersionAny,
914
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
915
                },
916
                space0: whitespace(),
917
                status: Status {
918
                    value: StatusValue::Any,
919
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
920
                },
921
                space1: whitespace(),
922
                line_terminator0: line_terminator(),
923
                headers: vec![],
924
                sections: vec![],
925
                body: None,
926
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
927
            }
928
            .to_json(),
929
            JValue::Object(vec![])
930
        );
931
    }
932

            
933
    fn header_query() -> Query {
934
        Query {
935
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
936
            value: QueryValue::Header {
937
                space0: whitespace(),
938
                name: Template {
939
                    delimiter: None,
940
                    elements: vec![TemplateElement::String {
941
                        value: "Content-Length".to_string(),
942
                        encoded: "10".to_string(),
943
                    }],
944
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
945
                },
946
            },
947
        }
948
    }
949

            
950
    fn header_capture() -> Capture {
951
        Capture {
952
            line_terminators: vec![],
953
            space0: whitespace(),
954
            name: Template {
955
                delimiter: None,
956
                elements: vec![TemplateElement::String {
957
                    value: "size".to_string(),
958
                    encoded: "unused".to_string(),
959
                }],
960
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
961
            },
962
            space1: whitespace(),
963
            space2: whitespace(),
964
            query: header_query(),
965
            filters: vec![],
966
            line_terminator0: line_terminator(),
967
        }
968
    }
969

            
970
    fn header_assert() -> Assert {
971
        Assert {
972
            line_terminators: vec![],
973
            space0: whitespace(),
974
            query: header_query(),
975
            filters: vec![],
976
            space1: whitespace(),
977
            predicate: equal_int_predicate(10),
978
            line_terminator0: line_terminator(),
979
        }
980
    }
981

            
982
    fn equal_int_predicate(value: i64) -> Predicate {
983
        Predicate {
984
            not: false,
985
            space0: whitespace(),
986
            predicate_func: PredicateFunc {
987
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
988
                value: PredicateFuncValue::Equal {
989
                    space0: whitespace(),
990
                    value: PredicateValue::Number(Number::Integer(value)),
991
                    operator: false,
992
                },
993
            },
994
        }
995
    }
996

            
997
    #[test]
998
    pub fn test_query() {
999
        assert_eq!(
            header_query().to_json(),
            JValue::Object(vec![
                ("type".to_string(), JValue::String("header".to_string())),
                (
                    "name".to_string(),
                    JValue::String("Content-Length".to_string())
                ),
            ])
        );
    }
    #[test]
    pub fn test_capture() {
        assert_eq!(
            header_capture().to_json(),
            JValue::Object(vec![
                ("name".to_string(), JValue::String("size".to_string())),
                (
                    "query".to_string(),
                    JValue::Object(vec![
                        ("type".to_string(), JValue::String("header".to_string())),
                        (
                            "name".to_string(),
                            JValue::String("Content-Length".to_string())
                        ),
                    ])
                ),
            ])
        );
    }
    #[test]
    pub fn test_predicate() {
        assert_eq!(
            equal_int_predicate(10).to_json(),
            JValue::Object(vec![
                ("type".to_string(), JValue::String("equal".to_string())),
                ("value".to_string(), JValue::Number("10".to_string()))
            ]),
        );
    }
    #[test]
    pub fn test_assert() {
        assert_eq!(
            header_assert().to_json(),
            JValue::Object(vec![
                (
                    "query".to_string(),
                    JValue::Object(vec![
                        ("type".to_string(), JValue::String("header".to_string())),
                        (
                            "name".to_string(),
                            JValue::String("Content-Length".to_string())
                        ),
                    ])
                ),
                (
                    "predicate".to_string(),
                    JValue::Object(vec![
                        ("type".to_string(), JValue::String("equal".to_string())),
                        ("value".to_string(), JValue::Number("10".to_string()))
                    ])
                )
            ]),
        );
    }
}