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 crate::ast::primitive::Placeholder;
19
use crate::ast::Template;
20
use crate::typing::{SourceString, ToSource};
21

            
22
/// This the AST for the JSON used within Hurl (for instance in [implicit JSON body request](https://hurl.dev/docs/request.html#json-body)).
23
///
24
/// # Example
25
///
26
/// ```hurl
27
/// POST https://example.org/api/cats
28
/// {
29
///     "id": 42,
30
///     "lives": {{lives_count}},
31
///     "name": "{{name}}"
32
/// }
33
/// ```
34
///
35
/// It is a superset of the standard JSON spec. Strings have been replaced by Hurl [`Placeholder`].
36
#[derive(Clone, Debug, PartialEq, Eq)]
37
pub enum JsonValue {
38
    Placeholder(Placeholder),
39
    Number(String),
40
    String(Template),
41
    Boolean(bool),
42
    List {
43
        space0: String,
44
        elements: Vec<JsonListElement>,
45
    },
46
    Object {
47
        space0: String,
48
        elements: Vec<JsonObjectElement>,
49
    },
50
    Null,
51
}
52

            
53
impl ToSource for JsonValue {
54
1170
    fn to_source(&self) -> SourceString {
55
1170
        match self {
56
15
            JsonValue::Placeholder(expr) => format!("{{{{{expr}}}}}").to_source(),
57
400
            JsonValue::Number(s) => s.to_source(),
58
345
            JsonValue::String(template) => template.to_source(),
59
20
            JsonValue::Boolean(value) => {
60
20
                if *value {
61
                    "true".to_source()
62
                } else {
63
20
                    "false".to_source()
64
                }
65
            }
66
145
            JsonValue::List { space0, elements } => {
67
145
                let elements = elements
68
145
                    .iter()
69
474
                    .map(|e| e.to_source())
70
145
                    .collect::<Vec<SourceString>>();
71
145
                format!("[{}{}]", space0, elements.join(",")).to_source()
72
            }
73
225
            JsonValue::Object { space0, elements } => {
74
225
                let elements = elements
75
225
                    .iter()
76
655
                    .map(|e| e.to_source())
77
225
                    .collect::<Vec<SourceString>>();
78
225
                format!("{{{}{}}}", space0, elements.join(",")).to_source()
79
            }
80
20
            JsonValue::Null => "null".to_source(),
81
        }
82
    }
83
}
84

            
85
#[derive(Clone, Debug, PartialEq, Eq)]
86
pub struct JsonListElement {
87
    pub space0: String,
88
    pub value: JsonValue,
89
    pub space1: String,
90
}
91

            
92
impl ToSource for JsonListElement {
93
445
    fn to_source(&self) -> SourceString {
94
445
        let mut s = SourceString::new();
95
445
        s.push_str(self.space0.as_str());
96
445
        s.push_str(self.value.to_source().as_str());
97
445
        s.push_str(self.space1.as_str());
98
445
        s
99
    }
100
}
101

            
102
#[derive(Clone, Debug, PartialEq, Eq)]
103
pub struct JsonObjectElement {
104
    pub space0: String,
105
    pub name: Template,
106
    pub space1: String,
107
    pub space2: String,
108
    pub value: JsonValue,
109
    pub space3: String,
110
}
111

            
112
impl ToSource for JsonObjectElement {
113
610
    fn to_source(&self) -> SourceString {
114
610
        let mut s = SourceString::new();
115
610
        s.push_str(self.space0.as_str());
116
610
        s.push_str(self.name.to_source().as_str());
117
610
        s.push_str(self.space1.as_str());
118
610
        s.push(':');
119
610
        s.push_str(self.space2.as_str());
120
610
        s.push_str(self.value.to_source().as_str());
121
610
        s.push_str(self.space3.as_str());
122
610
        s
123
    }
124
}