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 crate::types::{SourceString, ToSource};
19

            
20
use super::primitive::{Placeholder, SourceInfo, Template};
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 struct JsonValue {
38
    source_info: SourceInfo,
39
    kind: JsonValueKind,
40
}
41

            
42
impl JsonValue {
43
4520
    pub fn new(source_info: SourceInfo, kind: JsonValueKind) -> Self {
44
4520
        Self { source_info, kind }
45
    }
46

            
47
    pub fn source_info(&self) -> &SourceInfo {
48
        &self.source_info
49
    }
50

            
51
3372
    pub fn kind(&self) -> &JsonValueKind {
52
3372
        &self.kind
53
    }
54
}
55

            
56
#[derive(Clone, Debug, PartialEq, Eq)]
57
pub enum JsonValueKind {
58
    Placeholder(Placeholder),
59
    Number(String),
60
    String(Template),
61
    Boolean(bool),
62
    List {
63
        space0: String,
64
        elements: Vec<JsonListElement>,
65
    },
66
    Object {
67
        space0: String,
68
        elements: Vec<JsonObjectElement>,
69
    },
70
    Null,
71
}
72

            
73
impl ToSource for JsonValue {
74
1108
    fn to_source(&self) -> SourceString {
75
1108
        match &self.kind {
76
12
            JsonValueKind::Placeholder(expr) => format!("{{{{{expr}}}}}").to_source(),
77
352
            JsonValueKind::Number(s) => s.to_source(),
78
360
            JsonValueKind::String(template) => template.to_source(),
79
16
            JsonValueKind::Boolean(value) => {
80
16
                if *value {
81
                    "true".to_source()
82
                } else {
83
16
                    "false".to_source()
84
                }
85
            }
86
148
            JsonValueKind::List { space0, elements } => {
87
148
                let elements = elements
88
148
                    .iter()
89
428
                    .map(|e| e.to_source())
90
148
                    .collect::<Vec<SourceString>>();
91
148
                format!("[{}{}]", space0, elements.join(",")).to_source()
92
            }
93
204
            JsonValueKind::Object { space0, elements } => {
94
204
                let elements = elements
95
204
                    .iter()
96
576
                    .map(|e| e.to_source())
97
204
                    .collect::<Vec<SourceString>>();
98
204
                format!("{{{}{}}}", space0, elements.join(",")).to_source()
99
            }
100
16
            JsonValueKind::Null => "null".to_source(),
101
        }
102
    }
103
}
104

            
105
#[derive(Clone, Debug, PartialEq, Eq)]
106
pub struct JsonListElement {
107
    pub space0: String,
108
    pub value: JsonValue,
109
    pub space1: String,
110
}
111

            
112
impl ToSource for JsonListElement {
113
428
    fn to_source(&self) -> SourceString {
114
428
        let mut s = SourceString::new();
115
428
        s.push_str(self.space0.as_str());
116
428
        s.push_str(self.value.to_source().as_str());
117
428
        s.push_str(self.space1.as_str());
118
428
        s
119
    }
120
}
121

            
122
#[derive(Clone, Debug, PartialEq, Eq)]
123
pub struct JsonObjectElement {
124
    pub space0: String,
125
    pub name: Template,
126
    pub space1: String,
127
    pub space2: String,
128
    pub value: JsonValue,
129
    pub space3: String,
130
}
131

            
132
impl ToSource for JsonObjectElement {
133
576
    fn to_source(&self) -> SourceString {
134
576
        let mut s = SourceString::new();
135
576
        s.push_str(self.space0.as_str());
136
576
        s.push_str(self.name.to_source().as_str());
137
576
        s.push_str(self.space1.as_str());
138
576
        s.push(':');
139
576
        s.push_str(self.space2.as_str());
140
576
        s.push_str(self.value.to_source().as_str());
141
576
        s.push_str(self.space3.as_str());
142
576
        s
143
    }
144
}