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
60
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
46
60
        match &self.kind {
47
25
            MultilineStringKind::Text(value)
48
15
            | MultilineStringKind::Json(value)
49
50
            | MultilineStringKind::Xml(value) => write!(f, "{value}"),
50
10
            MultilineStringKind::GraphQl(value) => write!(f, "{value}"),
51
        }
52
    }
53
}
54

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

            
78
impl MultilineString {
79
510
    pub fn lang(&self) -> &'static str {
80
510
        match self.kind {
81
220
            MultilineStringKind::Text(_) => "",
82
100
            MultilineStringKind::Json(_) => "json",
83
75
            MultilineStringKind::Xml(_) => "xml",
84
115
            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
395
    pub fn has_attributes(&self) -> bool {
99
395
        !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
200
    fn to_source(&self) -> SourceString {
114
200
        match self {
115
80
            MultilineStringKind::Text(value)
116
40
            | MultilineStringKind::Json(value)
117
150
            | MultilineStringKind::Xml(value) => value.to_source(),
118
50
            MultilineStringKind::GraphQl(value) => value.to_source(),
119
        }
120
    }
121
}
122

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

            
129
impl ToSource for MultilineStringAttribute {
130
25
    fn to_source(&self) -> SourceString {
131
25
        match self {
132
20
            MultilineStringAttribute::Escape => "escape".to_source(),
133
5
            MultilineStringAttribute::NoVariable => "novariable".to_source(),
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
10
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
146
10
        write!(f, "{}", self.value)?;
147
10
        if let Some(vars) = &self.variables {
148
            write!(f, "{}", vars.to_source())?;
149
        }
150
10
        Ok(())
151
    }
152
}
153

            
154
impl ToSource for GraphQl {
155
50
    fn to_source(&self) -> SourceString {
156
50
        let mut source = SourceString::new();
157
50
        source.push_str(self.value.to_source().as_str());
158
50
        if let Some(vars) = &self.variables {
159
10
            source.push_str(vars.to_source().as_str());
160
        }
161
50
        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
10
    fn to_source(&self) -> SourceString {
174
10
        let mut source = "variable".to_source();
175
10
        source.push_str(self.space.as_str());
176
10
        source.push_str(self.value.to_source().as_str());
177
10
        source.push_str(self.whitespace.as_str());
178
10
        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
2660
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
206
2660
        let mut buffer = String::new();
207
2790
        for element in self.elements.iter() {
208
2790
            buffer.push_str(element.to_string().as_str());
209
        }
210
2660
        write!(f, "{buffer}")
211
    }
212
}
213

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

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

            
244
#[derive(Clone, Debug, PartialEq, Eq)]
245
pub enum TemplateElement {
246
    String { value: String, source: SourceString },
247
    Placeholder(Placeholder),
248
}
249

            
250
impl fmt::Display for TemplateElement {
251
2790
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
252
2790
        let s = match self {
253
2550
            TemplateElement::String { value, .. } => value.clone(),
254
            // TODO: see why we can't need to us `{{` and `}}` in a to_string method
255
240
            TemplateElement::Placeholder(value) => format!("{{{{{value}}}}}"),
256
        };
257
2790
        write!(f, "{s}")
258
    }
259
}
260

            
261
impl ToSource for TemplateElement {
262
8995
    fn to_source(&self) -> SourceString {
263
8995
        match self {
264
8700
            TemplateElement::String { source, .. } => source.clone(),
265
295
            TemplateElement::Placeholder(value) => value.to_source(),
266
        }
267
    }
268
}
269

            
270
#[derive(Clone, Debug, PartialEq, Eq)]
271
pub struct Comment {
272
    pub value: String,
273
    pub source_info: SourceInfo,
274
}
275

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

            
282
impl fmt::Display for Whitespace {
283
90
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
284
90
        write!(f, "{}", self.value)
285
    }
286
}
287

            
288
impl Whitespace {
289
14385
    pub fn as_str(&self) -> &str {
290
14385
        &self.value
291
    }
292
}
293

            
294
#[derive(Clone, Debug, PartialEq, Eq)]
295
pub enum Number {
296
    Float(Float),
297
    Integer(I64),
298
    BigInteger(String),
299
}
300

            
301
impl fmt::Display for Number {
302
300
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
303
300
        match self {
304
90
            Number::Float(value) => write!(f, "{}", value),
305
210
            Number::Integer(value) => write!(f, "{}", value),
306
            Number::BigInteger(value) => write!(f, "{}", value),
307
        }
308
    }
309
}
310

            
311
impl ToSource for Number {
312
995
    fn to_source(&self) -> SourceString {
313
995
        match self {
314
310
            Number::Float(value) => value.to_source(),
315
680
            Number::Integer(value) => value.to_source(),
316
5
            Number::BigInteger(value) => value.to_source(),
317
        }
318
    }
319
}
320

            
321
// keep Number terminology for both Integer and Decimal Numbers
322
// different representation for the same float value
323
// 1.01 and 1.010
324

            
325
#[derive(Clone, Debug)]
326
pub struct Float {
327
    value: f64,
328
    source: SourceString,
329
}
330

            
331
impl Float {
332
810
    pub fn new(value: f64, source: SourceString) -> Float {
333
810
        Float { value, source }
334
    }
335

            
336
410
    pub fn as_f64(&self) -> f64 {
337
410
        self.value
338
    }
339
}
340

            
341
impl fmt::Display for Float {
342
90
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
343
90
        write!(f, "{}", self.value)
344
    }
345
}
346

            
347
impl ToSource for Float {
348
310
    fn to_source(&self) -> SourceString {
349
310
        self.source.clone()
350
    }
351
}
352

            
353
#[derive(Clone, Debug, PartialEq, Eq)]
354
pub struct U64 {
355
    value: u64,
356
    source: SourceString,
357
}
358

            
359
impl U64 {
360
6035
    pub fn new(value: u64, source: SourceString) -> U64 {
361
6035
        U64 { value, source }
362
    }
363

            
364
5885
    pub fn as_u64(&self) -> u64 {
365
5885
        self.value
366
    }
367
}
368

            
369
impl fmt::Display for U64 {
370
195
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
371
195
        write!(f, "{}", self.value)
372
    }
373
}
374

            
375
impl ToSource for U64 {
376
100
    fn to_source(&self) -> SourceString {
377
100
        self.source.clone()
378
    }
379
}
380

            
381
#[derive(Clone, Debug, PartialEq, Eq)]
382
pub struct I64 {
383
    value: i64,
384
    source: SourceString,
385
}
386

            
387
impl I64 {
388
4395
    pub fn new(value: i64, source: SourceString) -> I64 {
389
4395
        I64 { value, source }
390
    }
391

            
392
3580
    pub fn as_i64(&self) -> i64 {
393
3580
        self.value
394
    }
395
}
396

            
397
impl fmt::Display for I64 {
398
215
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
399
215
        write!(f, "{}", self.value)
400
    }
401
}
402

            
403
impl ToSource for I64 {
404
810
    fn to_source(&self) -> SourceString {
405
810
        self.source.clone()
406
    }
407
}
408

            
409
impl PartialEq for Float {
410
    fn eq(&self, other: &Self) -> bool {
411
        self.source == other.source
412
    }
413
}
414

            
415
impl Eq for Float {}
416

            
417
#[derive(Clone, Debug, PartialEq, Eq)]
418
pub struct LineTerminator {
419
    pub space0: Whitespace,
420
    pub comment: Option<Comment>,
421
    pub newline: Whitespace,
422
}
423

            
424
#[allow(clippy::large_enum_variant)]
425
#[derive(Clone, Debug, PartialEq, Eq)]
426
pub enum Bytes {
427
    Json(JsonValue),
428
    Xml(String),
429
    MultilineString(MultilineString),
430
    OnelineString(Template),
431
    Base64(Base64),
432
    File(File),
433
    Hex(Hex),
434
}
435

            
436
#[derive(Clone, Debug, PartialEq, Eq)]
437
pub struct Hex {
438
    pub space0: Whitespace,
439
    pub value: Vec<u8>,
440
    pub source: SourceString,
441
    pub space1: Whitespace,
442
}
443

            
444
impl fmt::Display for Hex {
445
45
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
446
45
        write!(f, "hex,{}{}{};", self.space0, self.source, self.space1)
447
    }
448
}
449

            
450
/// Literal Regex.
451
#[derive(Clone, Debug)]
452
pub struct Regex {
453
    pub inner: regex::Regex,
454
    pub source: SourceString,
455
}
456

            
457
impl ToSource for Regex {
458
100
    fn to_source(&self) -> SourceString {
459
100
        self.source.clone()
460
    }
461
}
462

            
463
impl fmt::Display for Regex {
464
10
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
465
10
        write!(f, "{}", self.inner)
466
    }
467
}
468

            
469
impl PartialEq for Regex {
470
    fn eq(&self, other: &Self) -> bool {
471
        self.inner.to_string() == other.inner.to_string()
472
    }
473
}
474

            
475
impl Eq for Regex {}
476

            
477
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
478
pub struct SourceInfo {
479
    pub start: Pos,
480
    pub end: Pos,
481
}
482

            
483
impl SourceInfo {
484
1196900
    pub fn new(start: Pos, end: Pos) -> SourceInfo {
485
1196900
        SourceInfo { start, end }
486
    }
487
}
488

            
489
#[derive(Clone, Debug, PartialEq, Eq)]
490
pub struct Placeholder {
491
    pub space0: Whitespace,
492
    pub expr: Expr,
493
    pub space1: Whitespace,
494
}
495

            
496
impl fmt::Display for Placeholder {
497
395
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
498
395
        write!(f, "{}", self.expr)
499
    }
500
}
501

            
502
impl ToSource for Placeholder {
503
555
    fn to_source(&self) -> SourceString {
504
555
        let mut source = SourceString::new();
505
555
        source.push_str("{{");
506
555
        source.push_str(self.space0.as_str());
507
555
        source.push_str(self.expr.to_source().as_str());
508
555
        source.push_str(self.space1.as_str());
509
555
        source.push_str("}}");
510
555
        source
511
    }
512
}
513

            
514
#[derive(Clone, Debug, PartialEq, Eq)]
515
pub struct Expr {
516
    pub source_info: SourceInfo,
517
    pub kind: ExprKind,
518
}
519

            
520
impl fmt::Display for Expr {
521
395
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
522
395
        write!(f, "{}", self.kind)
523
    }
524
}
525

            
526
impl ToSource for Expr {
527
555
    fn to_source(&self) -> SourceString {
528
555
        self.kind.to_string().to_source()
529
    }
530
}
531

            
532
#[derive(Clone, Debug, PartialEq, Eq)]
533
pub enum ExprKind {
534
    Variable(Variable),
535
    Function(Function),
536
}
537

            
538
impl fmt::Display for ExprKind {
539
950
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
540
950
        match self {
541
930
            ExprKind::Variable(variable) => write!(f, "{}", variable),
542
20
            ExprKind::Function(function) => write!(f, "{}", function),
543
        }
544
    }
545
}
546

            
547
#[derive(Clone, Debug, PartialEq, Eq)]
548
pub struct Variable {
549
    pub name: String,
550
    pub source_info: SourceInfo,
551
}
552

            
553
impl fmt::Display for Variable {
554
930
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
555
930
        write!(f, "{}", self.name)
556
    }
557
}
558

            
559
#[derive(Clone, Debug, PartialEq, Eq)]
560
pub enum Function {
561
    NewDate,
562
    NewUuid,
563
}
564

            
565
impl fmt::Display for Function {
566
20
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
567
20
        match self {
568
10
            Function::NewDate => write!(f, "newDate"),
569
10
            Function::NewUuid => write!(f, "newUuid"),
570
        }
571
    }
572
}
573

            
574
#[cfg(test)]
575
mod tests {
576
    use super::*;
577
    use crate::ast::json::{JsonListElement, JsonObjectElement, JsonValue};
578
    use crate::typing::ToSource;
579

            
580
    #[test]
581
    fn test_float() {
582
        assert_eq!(
583
            Float {
584
                value: 1.0,
585
                source: "1.0".to_source()
586
            }
587
            .to_source()
588
            .as_str(),
589
            "1.0"
590
        );
591
        assert_eq!(
592
            Float {
593
                value: 1.0,
594
                source: "1.0".to_source()
595
            }
596
            .to_string(),
597
            "1"
598
        );
599

            
600
        assert_eq!(
601
            Float {
602
                value: 1.01,
603
                source: "1.01".to_source()
604
            }
605
            .to_source()
606
            .as_str(),
607
            "1.01"
608
        );
609
        assert_eq!(
610
            Float {
611
                value: 1.01,
612
                source: "1.01".to_source()
613
            }
614
            .to_string(),
615
            "1.01"
616
        );
617

            
618
        assert_eq!(
619
            Float {
620
                value: 1.01,
621
                source: "1.010".to_source()
622
            }
623
            .to_source()
624
            .as_str(),
625
            "1.010"
626
        );
627
        assert_eq!(
628
            Float {
629
                value: 1.01,
630
                source: "1.010".to_source()
631
            }
632
            .to_string(),
633
            "1.01"
634
        );
635

            
636
        assert_eq!(
637
            Float {
638
                value: -1.333,
639
                source: "-1.333".to_source()
640
            }
641
            .to_source()
642
            .as_str(),
643
            "-1.333"
644
        );
645
        assert_eq!(
646
            Float {
647
                value: -1.333,
648
                source: "-1.333".to_source()
649
            }
650
            .to_string(),
651
            "-1.333"
652
        );
653
    }
654

            
655
    fn whitespace() -> Whitespace {
656
        Whitespace {
657
            value: String::new(),
658
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
659
        }
660
    }
661

            
662
    fn variable_placeholder() -> Placeholder {
663
        Placeholder {
664
            space0: whitespace(),
665
            expr: Expr {
666
                kind: ExprKind::Variable(Variable {
667
                    name: "name".to_string(),
668
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
669
                }),
670
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
671
            },
672
            space1: whitespace(),
673
        }
674
    }
675

            
676
    fn hello_template() -> Template {
677
        Template::new(
678
            None,
679
            vec![
680
                TemplateElement::String {
681
                    value: "Hello ".to_string(),
682
                    source: "Hello ".to_source(),
683
                },
684
                TemplateElement::Placeholder(variable_placeholder()),
685
                TemplateElement::String {
686
                    value: "!".to_string(),
687
                    source: "!".to_source(),
688
                },
689
            ],
690
            SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
691
        )
692
    }
693

            
694
    #[test]
695
    fn test_template() {
696
        assert_eq!(hello_template().to_string(), "Hello {{name}}!");
697
    }
698

            
699
    #[test]
700
    fn test_template_to_source() {
701
        assert_eq!(
702
            "{{x}}",
703
            JsonValue::Placeholder(Placeholder {
704
                space0: Whitespace {
705
                    value: String::new(),
706
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
707
                },
708
                expr: Expr {
709
                    kind: ExprKind::Variable(Variable {
710
                        name: "x".to_string(),
711
                        source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
712
                    }),
713
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
714
                },
715
                space1: Whitespace {
716
                    value: String::new(),
717
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
718
                },
719
            })
720
            .to_source()
721
            .as_str()
722
        );
723
        assert_eq!("1", JsonValue::Number("1".to_string()).to_source().as_str());
724
        assert_eq!(
725
            "\"hello\"",
726
            JsonValue::String(Template::new(
727
                Some('"'),
728
                vec![TemplateElement::String {
729
                    value: "hello".to_string(),
730
                    source: "hello".to_source(),
731
                }],
732
                SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0))
733
            ))
734
            .to_source()
735
            .as_str()
736
        );
737
        assert_eq!("true", JsonValue::Boolean(true).to_source().as_str());
738
        assert_eq!(
739
            "[]",
740
            JsonValue::List {
741
                space0: String::new(),
742
                elements: vec![],
743
            }
744
            .to_source()
745
            .as_str()
746
        );
747
        assert_eq!(
748
            "[1, 2, 3]",
749
            JsonValue::List {
750
                space0: String::new(),
751
                elements: vec![
752
                    JsonListElement {
753
                        space0: String::new(),
754
                        value: JsonValue::Number("1".to_string()),
755
                        space1: String::new(),
756
                    },
757
                    JsonListElement {
758
                        space0: " ".to_string(),
759
                        value: JsonValue::Number("2".to_string()),
760
                        space1: String::new(),
761
                    },
762
                    JsonListElement {
763
                        space0: " ".to_string(),
764
                        value: JsonValue::Number("3".to_string()),
765
                        space1: String::new(),
766
                    }
767
                ],
768
            }
769
            .to_source()
770
            .as_str()
771
        );
772
        assert_eq!(
773
            "{}",
774
            JsonValue::Object {
775
                space0: String::new(),
776
                elements: vec![],
777
            }
778
            .to_source()
779
            .as_str()
780
        );
781
        assert_eq!(
782
            "{ \"id\": 123 }",
783
            JsonValue::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: JsonValue::Number("123".to_string()),
798
                    space3: " ".to_string(),
799
                }],
800
            }
801
            .to_source()
802
            .as_str()
803
        );
804
        assert_eq!("null", JsonValue::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
}