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
51
pub fn format(hurl_file: &HurlFile) -> String {
26
51
    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
51
    fn to_json(&self) -> JValue {
35
51
        JValue::Object(vec![(
36
51
            "entries".to_string(),
37
191
            JValue::List(self.entries.iter().map(|e| e.to_json()).collect()),
38
51
        )])
39
    }
40
}
41

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

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

            
63
174
        if !self.querystring_params().is_empty() {
64
6
            let params = self
65
6
                .querystring_params()
66
6
                .iter()
67
14
                .map(|p| p.to_json())
68
6
                .collect();
69
6
            attributes.push(("query_string_params".to_string(), JValue::List(params)));
70
        }
71
174
        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
174
        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
174
        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
174
        if !self.options().is_empty() {
88
262
            let options = self.options().iter().map(|c| c.to_json()).collect();
89
12
            attributes.push(("options".to_string(), JValue::List(options)));
90
        }
91
174
        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
174
        let comments: Vec<_> = self
97
174
            .line_terminators
98
174
            .iter()
99
211
            .filter_map(|l| l.comment.as_ref())
100
174
            .collect();
101
174
        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
174
        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
252
fn add_headers(attributes: &mut Vec<(String, JValue)>, headers: &[Header]) {
137
252
    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
81
    fn to_json(&self) -> JValue {
255
81
        let attributes = vec![
256
81
            ("name".to_string(), JValue::String(self.key.to_string())),
257
81
            ("value".to_string(), JValue::String(self.value.to_string())),
258
81
        ];
259
81
        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
258
    fn to_json(&self) -> JValue {
300
258
        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
6
            OptionKind::ConnectTimeout(value) => value.to_json(),
308
12
            OptionKind::Delay(value) => value.to_json(),
309
6
            OptionKind::FollowLocation(value) => value.to_json(),
310
6
            OptionKind::FollowLocationTrusted(value) => value.to_json(),
311
6
            OptionKind::Http10(value) => value.to_json(),
312
6
            OptionKind::Http11(value) => value.to_json(),
313
6
            OptionKind::Http2(value) => value.to_json(),
314
6
            OptionKind::Http3(value) => value.to_json(),
315
6
            OptionKind::Insecure(value) => value.to_json(),
316
6
            OptionKind::IpV4(value) => value.to_json(),
317
6
            OptionKind::IpV6(value) => value.to_json(),
318
6
            OptionKind::LimitRate(value) => value.to_json(),
319
6
            OptionKind::MaxRedirect(value) => value.to_json(),
320
6
            OptionKind::NetRc(value) => value.to_json(),
321
6
            OptionKind::NetRcFile(filename) => JValue::String(filename.to_string()),
322
6
            OptionKind::NetRcOptional(value) => value.to_json(),
323
6
            OptionKind::Output(filename) => JValue::String(filename.to_string()),
324
6
            OptionKind::PathAsIs(value) => value.to_json(),
325
6
            OptionKind::Proxy(value) => JValue::String(value.to_string()),
326
9
            OptionKind::Repeat(value) => value.to_json(),
327
6
            OptionKind::Resolve(value) => JValue::String(value.to_string()),
328
12
            OptionKind::Retry(value) => value.to_json(),
329
12
            OptionKind::RetryInterval(value) => value.to_json(),
330
6
            OptionKind::Skip(value) => value.to_json(),
331
6
            OptionKind::UnixSocket(value) => JValue::String(value.to_string()),
332
6
            OptionKind::User(value) => JValue::String(value.to_string()),
333
27
            OptionKind::Variable(value) => {
334
27
                JValue::String(format!("{}={}", value.name, value.value))
335
            }
336
9
            OptionKind::Verbose(value) => value.to_json(),
337
6
            OptionKind::VeryVerbose(value) => value.to_json(),
338
        };
339

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

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

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

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

            
389
impl ToJson for DurationOption {
390
30
    fn to_json(&self) -> JValue {
391
30
        match self {
392
21
            DurationOption::Literal(value) => value.to_json(),
393
9
            DurationOption::Placeholder(placeholder) => placeholder.to_json(),
394
        }
395
    }
396
}
397

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
786
impl ToJson for Placeholder {
787
69
    fn to_json(&self) -> JValue {
788
69
        JValue::String(format!("{{{{{}}}}}", self))
789
    }
790
}
791

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

            
798
impl ToJson for NaturalOption {
799
6
    fn to_json(&self) -> JValue {
800
6
        match self {
801
3
            NaturalOption::Literal(value) => JValue::Number(value.to_string()),
802
3
            NaturalOption::Placeholder(placeholder) => placeholder.to_json(),
803
        }
804
    }
805
}
806
#[cfg(test)]
807
pub mod tests {
808
    use hurl_core::reader::Pos;
809

            
810
    use super::*;
811

            
812
    fn whitespace() -> Whitespace {
813
        Whitespace {
814
            value: String::new(),
815
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
816
        }
817
    }
818

            
819
    fn line_terminator() -> LineTerminator {
820
        LineTerminator {
821
            space0: whitespace(),
822
            comment: None,
823
            newline: whitespace(),
824
        }
825
    }
826

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

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

            
943
    fn header_query() -> Query {
944
        Query {
945
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
946
            value: QueryValue::Header {
947
                space0: whitespace(),
948
                name: Template {
949
                    delimiter: None,
950
                    elements: vec![TemplateElement::String {
951
                        value: "Content-Length".to_string(),
952
                        encoded: "10".to_string(),
953
                    }],
954
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
955
                },
956
            },
957
        }
958
    }
959

            
960
    fn header_capture() -> Capture {
961
        Capture {
962
            line_terminators: vec![],
963
            space0: whitespace(),
964
            name: Template {
965
                delimiter: None,
966
                elements: vec![TemplateElement::String {
967
                    value: "size".to_string(),
968
                    encoded: "unused".to_string(),
969
                }],
970
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
971
            },
972
            space1: whitespace(),
973
            space2: whitespace(),
974
            query: header_query(),
975
            filters: vec![],
976
            line_terminator0: line_terminator(),
977
        }
978
    }
979

            
980
    fn header_assert() -> Assert {
981
        Assert {
982
            line_terminators: vec![],
983
            space0: whitespace(),
984
            query: header_query(),
985
            filters: vec![],
986
            space1: whitespace(),
987
            predicate: equal_int_predicate(10),
988
            line_terminator0: line_terminator(),
989
        }
990
    }
991

            
992
    fn equal_int_predicate(value: i64) -> Predicate {
993
        Predicate {
994
            not: false,
995
            space0: whitespace(),
996
            predicate_func: PredicateFunc {
997
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
998
                value: PredicateFuncValue::Equal {
999
                    space0: whitespace(),
                    value: PredicateValue::Number(Number::Integer(value)),
                    operator: false,
                },
            },
        }
    }
    #[test]
    pub fn test_query() {
        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()))
                    ])
                )
            ]),
        );
    }
}