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 std::fmt;
19
use std::fmt::Formatter;
20

            
21
use crate::reader::Pos;
22
use crate::types::{SourceString, ToSource};
23

            
24
use super::json::JsonValue;
25

            
26
#[derive(Clone, Debug, PartialEq, Eq)]
27
pub struct KeyValue {
28
    pub line_terminators: Vec<LineTerminator>,
29
    pub space0: Whitespace,
30
    pub key: Template,
31
    pub space1: Whitespace,
32
    pub space2: Whitespace,
33
    pub value: Template,
34
    pub line_terminator0: LineTerminator,
35
}
36

            
37
#[derive(Clone, Debug, PartialEq, Eq)]
38
pub struct MultilineString {
39
    pub attributes: Vec<MultilineStringAttribute>,
40
    pub space: Whitespace,
41
    pub newline: Whitespace,
42
    pub kind: MultilineStringKind,
43
}
44

            
45
impl fmt::Display for MultilineString {
46
65
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
47
65
        match &self.kind {
48
25
            MultilineStringKind::Text(value)
49
15
            | MultilineStringKind::Json(value)
50
50
            | MultilineStringKind::Xml(value) => write!(f, "{value}"),
51
15
            MultilineStringKind::GraphQl(value) => write!(f, "{value}"),
52
        }
53
    }
54
}
55

            
56
impl ToSource for MultilineString {
57
405
    fn to_source(&self) -> SourceString {
58
405
        let mut source = SourceString::new();
59
405
        let att = self
60
405
            .attributes
61
405
            .iter()
62
418
            .map(|att| att.identifier())
63
405
            .collect::<Vec<_>>()
64
405
            .join(",");
65
405
        source.push_str("```");
66
405
        source.push_str(self.lang());
67
405
        if !self.lang().is_empty() && self.has_attributes() {
68
5
            source.push(',');
69
        }
70
405
        source.push_str(&att);
71
405
        source.push_str(self.space.as_str());
72
405
        source.push_str(self.newline.as_str());
73
405
        source.push_str(self.kind.to_source().as_str());
74
405
        source.push_str("```");
75
405
        source
76
    }
77
}
78

            
79
impl MultilineString {
80
810
    pub fn lang(&self) -> &'static str {
81
810
        match self.kind {
82
340
            MultilineStringKind::Text(_) => "",
83
160
            MultilineStringKind::Json(_) => "json",
84
120
            MultilineStringKind::Xml(_) => "xml",
85
190
            MultilineStringKind::GraphQl(_) => "graphql",
86
        }
87
    }
88

            
89
210
    pub fn value(&self) -> Template {
90
210
        match &self.kind {
91
135
            MultilineStringKind::Text(text)
92
25
            | MultilineStringKind::Json(text)
93
185
            | MultilineStringKind::Xml(text) => text.clone(),
94
25
            MultilineStringKind::GraphQl(text) => text.value.clone(),
95
        }
96
    }
97

            
98
    /// Returns true if this multiline string has `escape` or `novariable` attributes.
99
635
    pub fn has_attributes(&self) -> bool {
100
635
        !self.attributes.is_empty()
101
    }
102
}
103

            
104
#[allow(clippy::large_enum_variant)]
105
#[derive(Clone, Debug, PartialEq, Eq)]
106
pub enum MultilineStringKind {
107
    Text(Template),
108
    Json(Template),
109
    Xml(Template),
110
    GraphQl(GraphQl),
111
}
112

            
113
impl ToSource for MultilineStringKind {
114
405
    fn to_source(&self) -> SourceString {
115
405
        match self {
116
170
            MultilineStringKind::Text(value)
117
80
            | MultilineStringKind::Json(value)
118
310
            | MultilineStringKind::Xml(value) => value.to_source(),
119
95
            MultilineStringKind::GraphQl(value) => value.to_source(),
120
        }
121
    }
122
}
123

            
124
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
125
pub enum MultilineStringAttribute {
126
    Escape,
127
    NoVariable,
128
}
129

            
130
impl MultilineStringAttribute {
131
65
    pub fn identifier(&self) -> &'static str {
132
65
        match self {
133
50
            MultilineStringAttribute::Escape => "escape",
134
15
            MultilineStringAttribute::NoVariable => "novariable",
135
        }
136
    }
137
}
138

            
139
#[derive(Clone, Debug, PartialEq, Eq)]
140
pub struct GraphQl {
141
    pub value: Template,
142
    pub variables: Option<GraphQlVariables>,
143
}
144

            
145
impl fmt::Display for GraphQl {
146
15
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
147
15
        write!(f, "{}", self.value)?;
148
15
        if let Some(vars) = &self.variables {
149
5
            write!(f, "{}", vars.to_source())?;
150
        }
151
15
        Ok(())
152
    }
153
}
154

            
155
impl ToSource for GraphQl {
156
95
    fn to_source(&self) -> SourceString {
157
95
        let mut source = SourceString::new();
158
95
        source.push_str(self.value.to_source().as_str());
159
95
        if let Some(vars) = &self.variables {
160
25
            source.push_str(vars.to_source().as_str());
161
        }
162
95
        source
163
    }
164
}
165

            
166
#[derive(Clone, Debug, PartialEq, Eq)]
167
pub struct GraphQlVariables {
168
    pub space: Whitespace,
169
    pub value: JsonValue,
170
    pub whitespace: Whitespace,
171
}
172

            
173
impl ToSource for GraphQlVariables {
174
30
    fn to_source(&self) -> SourceString {
175
30
        let mut source = "variables".to_source();
176
30
        source.push_str(self.space.as_str());
177
30
        source.push_str(self.value.to_source().as_str());
178
30
        source.push_str(self.whitespace.as_str());
179
30
        source
180
    }
181
}
182

            
183
#[derive(Clone, Debug, PartialEq, Eq)]
184
pub struct Base64 {
185
    pub space0: Whitespace,
186
    pub value: Vec<u8>,
187
    pub source: SourceString,
188
    pub space1: Whitespace,
189
}
190

            
191
#[derive(Clone, Debug, PartialEq, Eq)]
192
pub struct File {
193
    pub space0: Whitespace,
194
    pub filename: Template,
195
    pub space1: Whitespace,
196
}
197

            
198
#[derive(Clone, Debug, PartialEq, Eq)]
199
pub struct Template {
200
    pub delimiter: Option<char>,
201
    pub elements: Vec<TemplateElement>,
202
    pub source_info: SourceInfo,
203
}
204

            
205
impl fmt::Display for Template {
206
2550
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
207
2550
        let mut buffer = String::new();
208
2680
        for element in self.elements.iter() {
209
2680
            buffer.push_str(element.to_string().as_str());
210
        }
211
2550
        write!(f, "{buffer}")
212
    }
213
}
214

            
215
impl ToSource for Template {
216
15090
    fn to_source(&self) -> SourceString {
217
15090
        let mut s = SourceString::new();
218
15090
        if let Some(d) = self.delimiter {
219
7940
            s.push(d);
220
        }
221
18343
        let elements: Vec<SourceString> = self.elements.iter().map(|e| e.to_source()).collect();
222
15090
        s.push_str(elements.join("").as_str());
223
15090
        if let Some(d) = self.delimiter {
224
7940
            s.push(d);
225
        }
226
15090
        s
227
    }
228
}
229

            
230
impl Template {
231
    /// Creates a new template.
232
81690
    pub fn new(
233
81690
        delimiter: Option<char>,
234
81690
        elements: Vec<TemplateElement>,
235
81690
        source_info: SourceInfo,
236
81690
    ) -> Template {
237
81690
        Template {
238
81690
            delimiter,
239
81690
            elements,
240
81690
            source_info,
241
        }
242
    }
243

            
244
    /// Returns true if this template is empty.
245
210
    pub fn is_empty(&self) -> bool {
246
210
        self.elements.is_empty()
247
    }
248
}
249

            
250
#[derive(Clone, Debug, PartialEq, Eq)]
251
pub enum TemplateElement {
252
    String { value: String, source: SourceString },
253
    Placeholder(Placeholder),
254
}
255

            
256
impl fmt::Display for TemplateElement {
257
2680
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
258
2680
        let s = match self {
259
2465
            TemplateElement::String { value, .. } => value.clone(),
260
            // TODO: see why we can't need to us `{{` and `}}` in a to_string method
261
215
            TemplateElement::Placeholder(value) => format!("{{{{{value}}}}}"),
262
        };
263
2680
        write!(f, "{s}")
264
    }
265
}
266

            
267
impl ToSource for TemplateElement {
268
15325
    fn to_source(&self) -> SourceString {
269
15325
        match self {
270
14635
            TemplateElement::String { source, .. } => source.clone(),
271
690
            TemplateElement::Placeholder(value) => value.to_source(),
272
        }
273
    }
274
}
275

            
276
#[derive(Clone, Debug, PartialEq, Eq)]
277
pub struct Comment {
278
    pub value: String,
279
    pub source_info: SourceInfo,
280
}
281

            
282
impl ToSource for Comment {
283
2270
    fn to_source(&self) -> SourceString {
284
2270
        format!("#{}", self.value).to_source()
285
    }
286
}
287

            
288
#[derive(Clone, Debug, PartialEq, Eq)]
289
pub struct Whitespace {
290
    pub value: String,
291
    pub source_info: SourceInfo,
292
}
293

            
294
impl fmt::Display for Whitespace {
295
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
296
        write!(f, "{}", self.value)
297
    }
298
}
299

            
300
impl Whitespace {
301
77960
    pub fn as_str(&self) -> &str {
302
77960
        &self.value
303
    }
304
}
305

            
306
#[derive(Clone, Debug, PartialEq, Eq)]
307
pub enum Number {
308
    Float(Float),
309
    Integer(I64),
310
    BigInteger(String),
311
}
312

            
313
impl fmt::Display for Number {
314
330
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
315
330
        match self {
316
90
            Number::Float(value) => write!(f, "{value}"),
317
240
            Number::Integer(value) => write!(f, "{value}"),
318
            Number::BigInteger(value) => write!(f, "{value}"),
319
        }
320
    }
321
}
322

            
323
impl ToSource for Number {
324
1415
    fn to_source(&self) -> SourceString {
325
1415
        match self {
326
420
            Number::Float(value) => value.to_source(),
327
990
            Number::Integer(value) => value.to_source(),
328
5
            Number::BigInteger(value) => value.to_source(),
329
        }
330
    }
331
}
332

            
333
// keep Number terminology for both Integer and Decimal Numbers
334
// different representation for the same float value
335
// 1.01 and 1.010
336

            
337
#[derive(Clone, Debug)]
338
pub struct Float {
339
    value: f64,
340
    source: SourceString,
341
}
342

            
343
impl Float {
344
970
    pub fn new(value: f64, source: SourceString) -> Float {
345
970
        Float { value, source }
346
    }
347

            
348
415
    pub fn as_f64(&self) -> f64 {
349
415
        self.value
350
    }
351
}
352

            
353
impl fmt::Display for Float {
354
90
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
355
90
        write!(f, "{}", self.value)
356
    }
357
}
358

            
359
impl ToSource for Float {
360
420
    fn to_source(&self) -> SourceString {
361
420
        self.source.clone()
362
    }
363
}
364

            
365
#[derive(Clone, Debug, PartialEq, Eq)]
366
pub struct U64 {
367
    value: u64,
368
    source: SourceString,
369
}
370

            
371
impl U64 {
372
6770
    pub fn new(value: u64, source: SourceString) -> U64 {
373
6770
        U64 { value, source }
374
    }
375

            
376
6535
    pub fn as_u64(&self) -> u64 {
377
6535
        self.value
378
    }
379
}
380

            
381
impl fmt::Display for U64 {
382
345
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
383
345
        write!(f, "{}", self.value)
384
    }
385
}
386

            
387
impl ToSource for U64 {
388
175
    fn to_source(&self) -> SourceString {
389
175
        self.source.clone()
390
    }
391
}
392

            
393
#[derive(Clone, Debug, PartialEq, Eq)]
394
pub struct I64 {
395
    value: i64,
396
    source: SourceString,
397
}
398

            
399
impl I64 {
400
5580
    pub fn new(value: i64, source: SourceString) -> I64 {
401
5580
        I64 { value, source }
402
    }
403

            
404
4610
    pub fn as_i64(&self) -> i64 {
405
4610
        self.value
406
    }
407
}
408

            
409
impl fmt::Display for I64 {
410
245
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
411
245
        write!(f, "{}", self.value)
412
    }
413
}
414

            
415
impl ToSource for I64 {
416
995
    fn to_source(&self) -> SourceString {
417
995
        self.source.clone()
418
    }
419
}
420

            
421
impl PartialEq for Float {
422
    fn eq(&self, other: &Self) -> bool {
423
        self.source == other.source
424
    }
425
}
426

            
427
impl Eq for Float {}
428

            
429
#[derive(Clone, Debug, PartialEq, Eq)]
430
pub struct LineTerminator {
431
    pub space0: Whitespace,
432
    pub comment: Option<Comment>,
433
    pub newline: Whitespace,
434
}
435

            
436
#[allow(clippy::large_enum_variant)]
437
#[derive(Clone, Debug, PartialEq, Eq)]
438
pub enum Bytes {
439
    Json(JsonValue),
440
    Xml(String),
441
    MultilineString(MultilineString),
442
    OnelineString(Template),
443
    Base64(Base64),
444
    File(File),
445
    Hex(Hex),
446
}
447

            
448
#[derive(Clone, Debug, PartialEq, Eq)]
449
pub struct Hex {
450
    pub space0: Whitespace,
451
    pub value: Vec<u8>,
452
    pub source: SourceString,
453
    pub space1: Whitespace,
454
}
455

            
456
impl fmt::Display for Hex {
457
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
458
        write!(f, "hex,{}{}{};", self.space0, self.source, self.space1)
459
    }
460
}
461

            
462
/// Literal Regex.
463
#[derive(Clone, Debug)]
464
pub struct Regex {
465
    pub inner: regex::Regex,
466
    pub source: SourceString,
467
}
468

            
469
impl ToSource for Regex {
470
195
    fn to_source(&self) -> SourceString {
471
195
        self.source.clone()
472
    }
473
}
474

            
475
impl fmt::Display for Regex {
476
20
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
477
20
        write!(f, "{}", self.inner)
478
    }
479
}
480

            
481
impl PartialEq for Regex {
482
    fn eq(&self, other: &Self) -> bool {
483
        self.inner.to_string() == other.inner.to_string()
484
    }
485
}
486

            
487
impl Eq for Regex {}
488

            
489
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
490
pub struct SourceInfo {
491
    pub start: Pos,
492
    pub end: Pos,
493
}
494

            
495
impl SourceInfo {
496
1345955
    pub fn new(start: Pos, end: Pos) -> SourceInfo {
497
1345955
        SourceInfo { start, end }
498
    }
499
}
500

            
501
#[derive(Clone, Debug, PartialEq, Eq)]
502
pub struct Placeholder {
503
    pub space0: Whitespace,
504
    pub expr: Expr,
505
    pub space1: Whitespace,
506
}
507

            
508
impl fmt::Display for Placeholder {
509
395
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
510
395
        write!(f, "{}", self.expr)
511
    }
512
}
513

            
514
impl ToSource for Placeholder {
515
1255
    fn to_source(&self) -> SourceString {
516
1255
        let mut source = SourceString::new();
517
1255
        source.push_str("{{");
518
1255
        source.push_str(self.space0.as_str());
519
1255
        source.push_str(self.expr.to_source().as_str());
520
1255
        source.push_str(self.space1.as_str());
521
1255
        source.push_str("}}");
522
1255
        source
523
    }
524
}
525

            
526
#[derive(Clone, Debug, PartialEq, Eq)]
527
pub struct Expr {
528
    pub source_info: SourceInfo,
529
    pub kind: ExprKind,
530
}
531

            
532
impl fmt::Display for Expr {
533
395
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
534
395
        write!(f, "{}", self.kind)
535
    }
536
}
537

            
538
impl ToSource for Expr {
539
1255
    fn to_source(&self) -> SourceString {
540
1255
        self.kind.to_string().to_source()
541
    }
542
}
543

            
544
#[derive(Clone, Debug, PartialEq, Eq)]
545
pub enum ExprKind {
546
    Variable(Variable),
547
    Function(Function),
548
}
549

            
550
impl fmt::Display for ExprKind {
551
1650
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
552
1650
        match self {
553
1610
            ExprKind::Variable(variable) => write!(f, "{variable}"),
554
40
            ExprKind::Function(function) => write!(f, "{function}"),
555
        }
556
    }
557
}
558

            
559
#[derive(Clone, Debug, PartialEq, Eq)]
560
pub struct Variable {
561
    pub name: String,
562
    pub source_info: SourceInfo,
563
}
564

            
565
impl fmt::Display for Variable {
566
1610
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
567
1610
        write!(f, "{}", self.name)
568
    }
569
}
570

            
571
#[derive(Clone, Debug, PartialEq, Eq)]
572
pub enum Function {
573
    NewDate,
574
    NewUuid,
575
}
576

            
577
impl fmt::Display for Function {
578
40
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
579
40
        match self {
580
20
            Function::NewDate => write!(f, "newDate"),
581
20
            Function::NewUuid => write!(f, "newUuid"),
582
        }
583
    }
584
}
585

            
586
#[cfg(test)]
587
mod tests {
588
    use super::*;
589
    use crate::ast::json::{JsonListElement, JsonObjectElement, JsonValue};
590
    use crate::types::ToSource;
591

            
592
    #[test]
593
    fn test_float() {
594
        assert_eq!(
595
            Float {
596
                value: 1.0,
597
                source: "1.0".to_source()
598
            }
599
            .to_source()
600
            .as_str(),
601
            "1.0"
602
        );
603
        assert_eq!(
604
            Float {
605
                value: 1.0,
606
                source: "1.0".to_source()
607
            }
608
            .to_string(),
609
            "1"
610
        );
611

            
612
        assert_eq!(
613
            Float {
614
                value: 1.01,
615
                source: "1.01".to_source()
616
            }
617
            .to_source()
618
            .as_str(),
619
            "1.01"
620
        );
621
        assert_eq!(
622
            Float {
623
                value: 1.01,
624
                source: "1.01".to_source()
625
            }
626
            .to_string(),
627
            "1.01"
628
        );
629

            
630
        assert_eq!(
631
            Float {
632
                value: 1.01,
633
                source: "1.010".to_source()
634
            }
635
            .to_source()
636
            .as_str(),
637
            "1.010"
638
        );
639
        assert_eq!(
640
            Float {
641
                value: 1.01,
642
                source: "1.010".to_source()
643
            }
644
            .to_string(),
645
            "1.01"
646
        );
647

            
648
        assert_eq!(
649
            Float {
650
                value: -1.333,
651
                source: "-1.333".to_source()
652
            }
653
            .to_source()
654
            .as_str(),
655
            "-1.333"
656
        );
657
        assert_eq!(
658
            Float {
659
                value: -1.333,
660
                source: "-1.333".to_source()
661
            }
662
            .to_string(),
663
            "-1.333"
664
        );
665
    }
666

            
667
    fn whitespace() -> Whitespace {
668
        Whitespace {
669
            value: String::new(),
670
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
671
        }
672
    }
673

            
674
    fn variable_placeholder() -> Placeholder {
675
        Placeholder {
676
            space0: whitespace(),
677
            expr: Expr {
678
                kind: ExprKind::Variable(Variable {
679
                    name: "name".to_string(),
680
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
681
                }),
682
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
683
            },
684
            space1: whitespace(),
685
        }
686
    }
687

            
688
    fn hello_template() -> Template {
689
        Template::new(
690
            None,
691
            vec![
692
                TemplateElement::String {
693
                    value: "Hello ".to_string(),
694
                    source: "Hello ".to_source(),
695
                },
696
                TemplateElement::Placeholder(variable_placeholder()),
697
                TemplateElement::String {
698
                    value: "!".to_string(),
699
                    source: "!".to_source(),
700
                },
701
            ],
702
            SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
703
        )
704
    }
705

            
706
    #[test]
707
    fn test_template() {
708
        assert_eq!(hello_template().to_string(), "Hello {{name}}!");
709
    }
710

            
711
    #[test]
712
    fn test_template_to_source() {
713
        assert_eq!(
714
            "{{x}}",
715
            JsonValue::Placeholder(Placeholder {
716
                space0: Whitespace {
717
                    value: String::new(),
718
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
719
                },
720
                expr: Expr {
721
                    kind: ExprKind::Variable(Variable {
722
                        name: "x".to_string(),
723
                        source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
724
                    }),
725
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
726
                },
727
                space1: Whitespace {
728
                    value: String::new(),
729
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
730
                },
731
            })
732
            .to_source()
733
            .as_str()
734
        );
735
        assert_eq!("1", JsonValue::Number("1".to_string()).to_source().as_str());
736
        assert_eq!(
737
            "\"hello\"",
738
            JsonValue::String(Template::new(
739
                Some('"'),
740
                vec![TemplateElement::String {
741
                    value: "hello".to_string(),
742
                    source: "hello".to_source(),
743
                }],
744
                SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0))
745
            ))
746
            .to_source()
747
            .as_str()
748
        );
749
        assert_eq!("true", JsonValue::Boolean(true).to_source().as_str());
750
        assert_eq!(
751
            "[]",
752
            JsonValue::List {
753
                space0: String::new(),
754
                elements: vec![],
755
            }
756
            .to_source()
757
            .as_str()
758
        );
759
        assert_eq!(
760
            "[1, 2, 3]",
761
            JsonValue::List {
762
                space0: String::new(),
763
                elements: vec![
764
                    JsonListElement {
765
                        space0: String::new(),
766
                        value: JsonValue::Number("1".to_string()),
767
                        space1: String::new(),
768
                    },
769
                    JsonListElement {
770
                        space0: " ".to_string(),
771
                        value: JsonValue::Number("2".to_string()),
772
                        space1: String::new(),
773
                    },
774
                    JsonListElement {
775
                        space0: " ".to_string(),
776
                        value: JsonValue::Number("3".to_string()),
777
                        space1: String::new(),
778
                    }
779
                ],
780
            }
781
            .to_source()
782
            .as_str()
783
        );
784
        assert_eq!(
785
            "{}",
786
            JsonValue::Object {
787
                space0: String::new(),
788
                elements: vec![],
789
            }
790
            .to_source()
791
            .as_str()
792
        );
793
        assert_eq!(
794
            "{ \"id\": 123 }",
795
            JsonValue::Object {
796
                space0: String::new(),
797
                elements: vec![JsonObjectElement {
798
                    space0: " ".to_string(),
799
                    name: Template::new(
800
                        Some('"'),
801
                        vec![TemplateElement::String {
802
                            value: "id".to_string(),
803
                            source: "id".to_source(),
804
                        }],
805
                        SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1))
806
                    ),
807
                    space1: String::new(),
808
                    space2: " ".to_string(),
809
                    value: JsonValue::Number("123".to_string()),
810
                    space3: " ".to_string(),
811
                }],
812
            }
813
            .to_source()
814
            .as_str()
815
        );
816
        assert_eq!("null", JsonValue::Null.to_source().as_str());
817

            
818
        assert_eq!(
819
            "{{name}}",
820
            TemplateElement::Placeholder(Placeholder {
821
                space0: Whitespace {
822
                    value: String::new(),
823
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
824
                },
825
                expr: Expr {
826
                    kind: ExprKind::Variable(Variable {
827
                        name: "name".to_string(),
828
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
829
                    }),
830
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
831
                },
832
                space1: Whitespace {
833
                    value: String::new(),
834
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
835
                },
836
            })
837
            .to_source()
838
            .as_str(),
839
        );
840

            
841
        assert_eq!(
842
            "{{name}}",
843
            Template::new(
844
                None,
845
                vec![TemplateElement::Placeholder(Placeholder {
846
                    space0: Whitespace {
847
                        value: String::new(),
848
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
849
                    },
850
                    expr: Expr {
851
                        kind: ExprKind::Variable(Variable {
852
                            name: "name".to_string(),
853
                            source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
854
                        }),
855
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
856
                    },
857
                    space1: Whitespace {
858
                        value: String::new(),
859
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
860
                    },
861
                })],
862
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1))
863
            )
864
            .to_source()
865
            .as_str(),
866
        );
867
    }
868
}