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::{Placeholder, 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
    Placeholder(Placeholder),
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::Placeholder(_) => "placeholder".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::Placeholder(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
660
    pub fn encoded(&self) -> String {
138
660
        match self {
139
5
            Value::Placeholder(expr) => format!("{{{{{expr}}}}}"),
140
225
            Value::Number(s) => s.to_string(),
141
180
            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
384
                    .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
355
    fn encoded(&self) -> String {
180
355
        let mut s = String::new();
181
355
        s.push_str(self.space0.as_str());
182
355
        s.push_str(self.name.encoded().as_str());
183
355
        s.push_str(self.space1.as_str());
184
355
        s.push(':');
185
355
        s.push_str(self.space2.as_str());
186
355
        s.push_str(self.value.encoded().as_str());
187
355
        s.push_str(self.space3.as_str());
188
355
        s
189
    }
190
}
191

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

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

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

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