1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2025 Orange
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *          http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
use std::fmt;
19
use std::fmt::Formatter;
20

            
21
use crate::ast::JsonValue;
22
use crate::reader::Pos;
23
use crate::typing::{SourceString, ToSource};
24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
426
impl Eq for Float {}
427

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

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

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

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

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

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

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

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

            
486
impl Eq for Regex {}
487

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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