1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2026 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;
19
use base64::engine::general_purpose;
20
use hurl_core::ast::{
21
    Assert, Base64, Body, BooleanOption, Bytes, Capture, CertificateAttributeName, Comment, Cookie,
22
    CountOption, Duration, DurationOption, Entry, EntryOption, File, FilenameParam, Filter,
23
    FilterValue, Hex, HurlFile, JsonListElement, JsonValue, JsonValueKind, KeyValue,
24
    MultilineString, MultilineStringKind, MultipartParam, NaturalOption, OptionKind, Placeholder,
25
    Predicate, PredicateFuncValue, PredicateValue, Query, QueryValue, Regex, RegexValue, Request,
26
    Response, StatusValue, VersionValue,
27
};
28
use hurl_core::types::{Count, ToSource};
29

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

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

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

            
40
impl ToJson for HurlFile {
41
38
    fn to_json(&self) -> JValue {
42
38
        JValue::Object(vec![(
43
38
            "entries".to_string(),
44
128
            JValue::List(self.entries.iter().map(|e| e.to_json()).collect()),
45
        )])
46
    }
47
}
48

            
49
impl ToJson for Entry {
50
128
    fn to_json(&self) -> JValue {
51
128
        let mut attributes = vec![("request".to_string(), self.request.to_json())];
52
128
        if let Some(response) = &self.response {
53
58
            attributes.push(("response".to_string(), response.to_json()));
54
        }
55
128
        JValue::Object(attributes)
56
    }
57
}
58

            
59
impl ToJson for Request {
60
128
    fn to_json(&self) -> JValue {
61
128
        let mut attributes = vec![
62
128
            (
63
128
                "method".to_string(),
64
128
                JValue::String(self.method.to_string()),
65
128
            ),
66
128
            ("url".to_string(), JValue::String(self.url.to_string())),
67
        ];
68
128
        add_headers(&mut attributes, &self.headers);
69

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

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

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

            
117
impl ToJson for Response {
118
    /// Transforms this response to a JSON object.
119
58
    fn to_json(&self) -> JValue {
120
58
        let mut attributes = vec![];
121
58
        if let Some(v) = get_json_version(&self.version.value) {
122
8
            attributes.push(("version".to_string(), JValue::String(v)));
123
        }
124
58
        if let StatusValue::Specific(n) = self.status.value {
125
58
            attributes.push(("status".to_string(), JValue::Number(n.to_string())));
126
        }
127
58
        add_headers(&mut attributes, &self.headers);
128
58
        if !self.captures().is_empty() {
129
12
            let captures = self.captures().iter().map(|c| c.to_json()).collect();
130
6
            attributes.push(("captures".to_string(), JValue::List(captures)));
131
        }
132
58
        if !self.asserts().is_empty() {
133
250
            let asserts = self.asserts().iter().map(|a| a.to_json()).collect();
134
24
            attributes.push(("asserts".to_string(), JValue::List(asserts)));
135
        }
136
58
        if let Some(body) = &self.body {
137
16
            attributes.push(("body".to_string(), body.to_json()));
138
        }
139
58
        JValue::Object(attributes)
140
    }
141
}
142

            
143
186
fn add_headers(attributes: &mut Vec<(String, JValue)>, headers: &[KeyValue]) {
144
186
    if !headers.is_empty() {
145
36
        let headers = JValue::List(headers.iter().map(|h| h.to_json()).collect());
146
16
        attributes.push(("headers".to_string(), headers));
147
    }
148
}
149

            
150
impl ToJson for Body {
151
48
    fn to_json(&self) -> JValue {
152
48
        self.value.to_json()
153
    }
154
}
155

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

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

            
231
impl ToJson for Hex {
232
2
    fn to_json(&self) -> JValue {
233
2
        let value = general_purpose::STANDARD.encode(&self.value);
234
2
        JValue::Object(vec![
235
2
            ("encoding".to_string(), JValue::String("base64".to_string())),
236
2
            ("value".to_string(), JValue::String(value)),
237
2
        ])
238
    }
239
}
240

            
241
impl ToJson for File {
242
4
    fn to_json(&self) -> JValue {
243
4
        JValue::Object(vec![
244
4
            ("type".to_string(), JValue::String("file".to_string())),
245
4
            (
246
4
                "filename".to_string(),
247
4
                JValue::String(self.filename.to_string()),
248
4
            ),
249
4
        ])
250
    }
251
}
252

            
253
58
fn get_json_version(version_value: &VersionValue) -> Option<String> {
254
58
    match version_value {
255
2
        VersionValue::Version1 => Some("HTTP/1.0".to_string()),
256
2
        VersionValue::Version11 => Some("HTTP/1.1".to_string()),
257
2
        VersionValue::Version2 => Some("HTTP/2".to_string()),
258
2
        VersionValue::Version3 => Some("HTTP/3".to_string()),
259
50
        VersionValue::VersionAny => None,
260
    }
261
}
262

            
263
impl ToJson for KeyValue {
264
56
    fn to_json(&self) -> JValue {
265
56
        let attributes = vec![
266
56
            ("name".to_string(), JValue::String(self.key.to_string())),
267
56
            ("value".to_string(), JValue::String(self.value.to_string())),
268
        ];
269
56
        JValue::Object(attributes)
270
    }
271
}
272

            
273
impl ToJson for MultipartParam {
274
6
    fn to_json(&self) -> JValue {
275
6
        match self {
276
2
            MultipartParam::Param(param) => param.to_json(),
277
4
            MultipartParam::FilenameParam(param) => param.to_json(),
278
        }
279
    }
280
}
281

            
282
impl ToJson for FilenameParam {
283
4
    fn to_json(&self) -> JValue {
284
4
        let mut attributes = vec![
285
4
            ("name".to_string(), JValue::String(self.key.to_string())),
286
4
            (
287
4
                "filename".to_string(),
288
4
                JValue::String(self.value.filename.to_string()),
289
4
            ),
290
        ];
291
4
        if let Some(content_type) = &self.value.content_type {
292
2
            attributes.push((
293
2
                "content_type".to_string(),
294
2
                JValue::String(content_type.to_string()),
295
2
            ));
296
        }
297
4
        JValue::Object(attributes)
298
    }
299
}
300

            
301
impl ToJson for Cookie {
302
4
    fn to_json(&self) -> JValue {
303
4
        let attributes = vec![
304
4
            ("name".to_string(), JValue::String(self.name.to_string())),
305
4
            ("value".to_string(), JValue::String(self.value.to_string())),
306
        ];
307
4
        JValue::Object(attributes)
308
    }
309
}
310

            
311
impl ToJson for EntryOption {
312
224
    fn to_json(&self) -> JValue {
313
224
        let value = match &self.kind {
314
4
            OptionKind::AwsSigV4(value) => JValue::String(value.to_string()),
315
4
            OptionKind::CaCertificate(filename) => JValue::String(filename.to_string()),
316
6
            OptionKind::ClientCert(filename) => JValue::String(filename.to_string()),
317
4
            OptionKind::ClientKey(filename) => JValue::String(filename.to_string()),
318
4
            OptionKind::Compressed(value) => value.to_json(),
319
4
            OptionKind::ConnectTo(value) => JValue::String(value.to_string()),
320
4
            OptionKind::ConnectTimeout(value) => value.to_json(),
321
12
            OptionKind::Delay(value) => value.to_json(),
322
4
            OptionKind::Digest(value) => value.to_json(),
323
4
            OptionKind::FailWithBody(value) => value.to_json(),
324
4
            OptionKind::FollowLocation(value) => value.to_json(),
325
4
            OptionKind::FollowLocationTrusted(value) => value.to_json(),
326
4
            OptionKind::Header(value) => JValue::String(value.to_string()),
327
4
            OptionKind::Http10(value) => value.to_json(),
328
4
            OptionKind::Http11(value) => value.to_json(),
329
4
            OptionKind::Http2(value) => value.to_json(),
330
2
            OptionKind::Http2PriorKnowledge(value) => value.to_json(),
331
4
            OptionKind::Http3(value) => value.to_json(),
332
4
            OptionKind::Insecure(value) => value.to_json(),
333
4
            OptionKind::IpV4(value) => value.to_json(),
334
4
            OptionKind::IpV6(value) => value.to_json(),
335
4
            OptionKind::LimitRate(value) => value.to_json(),
336
4
            OptionKind::MaxRedirect(value) => value.to_json(),
337
4
            OptionKind::MaxTime(value) => value.to_json(),
338
4
            OptionKind::Negotiate(value) => value.to_json(),
339
4
            OptionKind::NetRc(value) => value.to_json(),
340
4
            OptionKind::NetRcFile(filename) => JValue::String(filename.to_string()),
341
4
            OptionKind::NetRcOptional(value) => value.to_json(),
342
4
            OptionKind::NoHeader(value) => JValue::String(value.to_string()),
343
2
            OptionKind::NoJsonpathCoercion(value) => value.to_json(),
344
4
            OptionKind::Ntlm(value) => value.to_json(),
345
4
            OptionKind::Output(filename) => JValue::String(filename.to_string()),
346
4
            OptionKind::PathAsIs(value) => value.to_json(),
347
4
            OptionKind::PinnedPublicKey(value) => JValue::String(value.to_string()),
348
4
            OptionKind::Proxy(value) => JValue::String(value.to_string()),
349
6
            OptionKind::Repeat(value) => value.to_json(),
350
4
            OptionKind::Resolve(value) => JValue::String(value.to_string()),
351
8
            OptionKind::Retry(value) => value.to_json(),
352
8
            OptionKind::RetryInterval(value) => value.to_json(),
353
4
            OptionKind::Skip(value) => value.to_json(),
354
4
            OptionKind::TlsMax(value) => JValue::String(value.to_string()),
355
4
            OptionKind::TlsMin(value) => JValue::String(value.to_string()),
356
4
            OptionKind::UnixSocket(value) => JValue::String(value.to_string()),
357
4
            OptionKind::User(value) => JValue::String(value.to_string()),
358
18
            OptionKind::Variable(value) => {
359
18
                JValue::String(format!("{}={}", value.name, value.value.to_source()))
360
            }
361
6
            OptionKind::Verbose(value) => value.to_json(),
362
4
            OptionKind::Verbosity(value) => JValue::String(value.to_string()),
363
4
            OptionKind::VeryVerbose(value) => value.to_json(),
364
        };
365

            
366
        // If the value contains the unit such as `{ "value": 10, "unit": "second" }`
367
        // The JSON for this option should still have one level
368
        // for example: { "name": "delay", "value": 10, "unit", "second" }
369
224
        let attributes = if let JValue::Object(mut attributes) = value {
370
16
            attributes.push((
371
16
                "name".to_string(),
372
16
                JValue::String(self.kind.identifier().to_string()),
373
16
            ));
374
16
            attributes
375
        } else {
376
208
            vec![
377
208
                (
378
208
                    "name".to_string(),
379
208
                    JValue::String(self.kind.identifier().to_string()),
380
208
                ),
381
208
                ("value".to_string(), value),
382
            ]
383
        };
384
224
        JValue::Object(attributes)
385
    }
386
}
387

            
388
impl ToJson for BooleanOption {
389
86
    fn to_json(&self) -> JValue {
390
86
        match self {
391
46
            BooleanOption::Literal(value) => JValue::Boolean(*value),
392
40
            BooleanOption::Placeholder(placeholder) => placeholder.to_json(),
393
        }
394
    }
395
}
396

            
397
impl ToJson for CountOption {
398
18
    fn to_json(&self) -> JValue {
399
18
        match self {
400
12
            CountOption::Literal(value) => value.to_json(),
401
6
            CountOption::Placeholder(placeholder) => placeholder.to_json(),
402
        }
403
    }
404
}
405

            
406
impl ToJson for Count {
407
12
    fn to_json(&self) -> JValue {
408
12
        match self {
409
8
            Count::Finite(n) => JValue::Number(n.to_string()),
410
4
            Count::Infinite => JValue::Number("-1".to_string()),
411
        }
412
    }
413
}
414

            
415
impl ToJson for DurationOption {
416
28
    fn to_json(&self) -> JValue {
417
28
        match self {
418
20
            DurationOption::Literal(value) => value.to_json(),
419
8
            DurationOption::Placeholder(placeholder) => placeholder.to_json(),
420
        }
421
    }
422
}
423

            
424
impl ToJson for Duration {
425
20
    fn to_json(&self) -> JValue {
426
20
        if let Some(unit) = self.unit {
427
16
            let mut attributes =
428
16
                vec![("value".to_string(), JValue::Number(self.value.to_string()))];
429
16
            attributes.push(("unit".to_string(), JValue::String(unit.to_string())));
430
16
            JValue::Object(attributes)
431
        } else {
432
4
            JValue::Number(self.value.to_string())
433
        }
434
    }
435
}
436

            
437
impl ToJson for Capture {
438
12
    fn to_json(&self) -> JValue {
439
12
        let mut attributes = vec![
440
12
            ("name".to_string(), JValue::String(self.name.to_string())),
441
12
            ("query".to_string(), self.query.to_json()),
442
        ];
443
12
        if !self.filters.is_empty() {
444
2
            let filters = JValue::List(self.filters.iter().map(|(_, f)| f.to_json()).collect());
445
2
            attributes.push(("filters".to_string(), filters));
446
        }
447
12
        if self.redacted {
448
4
            attributes.push(("redact".to_string(), JValue::Boolean(true)));
449
        }
450
12
        JValue::Object(attributes)
451
    }
452
}
453

            
454
impl ToJson for Assert {
455
250
    fn to_json(&self) -> JValue {
456
250
        let mut attributes = vec![("query".to_string(), self.query.to_json())];
457
250
        if !self.filters.is_empty() {
458
96
            let filters = JValue::List(self.filters.iter().map(|(_, f)| f.to_json()).collect());
459
78
            attributes.push(("filters".to_string(), filters));
460
        }
461
250
        attributes.push(("predicate".to_string(), self.predicate.to_json()));
462
250
        JValue::Object(attributes)
463
    }
464
}
465

            
466
impl ToJson for Query {
467
262
    fn to_json(&self) -> JValue {
468
262
        let attributes = query_value_attributes(&self.value);
469
262
        JValue::Object(attributes)
470
    }
471
}
472

            
473
262
fn query_value_attributes(query_value: &QueryValue) -> Vec<(String, JValue)> {
474
262
    let mut attributes = vec![];
475
262
    let att_type = JValue::String(query_value.identifier().to_string());
476
262
    attributes.push(("type".to_string(), att_type));
477

            
478
262
    match query_value {
479
162
        QueryValue::Jsonpath { expr, .. } => {
480
162
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
481
        }
482
4
        QueryValue::Header { name, .. } => {
483
4
            attributes.push(("name".to_string(), JValue::String(name.to_string())));
484
        }
485
6
        QueryValue::Cookie { expr, .. } => {
486
6
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
487
        }
488
2
        QueryValue::Xpath { expr, .. } => {
489
2
            attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
490
        }
491
2
        QueryValue::Regex { value, .. } => {
492
2
            attributes.push(("expr".to_string(), value.to_json()));
493
        }
494
6
        QueryValue::Variable { name, .. } => {
495
6
            attributes.push(("name".to_string(), JValue::String(name.to_string())));
496
        }
497
        QueryValue::Certificate {
498
22
            attribute_name: field,
499
            ..
500
22
        } => {
501
22
            attributes.push(("expr".to_string(), field.to_json()));
502
        }
503
58
        _ => {}
504
    };
505
262
    attributes
506
}
507

            
508
impl ToJson for RegexValue {
509
6
    fn to_json(&self) -> JValue {
510
6
        match self {
511
2
            RegexValue::Template(template) => JValue::String(template.to_string()),
512
4
            RegexValue::Regex(regex) => regex.to_json(),
513
        }
514
    }
515
}
516

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

            
527
impl ToJson for CertificateAttributeName {
528
22
    fn to_json(&self) -> JValue {
529
22
        JValue::String(self.identifier().to_string())
530
    }
531
}
532

            
533
impl ToJson for Predicate {
534
250
    fn to_json(&self) -> JValue {
535
250
        let mut attributes = vec![];
536
250
        if self.not {
537
2
            attributes.push(("not".to_string(), JValue::Boolean(true)));
538
        }
539
250
        let identifier = self.predicate_func.value.identifier();
540
250
        attributes.push(("type".to_string(), JValue::String(identifier.to_string())));
541

            
542
250
        match &self.predicate_func.value {
543
168
            PredicateFuncValue::Equal { value, .. } => add_predicate_value(&mut attributes, value),
544
6
            PredicateFuncValue::NotEqual { value, .. } => {
545
6
                add_predicate_value(&mut attributes, value);
546
            }
547
6
            PredicateFuncValue::GreaterThan { value, .. } => {
548
6
                add_predicate_value(&mut attributes, value);
549
            }
550
2
            PredicateFuncValue::GreaterThanOrEqual { value, .. } => {
551
2
                add_predicate_value(&mut attributes, value);
552
            }
553
6
            PredicateFuncValue::LessThan { value, .. } => {
554
6
                add_predicate_value(&mut attributes, value);
555
            }
556
2
            PredicateFuncValue::LessThanOrEqual { value, .. } => {
557
2
                add_predicate_value(&mut attributes, value);
558
            }
559
6
            PredicateFuncValue::StartWith { value, .. } => {
560
6
                add_predicate_value(&mut attributes, value);
561
            }
562
4
            PredicateFuncValue::EndWith { value, .. } => {
563
4
                add_predicate_value(&mut attributes, value);
564
            }
565
6
            PredicateFuncValue::Contain { value, .. } => {
566
6
                add_predicate_value(&mut attributes, value);
567
            }
568
2
            PredicateFuncValue::Include { value, .. } => {
569
2
                add_predicate_value(&mut attributes, value);
570
            }
571
6
            PredicateFuncValue::Match { value, .. } => {
572
6
                add_predicate_value(&mut attributes, value);
573
            }
574
            PredicateFuncValue::Exist
575
            | PredicateFuncValue::IsBoolean
576
            | PredicateFuncValue::IsCollection
577
            | PredicateFuncValue::IsDate
578
            | PredicateFuncValue::IsEmpty
579
            | PredicateFuncValue::IsFloat
580
            | PredicateFuncValue::IsInteger
581
            | PredicateFuncValue::IsIpv4
582
            | PredicateFuncValue::IsIpv6
583
            | PredicateFuncValue::IsIsoDate
584
            | PredicateFuncValue::IsList
585
            | PredicateFuncValue::IsNumber
586
            | PredicateFuncValue::IsObject
587
            | PredicateFuncValue::IsString
588
36
            | PredicateFuncValue::IsUuid => {}
589
        }
590
250
        JValue::Object(attributes)
591
    }
592
}
593

            
594
214
fn add_predicate_value(attributes: &mut Vec<(String, JValue)>, predicate_value: &PredicateValue) {
595
214
    let (value, encoding) = json_predicate_value(predicate_value);
596
214
    attributes.push(("value".to_string(), value));
597
214
    if let Some(encoding) = encoding {
598
24
        attributes.push(("encoding".to_string(), JValue::String(encoding)));
599
    }
600
}
601

            
602
214
fn json_predicate_value(predicate_value: &PredicateValue) -> (JValue, Option<String>) {
603
214
    match predicate_value {
604
100
        PredicateValue::String(value) => (JValue::String(value.to_string()), None),
605
12
        PredicateValue::MultilineString(value) => (JValue::String(value.value().to_string()), None),
606
2
        PredicateValue::Bool(value) => (JValue::Boolean(*value), None),
607
2
        PredicateValue::Null => (JValue::Null, None),
608
70
        PredicateValue::Number(value) => (JValue::Number(value.to_string()), None),
609
2
        PredicateValue::File(value) => (value.to_json(), None),
610
18
        PredicateValue::Hex(value) => {
611
18
            let base64_string = general_purpose::STANDARD.encode(value.value.clone());
612
18
            (JValue::String(base64_string), Some("base64".to_string()))
613
        }
614
2
        PredicateValue::Base64(value) => {
615
2
            let base64_string = general_purpose::STANDARD.encode(value.value.clone());
616
2
            (JValue::String(base64_string), Some("base64".to_string()))
617
        }
618
2
        PredicateValue::Placeholder(value) => (JValue::String(value.to_string()), None),
619
4
        PredicateValue::Regex(value) => {
620
4
            (JValue::String(value.to_string()), Some("regex".to_string()))
621
        }
622
    }
623
}
624

            
625
impl ToJson for JsonValue {
626
68
    fn to_json(&self) -> JValue {
627
68
        match &self.kind() {
628
2
            JsonValueKind::Null => JValue::Null,
629
30
            JsonValueKind::Number(s) => JValue::Number(s.to_string()),
630
12
            JsonValueKind::String(s) => JValue::String(s.to_string()),
631
2
            JsonValueKind::Boolean(v) => JValue::Boolean(*v),
632
10
            JsonValueKind::List { elements, .. } => {
633
34
                JValue::List(elements.iter().map(|e| e.to_json()).collect())
634
            }
635
10
            JsonValueKind::Object { elements, .. } => JValue::Object(
636
10
                elements
637
10
                    .iter()
638
30
                    .map(|elem| (elem.name.to_string(), elem.value.to_json()))
639
10
                    .collect(),
640
            ),
641
2
            JsonValueKind::Placeholder(exp) => JValue::String(format!("{{{{{exp}}}}}")),
642
        }
643
    }
644
}
645

            
646
impl ToJson for JsonListElement {
647
34
    fn to_json(&self) -> JValue {
648
34
        self.value.to_json()
649
    }
650
}
651

            
652
impl ToJson for Filter {
653
98
    fn to_json(&self) -> JValue {
654
98
        self.value.to_json()
655
    }
656
}
657

            
658
impl ToJson for FilterValue {
659
98
    fn to_json(&self) -> JValue {
660
98
        let mut attributes = vec![];
661
98
        let att_name = "type".to_string();
662
98
        let att_value = JValue::String(self.identifier().to_string());
663
98
        attributes.push((att_name, att_value));
664

            
665
98
        match self {
666
2
            FilterValue::Decode { encoding, .. } => {
667
2
                attributes.push(("encoding".to_string(), JValue::String(encoding.to_string())));
668
            }
669
4
            FilterValue::Format { fmt, .. } => {
670
4
                attributes.push(("fmt".to_string(), JValue::String(fmt.to_string())));
671
            }
672
6
            FilterValue::DateFormat { fmt, .. } => {
673
6
                attributes.push(("fmt".to_string(), JValue::String(fmt.to_string())));
674
            }
675
6
            FilterValue::JsonPath { expr, .. } => {
676
6
                attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
677
            }
678
2
            FilterValue::Nth { n, .. } => {
679
2
                attributes.push(("n".to_string(), JValue::Number(n.to_string())));
680
            }
681
2
            FilterValue::Regex { value, .. } => {
682
2
                attributes.push(("expr".to_string(), value.to_json()));
683
            }
684
            FilterValue::Replace {
685
10
                old_value,
686
10
                new_value,
687
                ..
688
10
            } => {
689
10
                attributes.push((
690
10
                    "old_value".to_string(),
691
10
                    JValue::String(old_value.to_string()),
692
10
                ));
693
10
                attributes.push((
694
10
                    "new_value".to_string(),
695
10
                    JValue::String(new_value.to_string()),
696
10
                ));
697
            }
698
            FilterValue::ReplaceRegex {
699
2
                pattern, new_value, ..
700
2
            } => {
701
2
                attributes.push(("pattern".to_string(), pattern.to_json()));
702
2
                attributes.push((
703
2
                    "new_value".to_string(),
704
2
                    JValue::String(new_value.to_string()),
705
2
                ));
706
            }
707
2
            FilterValue::Split { sep, .. } => {
708
2
                attributes.push(("sep".to_string(), JValue::String(sep.to_string())));
709
            }
710
2
            FilterValue::ToDate { fmt, .. } => {
711
2
                attributes.push(("fmt".to_string(), JValue::String(fmt.to_string())));
712
            }
713
2
            FilterValue::UrlQueryParam { param, .. } => {
714
2
                attributes.push(("param".to_string(), JValue::String(param.to_string())));
715
            }
716
2
            FilterValue::XPath { expr, .. } => {
717
2
                attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
718
            }
719
56
            _ => {}
720
        }
721
98
        JValue::Object(attributes)
722
    }
723
}
724

            
725
impl ToJson for Placeholder {
726
56
    fn to_json(&self) -> JValue {
727
56
        JValue::String(format!("{{{{{self}}}}}"))
728
    }
729
}
730

            
731
impl ToJson for Comment {
732
14
    fn to_json(&self) -> JValue {
733
14
        JValue::String(self.value.to_string())
734
    }
735
}
736

            
737
impl ToJson for NaturalOption {
738
4
    fn to_json(&self) -> JValue {
739
4
        match self {
740
2
            NaturalOption::Literal(value) => JValue::Number(value.to_string()),
741
2
            NaturalOption::Placeholder(placeholder) => placeholder.to_json(),
742
        }
743
    }
744
}
745
#[cfg(test)]
746
pub mod tests {
747
    use hurl_core::ast::{
748
        I64, LineTerminator, Method, Number, PredicateFunc, SourceInfo, Status, Template,
749
        TemplateElement, Version, Whitespace,
750
    };
751
    use hurl_core::reader::Pos;
752
    use hurl_core::types::ToSource;
753

            
754
    use super::*;
755

            
756
    fn whitespace() -> Whitespace {
757
        Whitespace {
758
            value: String::new(),
759
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
760
        }
761
    }
762

            
763
    fn line_terminator() -> LineTerminator {
764
        LineTerminator {
765
            space0: whitespace(),
766
            comment: None,
767
            newline: whitespace(),
768
        }
769
    }
770

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

            
833
    #[test]
834
    pub fn test_response() {
835
        assert_eq!(
836
            Response {
837
                line_terminators: vec![],
838
                version: Version {
839
                    value: VersionValue::Version11,
840
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
841
                },
842
                space0: whitespace(),
843
                status: Status {
844
                    value: StatusValue::Specific(200),
845
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
846
                },
847
                space1: whitespace(),
848
                line_terminator0: line_terminator(),
849
                headers: vec![],
850
                sections: vec![],
851
                body: None,
852
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
853
            }
854
            .to_json(),
855
            JValue::Object(vec![
856
                (
857
                    "version".to_string(),
858
                    JValue::String("HTTP/1.1".to_string())
859
                ),
860
                ("status".to_string(), JValue::Number("200".to_string()))
861
            ])
862
        );
863
        assert_eq!(
864
            Response {
865
                line_terminators: vec![],
866
                version: Version {
867
                    value: VersionValue::VersionAny,
868
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
869
                },
870
                space0: whitespace(),
871
                status: Status {
872
                    value: StatusValue::Any,
873
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
874
                },
875
                space1: whitespace(),
876
                line_terminator0: line_terminator(),
877
                headers: vec![],
878
                sections: vec![],
879
                body: None,
880
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
881
            }
882
            .to_json(),
883
            JValue::Object(vec![])
884
        );
885
    }
886

            
887
    fn header_query() -> Query {
888
        Query {
889
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
890
            value: QueryValue::Header {
891
                space0: whitespace(),
892
                name: Template::new(
893
                    None,
894
                    vec![TemplateElement::String {
895
                        value: "Content-Length".to_string(),
896
                        source: "Content-Length".to_source(),
897
                    }],
898
                    SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
899
                ),
900
            },
901
        }
902
    }
903

            
904
    fn header_capture() -> Capture {
905
        Capture {
906
            line_terminators: vec![],
907
            space0: whitespace(),
908
            name: Template::new(
909
                None,
910
                vec![TemplateElement::String {
911
                    value: "size".to_string(),
912
                    source: "unused".to_source(),
913
                }],
914
                SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
915
            ),
916
            space1: whitespace(),
917
            space2: whitespace(),
918
            query: header_query(),
919
            filters: vec![],
920
            space3: whitespace(),
921
            redacted: false,
922
            line_terminator0: line_terminator(),
923
        }
924
    }
925

            
926
    fn header_assert() -> Assert {
927
        Assert {
928
            line_terminators: vec![],
929
            space0: whitespace(),
930
            query: header_query(),
931
            filters: vec![],
932
            space1: whitespace(),
933
            predicate: equal_int_predicate(10),
934
            line_terminator0: line_terminator(),
935
        }
936
    }
937

            
938
    fn equal_int_predicate(value: i64) -> Predicate {
939
        Predicate {
940
            not: false,
941
            space0: whitespace(),
942
            predicate_func: PredicateFunc {
943
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
944
                value: PredicateFuncValue::Equal {
945
                    space0: whitespace(),
946
                    value: PredicateValue::Number(Number::Integer(I64::new(
947
                        value,
948
                        value.to_string().to_source(),
949
                    ))),
950
                },
951
            },
952
        }
953
    }
954

            
955
    #[test]
956
    pub fn test_query() {
957
        assert_eq!(
958
            header_query().to_json(),
959
            JValue::Object(vec![
960
                ("type".to_string(), JValue::String("header".to_string())),
961
                (
962
                    "name".to_string(),
963
                    JValue::String("Content-Length".to_string())
964
                ),
965
            ])
966
        );
967
    }
968

            
969
    #[test]
970
    pub fn test_capture() {
971
        assert_eq!(
972
            header_capture().to_json(),
973
            JValue::Object(vec![
974
                ("name".to_string(), JValue::String("size".to_string())),
975
                (
976
                    "query".to_string(),
977
                    JValue::Object(vec![
978
                        ("type".to_string(), JValue::String("header".to_string())),
979
                        (
980
                            "name".to_string(),
981
                            JValue::String("Content-Length".to_string())
982
                        ),
983
                    ])
984
                ),
985
            ])
986
        );
987
    }
988

            
989
    #[test]
990
    pub fn test_predicate() {
991
        assert_eq!(
992
            equal_int_predicate(10).to_json(),
993
            JValue::Object(vec![
994
                ("type".to_string(), JValue::String("==".to_string())),
995
                ("value".to_string(), JValue::Number("10".to_string()))
996
            ]),
997
        );
998
    }
999

            
    #[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("==".to_string())),
                        ("value".to_string(), JValue::Number("10".to_string()))
                    ])
                )
            ]),
        );
    }
}