1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2024 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 core::fmt;
19

            
20
use crate::ast::core::Template;
21
use crate::ast::{Expr, TemplateElement};
22

            
23
///
24
/// This the AST for the JSON used within Hurl
25
///
26
/// It is a superset of the standard json spec.
27
/// Strings have been replaced by Hurl template.
28
///
29
#[derive(Clone, Debug, PartialEq, Eq)]
30
pub enum Value {
31
    Expression(Expr),
32
    Number(String),
33
    String(Template),
34
    Boolean(bool),
35
    List {
36
        space0: String,
37
        elements: Vec<ListElement>,
38
    },
39
    Object {
40
        space0: String,
41
        elements: Vec<ObjectElement>,
42
    },
43
    Null,
44
}
45

            
46
impl Value {
47
    pub fn _type(&self) -> String {
48
        match self {
49
            Value::Number(_) => "number".to_string(),
50
            Value::Null => "null".to_string(),
51
            Value::Boolean(_) => "boolean".to_string(),
52
            Value::List { .. } => "list".to_string(),
53
            Value::Object { .. } => "object".to_string(),
54
            Value::String(_) => "string".to_string(),
55
            Value::Expression(_) => "expression".to_string(),
56
        }
57
    }
58
}
59

            
60
#[derive(Clone, Debug, PartialEq, Eq)]
61
pub struct ListElement {
62
    pub space0: String,
63
    pub value: Value,
64
    pub space1: String,
65
}
66

            
67
#[derive(Clone, Debug, PartialEq, Eq)]
68
pub struct ObjectElement {
69
    pub space0: String,
70
    pub name: Template,
71
    pub space1: String,
72
    pub space2: String,
73
    pub value: Value,
74
    pub space3: String,
75
}
76

            
77
impl fmt::Display for Value {
78
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79
        let s = match self {
80
            Value::Expression(expr) => format!("{{{{{expr}}}}}"),
81
            Value::Number(s) => s.to_string(),
82
            Value::String(template) => format!("\"{template}\""),
83
            Value::Boolean(value) => {
84
                if *value {
85
                    "true".to_string()
86
                } else {
87
                    "false".to_string()
88
                }
89
            }
90
            Value::List { space0, elements } => {
91
                let elements = elements
92
                    .iter()
93
                    .map(|e| e.to_string())
94
                    .collect::<Vec<String>>();
95
                format!("[{}{}]", space0, elements.join(","))
96
            }
97
            Value::Object { space0, elements } => {
98
                let elements = elements
99
                    .iter()
100
                    .map(|e| e.to_string())
101
                    .collect::<Vec<String>>();
102
                format!("{{{}{}}}", space0, elements.join(","))
103
            }
104
            Value::Null => "null".to_string(),
105
        };
106
        write!(f, "{s}")
107
    }
108
}
109

            
110
impl fmt::Display for ListElement {
111
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112
        let mut s = String::new();
113
        s.push_str(self.space0.as_str());
114
        s.push_str(self.value.to_string().as_str());
115
        s.push_str(self.space1.as_str());
116
        write!(f, "{s}")
117
    }
118
}
119

            
120
impl fmt::Display for ObjectElement {
121
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122
        let mut s = String::new();
123
        s.push_str(self.space0.as_str());
124
        s.push('"');
125
        s.push_str(self.name.to_string().as_str());
126
        s.push('"');
127
        s.push_str(self.space1.as_str());
128
        s.push(':');
129
        s.push_str(self.space2.as_str());
130
        s.push_str(self.value.to_string().as_str());
131
        s.push_str(self.space3.as_str());
132
        write!(f, "{s}")
133
    }
134
}
135

            
136
impl Value {
137
655
    pub fn encoded(&self) -> String {
138
655
        match self {
139
5
            Value::Expression(expr) => format!("{{{{{expr}}}}}"),
140
225
            Value::Number(s) => s.to_string(),
141
175
            Value::String(template) => template.encoded(),
142
10
            Value::Boolean(value) => {
143
10
                if *value {
144
                    "true".to_string()
145
                } else {
146
10
                    "false".to_string()
147
                }
148
            }
149
85
            Value::List { space0, elements } => {
150
85
                let elements = elements
151
85
                    .iter()
152
262
                    .map(|e| e.encoded())
153
85
                    .collect::<Vec<String>>();
154
85
                format!("[{}{}]", space0, elements.join(","))
155
            }
156
145
            Value::Object { space0, elements } => {
157
145
                let elements = elements
158
145
                    .iter()
159
379
                    .map(|e| e.encoded())
160
145
                    .collect::<Vec<String>>();
161
145
                format!("{{{}{}}}", space0, elements.join(","))
162
            }
163
10
            Value::Null => "null".to_string(),
164
        }
165
    }
166
}
167

            
168
impl ListElement {
169
245
    fn encoded(&self) -> String {
170
245
        let mut s = String::new();
171
245
        s.push_str(self.space0.as_str());
172
245
        s.push_str(self.value.encoded().as_str());
173
245
        s.push_str(self.space1.as_str());
174
245
        s
175
    }
176
}
177

            
178
impl ObjectElement {
179
350
    fn encoded(&self) -> String {
180
350
        let mut s = String::new();
181
350
        s.push_str(self.space0.as_str());
182
350
        s.push_str(self.name.encoded().as_str());
183
350
        s.push_str(self.space1.as_str());
184
350
        s.push(':');
185
350
        s.push_str(self.space2.as_str());
186
350
        s.push_str(self.value.encoded().as_str());
187
350
        s.push_str(self.space3.as_str());
188
350
        s
189
    }
190
}
191

            
192
impl Template {
193
525
    fn encoded(&self) -> String {
194
525
        let mut s = String::new();
195
525
        if let Some(d) = self.delimiter {
196
525
            s.push(d);
197
        }
198
630
        let elements: Vec<String> = self.elements.iter().map(|e| e.encoded()).collect();
199
525
        s.push_str(elements.join("").as_str());
200
525
        if let Some(d) = self.delimiter {
201
525
            s.push(d);
202
        }
203
525
        s
204
    }
205
}
206

            
207
impl TemplateElement {
208
525
    fn encoded(&self) -> String {
209
525
        match self {
210
520
            TemplateElement::String { encoded, .. } => encoded.to_string(),
211
5
            TemplateElement::Expression(expr) => format!("{{{{{expr}}}}}"),
212
        }
213
    }
214
}
215

            
216
#[cfg(test)]
217
mod tests {
218
    use super::*;
219
    use crate::ast::{SourceInfo, TemplateElement, Variable, Whitespace};
220
    use crate::reader::Pos;
221

            
222
    #[test]
223
    fn test_to_string() {
224
        assert_eq!(
225
            "{{x}}".to_string(),
226
            Value::Expression(Expr {
227
                space0: Whitespace {
228
                    value: String::new(),
229
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
230
                },
231
                variable: Variable {
232
                    name: "x".to_string(),
233
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
234
                },
235
                space1: Whitespace {
236
                    value: String::new(),
237
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
238
                },
239
            })
240
            .to_string()
241
        );
242
        assert_eq!("1".to_string(), Value::Number("1".to_string()).to_string());
243
        assert_eq!(
244
            "\"hello\"".to_string(),
245
            Value::String(Template {
246
                delimiter: None,
247
                elements: vec![TemplateElement::String {
248
                    value: "hello".to_string(),
249
                    encoded: "hello".to_string(),
250
                }],
251
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
252
            })
253
            .to_string()
254
        );
255
        assert_eq!("true".to_string(), Value::Boolean(true).to_string());
256
        assert_eq!(
257
            "[]".to_string(),
258
            Value::List {
259
                space0: String::new(),
260
                elements: vec![],
261
            }
262
            .to_string()
263
        );
264
        assert_eq!(
265
            "[1, 2, 3]".to_string(),
266
            Value::List {
267
                space0: String::new(),
268
                elements: vec![
269
                    ListElement {
270
                        space0: String::new(),
271
                        value: Value::Number("1".to_string()),
272
                        space1: String::new(),
273
                    },
274
                    ListElement {
275
                        space0: " ".to_string(),
276
                        value: Value::Number("2".to_string()),
277
                        space1: String::new(),
278
                    },
279
                    ListElement {
280
                        space0: " ".to_string(),
281
                        value: Value::Number("3".to_string()),
282
                        space1: String::new(),
283
                    }
284
                ],
285
            }
286
            .to_string()
287
        );
288
        assert_eq!(
289
            "{}".to_string(),
290
            Value::Object {
291
                space0: String::new(),
292
                elements: vec![],
293
            }
294
            .to_string()
295
        );
296
        assert_eq!(
297
            "{ \"id\": 123 }".to_string(),
298
            Value::Object {
299
                space0: String::new(),
300
                elements: vec![ObjectElement {
301
                    space0: " ".to_string(),
302
                    name: Template {
303
                        delimiter: Some('"'),
304
                        elements: vec![TemplateElement::String {
305
                            value: "id".to_string(),
306
                            encoded: "id".to_string(),
307
                        }],
308
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
309
                    },
310
                    space1: String::new(),
311
                    space2: " ".to_string(),
312
                    value: Value::Number("123".to_string()),
313
                    space3: " ".to_string(),
314
                }],
315
            }
316
            .to_string()
317
        );
318
        assert_eq!("null".to_string(), Value::Null.to_string());
319
    }
320

            
321
    #[test]
322
    fn test_encoded() {
323
        assert_eq!(
324
            TemplateElement::Expression(Expr {
325
                space0: Whitespace {
326
                    value: String::new(),
327
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
328
                },
329
                variable: Variable {
330
                    name: "name".to_string(),
331
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
332
                },
333
                space1: Whitespace {
334
                    value: String::new(),
335
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
336
                },
337
            })
338
            .encoded(),
339
            "{{name}}".to_string()
340
        );
341
        assert_eq!(
342
            Template {
343
                delimiter: None,
344
                elements: vec![TemplateElement::Expression(Expr {
345
                    space0: Whitespace {
346
                        value: String::new(),
347
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
348
                    },
349
                    variable: Variable {
350
                        name: "name".to_string(),
351
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
352
                    },
353
                    space1: Whitespace {
354
                        value: String::new(),
355
                        source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
356
                    },
357
                })],
358
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
359
            }
360
            .encoded(),
361
            "{{name}}".to_string()
362
        );
363
    }
364
}