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

            
78
impl MultilineString {
79
610
    pub fn lang(&self) -> &'static str {
80
610
        match self.kind {
81
250
            MultilineStringKind::Text(_) => "",
82
120
            MultilineStringKind::Json(_) => "json",
83
90
            MultilineStringKind::Xml(_) => "xml",
84
150
            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
575
    pub fn has_attributes(&self) -> bool {
99
575
        !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
305
    fn to_source(&self) -> SourceString {
114
305
        match self {
115
125
            MultilineStringKind::Text(value)
116
60
            | MultilineStringKind::Json(value)
117
230
            | MultilineStringKind::Xml(value) => value.to_source(),
118
75
            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
45
    pub fn identifier(&self) -> &'static str {
131
45
        match self {
132
35
            MultilineStringAttribute::Escape => "escape",
133
10
            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
75
    fn to_source(&self) -> SourceString {
156
75
        let mut source = SourceString::new();
157
75
        source.push_str(self.value.to_source().as_str());
158
75
        if let Some(vars) = &self.variables {
159
20
            source.push_str(vars.to_source().as_str());
160
        }
161
75
        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
25
    fn to_source(&self) -> SourceString {
174
25
        let mut source = "variables".to_source();
175
25
        source.push_str(self.space.as_str());
176
25
        source.push_str(self.value.to_source().as_str());
177
25
        source.push_str(self.whitespace.as_str());
178
25
        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
2490
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
206
2490
        let mut buffer = String::new();
207
2620
        for element in self.elements.iter() {
208
2620
            buffer.push_str(element.to_string().as_str());
209
        }
210
2490
        write!(f, "{buffer}")
211
    }
212
}
213

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

            
229
impl Template {
230
    /// Creates a new template.
231
72925
    pub fn new(
232
72925
        delimiter: Option<char>,
233
72925
        elements: Vec<TemplateElement>,
234
72925
        source_info: SourceInfo,
235
72925
    ) -> Template {
236
72925
        Template {
237
72925
            delimiter,
238
72925
            elements,
239
72925
            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
2620
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
252
2620
        let s = match self {
253
2405
            TemplateElement::String { value, .. } => value.clone(),
254
            // TODO: see why we can't need to us `{{` and `}}` in a to_string method
255
215
            TemplateElement::Placeholder(value) => format!("{{{{{value}}}}}"),
256
        };
257
2620
        write!(f, "{s}")
258
    }
259
}
260

            
261
impl ToSource for TemplateElement {
262
11710
    fn to_source(&self) -> SourceString {
263
11710
        match self {
264
11215
            TemplateElement::String { source, .. } => source.clone(),
265
495
            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
impl ToSource for Comment {
277
2045
    fn to_source(&self) -> SourceString {
278
2045
        format!("#{}", self.value).to_source()
279
    }
280
}
281

            
282
#[derive(Clone, Debug, PartialEq, Eq)]
283
pub struct Whitespace {
284
    pub value: String,
285
    pub source_info: SourceInfo,
286
}
287

            
288
impl fmt::Display for Whitespace {
289
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
290
        write!(f, "{}", self.value)
291
    }
292
}
293

            
294
impl Whitespace {
295
70290
    pub fn as_str(&self) -> &str {
296
70290
        &self.value
297
    }
298
}
299

            
300
#[derive(Clone, Debug, PartialEq, Eq)]
301
pub enum Number {
302
    Float(Float),
303
    Integer(I64),
304
    BigInteger(String),
305
}
306

            
307
impl fmt::Display for Number {
308
300
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
309
300
        match self {
310
90
            Number::Float(value) => write!(f, "{}", value),
311
210
            Number::Integer(value) => write!(f, "{}", value),
312
            Number::BigInteger(value) => write!(f, "{}", value),
313
        }
314
    }
315
}
316

            
317
impl ToSource for Number {
318
1015
    fn to_source(&self) -> SourceString {
319
1015
        match self {
320
310
            Number::Float(value) => value.to_source(),
321
700
            Number::Integer(value) => value.to_source(),
322
5
            Number::BigInteger(value) => value.to_source(),
323
        }
324
    }
325
}
326

            
327
// keep Number terminology for both Integer and Decimal Numbers
328
// different representation for the same float value
329
// 1.01 and 1.010
330

            
331
#[derive(Clone, Debug)]
332
pub struct Float {
333
    value: f64,
334
    source: SourceString,
335
}
336

            
337
impl Float {
338
810
    pub fn new(value: f64, source: SourceString) -> Float {
339
810
        Float { value, source }
340
    }
341

            
342
410
    pub fn as_f64(&self) -> f64 {
343
410
        self.value
344
    }
345
}
346

            
347
impl fmt::Display for Float {
348
90
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
349
90
        write!(f, "{}", self.value)
350
    }
351
}
352

            
353
impl ToSource for Float {
354
310
    fn to_source(&self) -> SourceString {
355
310
        self.source.clone()
356
    }
357
}
358

            
359
#[derive(Clone, Debug, PartialEq, Eq)]
360
pub struct U64 {
361
    value: u64,
362
    source: SourceString,
363
}
364

            
365
impl U64 {
366
6135
    pub fn new(value: u64, source: SourceString) -> U64 {
367
6135
        U64 { value, source }
368
    }
369

            
370
5985
    pub fn as_u64(&self) -> u64 {
371
5985
        self.value
372
    }
373
}
374

            
375
impl fmt::Display for U64 {
376
225
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
377
225
        write!(f, "{}", self.value)
378
    }
379
}
380

            
381
impl ToSource for U64 {
382
100
    fn to_source(&self) -> SourceString {
383
100
        self.source.clone()
384
    }
385
}
386

            
387
#[derive(Clone, Debug, PartialEq, Eq)]
388
pub struct I64 {
389
    value: i64,
390
    source: SourceString,
391
}
392

            
393
impl I64 {
394
4470
    pub fn new(value: i64, source: SourceString) -> I64 {
395
4470
        I64 { value, source }
396
    }
397

            
398
3735
    pub fn as_i64(&self) -> i64 {
399
3735
        self.value
400
    }
401
}
402

            
403
impl fmt::Display for I64 {
404
215
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
405
215
        write!(f, "{}", self.value)
406
    }
407
}
408

            
409
impl ToSource for I64 {
410
700
    fn to_source(&self) -> SourceString {
411
700
        self.source.clone()
412
    }
413
}
414

            
415
impl PartialEq for Float {
416
    fn eq(&self, other: &Self) -> bool {
417
        self.source == other.source
418
    }
419
}
420

            
421
impl Eq for Float {}
422

            
423
#[derive(Clone, Debug, PartialEq, Eq)]
424
pub struct LineTerminator {
425
    pub space0: Whitespace,
426
    pub comment: Option<Comment>,
427
    pub newline: Whitespace,
428
}
429

            
430
#[allow(clippy::large_enum_variant)]
431
#[derive(Clone, Debug, PartialEq, Eq)]
432
pub enum Bytes {
433
    Json(JsonValue),
434
    Xml(String),
435
    MultilineString(MultilineString),
436
    OnelineString(Template),
437
    Base64(Base64),
438
    File(File),
439
    Hex(Hex),
440
}
441

            
442
#[derive(Clone, Debug, PartialEq, Eq)]
443
pub struct Hex {
444
    pub space0: Whitespace,
445
    pub value: Vec<u8>,
446
    pub source: SourceString,
447
    pub space1: Whitespace,
448
}
449

            
450
impl fmt::Display for Hex {
451
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
452
        write!(f, "hex,{}{}{};", self.space0, self.source, self.space1)
453
    }
454
}
455

            
456
/// Literal Regex.
457
#[derive(Clone, Debug)]
458
pub struct Regex {
459
    pub inner: regex::Regex,
460
    pub source: SourceString,
461
}
462

            
463
impl ToSource for Regex {
464
135
    fn to_source(&self) -> SourceString {
465
135
        self.source.clone()
466
    }
467
}
468

            
469
impl fmt::Display for Regex {
470
20
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
471
20
        write!(f, "{}", self.inner)
472
    }
473
}
474

            
475
impl PartialEq for Regex {
476
    fn eq(&self, other: &Self) -> bool {
477
        self.inner.to_string() == other.inner.to_string()
478
    }
479
}
480

            
481
impl Eq for Regex {}
482

            
483
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
484
pub struct SourceInfo {
485
    pub start: Pos,
486
    pub end: Pos,
487
}
488

            
489
impl SourceInfo {
490
1214330
    pub fn new(start: Pos, end: Pos) -> SourceInfo {
491
1214330
        SourceInfo { start, end }
492
    }
493
}
494

            
495
#[derive(Clone, Debug, PartialEq, Eq)]
496
pub struct Placeholder {
497
    pub space0: Whitespace,
498
    pub expr: Expr,
499
    pub space1: Whitespace,
500
}
501

            
502
impl fmt::Display for Placeholder {
503
375
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
504
375
        write!(f, "{}", self.expr)
505
    }
506
}
507

            
508
impl ToSource for Placeholder {
509
880
    fn to_source(&self) -> SourceString {
510
880
        let mut source = SourceString::new();
511
880
        source.push_str("{{");
512
880
        source.push_str(self.space0.as_str());
513
880
        source.push_str(self.expr.to_source().as_str());
514
880
        source.push_str(self.space1.as_str());
515
880
        source.push_str("}}");
516
880
        source
517
    }
518
}
519

            
520
#[derive(Clone, Debug, PartialEq, Eq)]
521
pub struct Expr {
522
    pub source_info: SourceInfo,
523
    pub kind: ExprKind,
524
}
525

            
526
impl fmt::Display for Expr {
527
375
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
528
375
        write!(f, "{}", self.kind)
529
    }
530
}
531

            
532
impl ToSource for Expr {
533
880
    fn to_source(&self) -> SourceString {
534
880
        self.kind.to_string().to_source()
535
    }
536
}
537

            
538
#[derive(Clone, Debug, PartialEq, Eq)]
539
pub enum ExprKind {
540
    Variable(Variable),
541
    Function(Function),
542
}
543

            
544
impl fmt::Display for ExprKind {
545
1255
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
546
1255
        match self {
547
1225
            ExprKind::Variable(variable) => write!(f, "{}", variable),
548
30
            ExprKind::Function(function) => write!(f, "{}", function),
549
        }
550
    }
551
}
552

            
553
#[derive(Clone, Debug, PartialEq, Eq)]
554
pub struct Variable {
555
    pub name: String,
556
    pub source_info: SourceInfo,
557
}
558

            
559
impl fmt::Display for Variable {
560
1225
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
561
1225
        write!(f, "{}", self.name)
562
    }
563
}
564

            
565
#[derive(Clone, Debug, PartialEq, Eq)]
566
pub enum Function {
567
    NewDate,
568
    NewUuid,
569
}
570

            
571
impl fmt::Display for Function {
572
30
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
573
30
        match self {
574
15
            Function::NewDate => write!(f, "newDate"),
575
15
            Function::NewUuid => write!(f, "newUuid"),
576
        }
577
    }
578
}
579

            
580
#[cfg(test)]
581
mod tests {
582
    use super::*;
583
    use crate::ast::json::{JsonListElement, JsonObjectElement, JsonValue};
584
    use crate::typing::ToSource;
585

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

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

            
624
        assert_eq!(
625
            Float {
626
                value: 1.01,
627
                source: "1.010".to_source()
628
            }
629
            .to_source()
630
            .as_str(),
631
            "1.010"
632
        );
633
        assert_eq!(
634
            Float {
635
                value: 1.01,
636
                source: "1.010".to_source()
637
            }
638
            .to_string(),
639
            "1.01"
640
        );
641

            
642
        assert_eq!(
643
            Float {
644
                value: -1.333,
645
                source: "-1.333".to_source()
646
            }
647
            .to_source()
648
            .as_str(),
649
            "-1.333"
650
        );
651
        assert_eq!(
652
            Float {
653
                value: -1.333,
654
                source: "-1.333".to_source()
655
            }
656
            .to_string(),
657
            "-1.333"
658
        );
659
    }
660

            
661
    fn whitespace() -> Whitespace {
662
        Whitespace {
663
            value: String::new(),
664
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
665
        }
666
    }
667

            
668
    fn variable_placeholder() -> Placeholder {
669
        Placeholder {
670
            space0: whitespace(),
671
            expr: Expr {
672
                kind: ExprKind::Variable(Variable {
673
                    name: "name".to_string(),
674
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
675
                }),
676
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
677
            },
678
            space1: whitespace(),
679
        }
680
    }
681

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

            
700
    #[test]
701
    fn test_template() {
702
        assert_eq!(hello_template().to_string(), "Hello {{name}}!");
703
    }
704

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

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

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