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 space: Whitespace,
40
    pub newline: Whitespace,
41
    pub kind: MultilineStringKind,
42
}
43

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

            
56
impl ToSource for MultilineString {
57
372
    fn to_source(&self) -> SourceString {
58
372
        let mut source = SourceString::new();
59
372
        source.push_str("```");
60
372
        source.push_str(self.lang());
61
372
        source.push_str(self.space.as_str());
62
372
        source.push_str(self.newline.as_str());
63
372
        source.push_str(self.kind.to_source().as_str());
64
372
        source.push_str("```");
65
372
        source
66
    }
67
}
68

            
69
impl MultilineString {
70
372
    pub fn lang(&self) -> &'static str {
71
372
        match self.kind {
72
124
            MultilineStringKind::Text(_) => "",
73
64
            MultilineStringKind::Json(_) => "json",
74
48
            MultilineStringKind::Xml(_) => "xml",
75
60
            MultilineStringKind::Raw(_) => "raw",
76
76
            MultilineStringKind::GraphQl(_) => "graphql",
77
        }
78
    }
79

            
80
196
    pub fn value(&self) -> Template {
81
196
        match &self.kind {
82
116
            MultilineStringKind::Text(text)
83
20
            | MultilineStringKind::Json(text)
84
156
            | MultilineStringKind::Xml(text) => text.clone(),
85
20
            MultilineStringKind::Raw(text) => text.clone(),
86
20
            MultilineStringKind::GraphQl(text) => text.value.clone(),
87
        }
88
    }
89
}
90

            
91
#[allow(clippy::large_enum_variant)]
92
#[derive(Clone, Debug, PartialEq, Eq)]
93
pub enum MultilineStringKind {
94
    Text(Template),
95
    Json(Template),
96
    Xml(Template),
97
    Raw(Template),
98
    GraphQl(GraphQl),
99
}
100

            
101
impl ToSource for MultilineStringKind {
102
372
    fn to_source(&self) -> SourceString {
103
372
        match self {
104
124
            MultilineStringKind::Text(value)
105
64
            | MultilineStringKind::Json(value)
106
236
            | MultilineStringKind::Xml(value) => value.to_source(),
107
60
            MultilineStringKind::Raw(value) => value.to_source(),
108
76
            MultilineStringKind::GraphQl(value) => value.to_source(),
109
        }
110
    }
111
}
112

            
113
#[derive(Clone, Debug, PartialEq, Eq)]
114
pub struct GraphQl {
115
    pub value: Template,
116
    pub variables: Option<GraphQlVariables>,
117
}
118

            
119
impl fmt::Display for GraphQl {
120
12
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
121
12
        write!(f, "{}", self.value)?;
122
12
        if let Some(vars) = &self.variables {
123
4
            write!(f, "{}", vars.to_source())?;
124
        }
125
12
        Ok(())
126
    }
127
}
128

            
129
impl ToSource for GraphQl {
130
76
    fn to_source(&self) -> SourceString {
131
76
        let mut source = SourceString::new();
132
76
        source.push_str(self.value.to_source().as_str());
133
76
        if let Some(vars) = &self.variables {
134
20
            source.push_str(vars.to_source().as_str());
135
        }
136
76
        source
137
    }
138
}
139

            
140
#[derive(Clone, Debug, PartialEq, Eq)]
141
pub struct GraphQlVariables {
142
    pub space: Whitespace,
143
    pub value: JsonValue,
144
    pub whitespace: Whitespace,
145
}
146

            
147
impl ToSource for GraphQlVariables {
148
24
    fn to_source(&self) -> SourceString {
149
24
        let mut source = "variables".to_source();
150
24
        source.push_str(self.space.as_str());
151
24
        source.push_str(self.value.to_source().as_str());
152
24
        source.push_str(self.whitespace.as_str());
153
24
        source
154
    }
155
}
156

            
157
#[derive(Clone, Debug, PartialEq, Eq)]
158
pub struct Base64 {
159
    pub space0: Whitespace,
160
    pub value: Vec<u8>,
161
    pub source: SourceString,
162
    pub space1: Whitespace,
163
}
164

            
165
#[derive(Clone, Debug, PartialEq, Eq)]
166
pub struct File {
167
    pub space0: Whitespace,
168
    pub filename: Template,
169
    pub space1: Whitespace,
170
}
171

            
172
#[derive(Clone, Debug, PartialEq, Eq)]
173
pub struct Template {
174
    pub delimiter: Option<char>,
175
    pub elements: Vec<TemplateElement>,
176
    pub source_info: SourceInfo,
177
}
178

            
179
impl fmt::Display for Template {
180
2260
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
181
2260
        let mut buffer = String::new();
182
2332
        for element in self.elements.iter() {
183
2332
            buffer.push_str(element.to_string().as_str());
184
        }
185
2260
        write!(f, "{buffer}")
186
    }
187
}
188

            
189
impl ToSource for Template {
190
12420
    fn to_source(&self) -> SourceString {
191
12420
        let mut s = SourceString::new();
192
12420
        if let Some(d) = self.delimiter {
193
6444
            s.push(d);
194
        }
195
12520
        let elements: Vec<SourceString> = self.elements.iter().map(|e| e.to_source()).collect();
196
12420
        s.push_str(elements.join("").as_str());
197
12420
        if let Some(d) = self.delimiter {
198
6444
            s.push(d);
199
        }
200
12420
        s
201
    }
202
}
203

            
204
impl Template {
205
    /// Creates a new template.
206
74464
    pub fn new(
207
74464
        delimiter: Option<char>,
208
74464
        elements: Vec<TemplateElement>,
209
74464
        source_info: SourceInfo,
210
74464
    ) -> Template {
211
74464
        Template {
212
74464
            delimiter,
213
74464
            elements,
214
74464
            source_info,
215
        }
216
    }
217

            
218
    /// Returns true if this template is empty.
219
176
    pub fn is_empty(&self) -> bool {
220
176
        self.elements.is_empty()
221
    }
222
}
223

            
224
#[derive(Clone, Debug, PartialEq, Eq)]
225
pub enum TemplateElement {
226
    String { value: String, source: SourceString },
227
    Placeholder(Placeholder),
228
}
229

            
230
impl fmt::Display for TemplateElement {
231
2332
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
232
2332
        let s = match self {
233
2156
            TemplateElement::String { value, .. } => value.clone(),
234
            // TODO: see why we can't need to us `{{` and `}}` in a to_string method
235
176
            TemplateElement::Placeholder(value) => format!("{{{{{value}}}}}"),
236
        };
237
2332
        write!(f, "{s}")
238
    }
239
}
240

            
241
impl ToSource for TemplateElement {
242
12520
    fn to_source(&self) -> SourceString {
243
12520
        match self {
244
11976
            TemplateElement::String { source, .. } => source.clone(),
245
544
            TemplateElement::Placeholder(value) => value.to_source(),
246
        }
247
    }
248
}
249

            
250
#[derive(Clone, Debug, PartialEq, Eq)]
251
pub struct Comment {
252
    pub value: String,
253
    pub source_info: SourceInfo,
254
}
255

            
256
impl ToSource for Comment {
257
1888
    fn to_source(&self) -> SourceString {
258
1888
        format!("#{}", self.value).to_source()
259
    }
260
}
261

            
262
#[derive(Clone, Debug, PartialEq, Eq)]
263
pub struct Whitespace {
264
    pub value: String,
265
    pub source_info: SourceInfo,
266
}
267

            
268
impl fmt::Display for Whitespace {
269
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
270
        write!(f, "{}", self.value)
271
    }
272
}
273

            
274
impl Whitespace {
275
64044
    pub fn as_str(&self) -> &str {
276
64044
        &self.value
277
    }
278
}
279

            
280
#[derive(Clone, Debug, PartialEq, Eq)]
281
pub enum Number {
282
    Float(Float),
283
    Integer(I64),
284
    BigInteger(String),
285
}
286

            
287
impl fmt::Display for Number {
288
264
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
289
264
        match self {
290
72
            Number::Float(value) => write!(f, "{value}"),
291
192
            Number::Integer(value) => write!(f, "{value}"),
292
            Number::BigInteger(value) => write!(f, "{value}"),
293
        }
294
    }
295
}
296

            
297
impl ToSource for Number {
298
1164
    fn to_source(&self) -> SourceString {
299
1164
        match self {
300
336
            Number::Float(value) => value.to_source(),
301
824
            Number::Integer(value) => value.to_source(),
302
4
            Number::BigInteger(value) => value.to_source(),
303
        }
304
    }
305
}
306

            
307
// keep Number terminology for both Integer and Decimal Numbers
308
// different representation for the same float value
309
// 1.01 and 1.010
310

            
311
#[derive(Clone, Debug)]
312
pub struct Float {
313
    value: f64,
314
    source: SourceString,
315
}
316

            
317
impl Float {
318
792
    pub fn new(value: f64, source: SourceString) -> Float {
319
792
        Float { value, source }
320
    }
321

            
322
348
    pub fn as_f64(&self) -> f64 {
323
348
        self.value
324
    }
325
}
326

            
327
impl fmt::Display for Float {
328
72
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
329
72
        write!(f, "{}", self.value)
330
    }
331
}
332

            
333
impl ToSource for Float {
334
336
    fn to_source(&self) -> SourceString {
335
336
        self.source.clone()
336
    }
337
}
338

            
339
#[derive(Clone, Debug, PartialEq, Eq)]
340
pub struct U64 {
341
    value: u64,
342
    source: SourceString,
343
}
344

            
345
impl U64 {
346
632
    pub fn new(value: u64, source: SourceString) -> U64 {
347
632
        U64 { value, source }
348
    }
349

            
350
444
    pub fn as_u64(&self) -> u64 {
351
444
        self.value
352
    }
353
}
354

            
355
impl fmt::Display for U64 {
356
488
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
357
488
        write!(f, "{}", self.value)
358
    }
359
}
360

            
361
impl ToSource for U64 {
362
140
    fn to_source(&self) -> SourceString {
363
140
        self.source.clone()
364
    }
365
}
366

            
367
#[derive(Clone, Debug, PartialEq, Eq)]
368
pub struct I64 {
369
    value: i64,
370
    source: SourceString,
371
}
372

            
373
impl I64 {
374
5740
    pub fn new(value: i64, source: SourceString) -> I64 {
375
5740
        I64 { value, source }
376
    }
377

            
378
4984
    pub fn as_i64(&self) -> i64 {
379
4984
        self.value
380
    }
381
}
382

            
383
impl fmt::Display for I64 {
384
196
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
385
196
        write!(f, "{}", self.value)
386
    }
387
}
388

            
389
impl ToSource for I64 {
390
828
    fn to_source(&self) -> SourceString {
391
828
        self.source.clone()
392
    }
393
}
394

            
395
impl PartialEq for Float {
396
    fn eq(&self, other: &Self) -> bool {
397
        self.source == other.source
398
    }
399
}
400

            
401
impl Eq for Float {}
402

            
403
#[derive(Clone, Debug, PartialEq, Eq)]
404
pub struct LineTerminator {
405
    pub space0: Whitespace,
406
    pub comment: Option<Comment>,
407
    pub newline: Whitespace,
408
}
409

            
410
#[allow(clippy::large_enum_variant)]
411
#[derive(Clone, Debug, PartialEq, Eq)]
412
pub enum Bytes {
413
    Json(JsonValue),
414
    Xml(String),
415
    MultilineString(MultilineString),
416
    OnelineString(Template),
417
    Base64(Base64),
418
    File(File),
419
    Hex(Hex),
420
}
421

            
422
#[derive(Clone, Debug, PartialEq, Eq)]
423
pub struct Hex {
424
    pub space0: Whitespace,
425
    pub value: Vec<u8>,
426
    pub source: SourceString,
427
    pub space1: Whitespace,
428
}
429

            
430
impl fmt::Display for Hex {
431
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
432
        write!(f, "hex,{}{}{};", self.space0, self.source, self.space1)
433
    }
434
}
435

            
436
/// Literal Regex.
437
#[derive(Clone, Debug)]
438
pub struct Regex {
439
    pub inner: regex::Regex,
440
    pub source: SourceString,
441
}
442

            
443
impl ToSource for Regex {
444
156
    fn to_source(&self) -> SourceString {
445
156
        self.source.clone()
446
    }
447
}
448

            
449
impl fmt::Display for Regex {
450
16
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
451
16
        write!(f, "{}", self.inner)
452
    }
453
}
454

            
455
impl PartialEq for Regex {
456
    fn eq(&self, other: &Self) -> bool {
457
        self.inner.to_string() == other.inner.to_string()
458
    }
459
}
460

            
461
impl Eq for Regex {}
462

            
463
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
464
pub struct SourceInfo {
465
    pub start: Pos,
466
    pub end: Pos,
467
}
468

            
469
impl SourceInfo {
470
1254612
    pub fn new(start: Pos, end: Pos) -> SourceInfo {
471
1254612
        SourceInfo { start, end }
472
    }
473
}
474

            
475
#[derive(Clone, Debug, PartialEq, Eq)]
476
pub struct Placeholder {
477
    pub space0: Whitespace,
478
    pub expr: Expr,
479
    pub space1: Whitespace,
480
}
481

            
482
impl fmt::Display for Placeholder {
483
336
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
484
336
        write!(f, "{}", self.expr)
485
    }
486
}
487

            
488
impl ToSource for Placeholder {
489
1008
    fn to_source(&self) -> SourceString {
490
1008
        let mut source = SourceString::new();
491
1008
        source.push_str("{{");
492
1008
        source.push_str(self.space0.as_str());
493
1008
        source.push_str(self.expr.to_source().as_str());
494
1008
        source.push_str(self.space1.as_str());
495
1008
        source.push_str("}}");
496
1008
        source
497
    }
498
}
499

            
500
#[derive(Clone, Debug, PartialEq, Eq)]
501
pub struct Expr {
502
    pub source_info: SourceInfo,
503
    pub kind: ExprKind,
504
}
505

            
506
impl fmt::Display for Expr {
507
336
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
508
336
        write!(f, "{}", self.kind)
509
    }
510
}
511

            
512
impl ToSource for Expr {
513
1008
    fn to_source(&self) -> SourceString {
514
1008
        self.kind.to_string().to_source()
515
    }
516
}
517

            
518
#[derive(Clone, Debug, PartialEq, Eq)]
519
pub enum ExprKind {
520
    Variable(Variable),
521
    Function(Function),
522
}
523

            
524
impl fmt::Display for ExprKind {
525
1344
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
526
1344
        match self {
527
1312
            ExprKind::Variable(variable) => write!(f, "{variable}"),
528
32
            ExprKind::Function(function) => write!(f, "{function}"),
529
        }
530
    }
531
}
532

            
533
#[derive(Clone, Debug, PartialEq, Eq)]
534
pub struct Variable {
535
    pub name: String,
536
    pub source_info: SourceInfo,
537
}
538

            
539
impl fmt::Display for Variable {
540
1312
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
541
1312
        write!(f, "{}", self.name)
542
    }
543
}
544

            
545
#[derive(Clone, Debug, PartialEq, Eq)]
546
pub enum Function {
547
    NewDate,
548
    NewUuid,
549
}
550

            
551
impl fmt::Display for Function {
552
32
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
553
32
        match self {
554
16
            Function::NewDate => write!(f, "newDate"),
555
16
            Function::NewUuid => write!(f, "newUuid"),
556
        }
557
    }
558
}
559

            
560
#[cfg(test)]
561
mod tests {
562
    use super::*;
563
    use crate::ast::json::{JsonListElement, JsonObjectElement, JsonValue, JsonValueKind};
564
    use crate::types::ToSource;
565

            
566
    fn json_value(kind: JsonValueKind) -> JsonValue {
567
        JsonValue::new(SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)), kind)
568
    }
569

            
570
    #[test]
571
    fn test_float() {
572
        assert_eq!(
573
            Float {
574
                value: 1.0,
575
                source: "1.0".to_source()
576
            }
577
            .to_source()
578
            .as_str(),
579
            "1.0"
580
        );
581
        assert_eq!(
582
            Float {
583
                value: 1.0,
584
                source: "1.0".to_source()
585
            }
586
            .to_string(),
587
            "1"
588
        );
589

            
590
        assert_eq!(
591
            Float {
592
                value: 1.01,
593
                source: "1.01".to_source()
594
            }
595
            .to_source()
596
            .as_str(),
597
            "1.01"
598
        );
599
        assert_eq!(
600
            Float {
601
                value: 1.01,
602
                source: "1.01".to_source()
603
            }
604
            .to_string(),
605
            "1.01"
606
        );
607

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

            
626
        assert_eq!(
627
            Float {
628
                value: -1.333,
629
                source: "-1.333".to_source()
630
            }
631
            .to_source()
632
            .as_str(),
633
            "-1.333"
634
        );
635
        assert_eq!(
636
            Float {
637
                value: -1.333,
638
                source: "-1.333".to_source()
639
            }
640
            .to_string(),
641
            "-1.333"
642
        );
643
    }
644

            
645
    fn whitespace() -> Whitespace {
646
        Whitespace {
647
            value: String::new(),
648
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
649
        }
650
    }
651

            
652
    fn variable_placeholder() -> Placeholder {
653
        Placeholder {
654
            space0: whitespace(),
655
            expr: Expr {
656
                kind: ExprKind::Variable(Variable {
657
                    name: "name".to_string(),
658
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
659
                }),
660
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
661
            },
662
            space1: whitespace(),
663
        }
664
    }
665

            
666
    fn hello_template() -> Template {
667
        Template::new(
668
            None,
669
            vec![
670
                TemplateElement::String {
671
                    value: "Hello ".to_string(),
672
                    source: "Hello ".to_source(),
673
                },
674
                TemplateElement::Placeholder(variable_placeholder()),
675
                TemplateElement::String {
676
                    value: "!".to_string(),
677
                    source: "!".to_source(),
678
                },
679
            ],
680
            SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
681
        )
682
    }
683

            
684
    #[test]
685
    fn test_template() {
686
        assert_eq!(hello_template().to_string(), "Hello {{name}}!");
687
    }
688

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

            
806
        assert_eq!(
807
            "{{name}}",
808
            TemplateElement::Placeholder(Placeholder {
809
                space0: Whitespace {
810
                    value: String::new(),
811
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
812
                },
813
                expr: Expr {
814
                    kind: ExprKind::Variable(Variable {
815
                        name: "name".to_string(),
816
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
817
                    }),
818
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
819
                },
820
                space1: Whitespace {
821
                    value: String::new(),
822
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
823
                },
824
            })
825
            .to_source()
826
            .as_str(),
827
        );
828

            
829
        assert_eq!(
830
            "{{name}}",
831
            Template::new(
832
                None,
833
                vec![TemplateElement::Placeholder(Placeholder {
834
                    space0: Whitespace {
835
                        value: String::new(),
836
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
837
                    },
838
                    expr: Expr {
839
                        kind: ExprKind::Variable(Variable {
840
                            name: "name".to_string(),
841
                            source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
842
                        }),
843
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
844
                    },
845
                    space1: Whitespace {
846
                        value: String::new(),
847
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
848
                    },
849
                })],
850
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1))
851
            )
852
            .to_source()
853
            .as_str(),
854
        );
855
    }
856
}