Lines
49.5 %
Functions
57.14 %
Branches
100 %
/*
* Hurl (https://hurl.dev)
* Copyright (C) 2024 Orange
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use core::fmt;
use crate::ast::core::Template;
use crate::ast::{Placeholder, TemplateElement};
///
/// This the AST for the JSON used within Hurl
/// It is a superset of the standard json spec.
/// Strings have been replaced by Hurl template.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Value {
Placeholder(Placeholder),
Number(String),
String(Template),
Boolean(bool),
List {
space0: String,
elements: Vec<ListElement>,
},
Object {
elements: Vec<ObjectElement>,
Null,
}
impl Value {
pub fn _type(&self) -> String {
match self {
Value::Number(_) => "number".to_string(),
Value::Null => "null".to_string(),
Value::Boolean(_) => "boolean".to_string(),
Value::List { .. } => "list".to_string(),
Value::Object { .. } => "object".to_string(),
Value::String(_) => "string".to_string(),
Value::Placeholder(_) => "placeholder".to_string(),
pub struct ListElement {
pub space0: String,
pub value: Value,
pub space1: String,
pub struct ObjectElement {
pub name: Template,
pub space2: String,
pub space3: String,
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
Value::Placeholder(expr) => format!("{{{{{expr}}}}}"),
Value::Number(s) => s.to_string(),
Value::String(template) => format!("\"{template}\""),
Value::Boolean(value) => {
if *value {
"true".to_string()
} else {
"false".to_string()
Value::List { space0, elements } => {
let elements = elements
.iter()
.map(|e| e.to_string())
.collect::<Vec<String>>();
format!("[{}{}]", space0, elements.join(","))
Value::Object { space0, elements } => {
format!("{{{}{}}}", space0, elements.join(","))
};
write!(f, "{s}")
impl fmt::Display for ListElement {
let mut s = String::new();
s.push_str(self.space0.as_str());
s.push_str(self.value.to_string().as_str());
s.push_str(self.space1.as_str());
impl fmt::Display for ObjectElement {
s.push('"');
s.push_str(self.name.to_string().as_str());
s.push(':');
s.push_str(self.space2.as_str());
s.push_str(self.space3.as_str());
pub fn encoded(&self) -> String {
Value::String(template) => template.encoded(),
.map(|e| e.encoded())
impl ListElement {
fn encoded(&self) -> String {
s.push_str(self.value.encoded().as_str());
s
impl ObjectElement {
s.push_str(self.name.encoded().as_str());
impl Template {
if let Some(d) = self.delimiter {
s.push(d);
let elements: Vec<String> = self.elements.iter().map(|e| e.encoded()).collect();
s.push_str(elements.join("").as_str());
impl TemplateElement {
TemplateElement::String { encoded, .. } => encoded.to_string(),
TemplateElement::Placeholder(expr) => format!("{{{{{expr}}}}}"),
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{Expr, ExprKind, SourceInfo, TemplateElement, Variable, Whitespace};
use crate::reader::Pos;
#[test]
fn test_to_string() {
assert_eq!(
"{{x}}".to_string(),
Value::Placeholder(Placeholder {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
expr: Expr {
kind: ExprKind::Variable(Variable {
name: "x".to_string(),
}),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
space1: Whitespace {
})
.to_string()
);
assert_eq!("1".to_string(), Value::Number("1".to_string()).to_string());
"\"hello\"".to_string(),
Value::String(Template {
delimiter: None,
elements: vec![TemplateElement::String {
value: "hello".to_string(),
encoded: "hello".to_string(),
}],
assert_eq!("true".to_string(), Value::Boolean(true).to_string());
"[]".to_string(),
Value::List {
space0: String::new(),
elements: vec![],
"[1, 2, 3]".to_string(),
elements: vec![
ListElement {
value: Value::Number("1".to_string()),
space1: String::new(),
space0: " ".to_string(),
value: Value::Number("2".to_string()),
value: Value::Number("3".to_string()),
],
"{}".to_string(),
Value::Object {
"{ \"id\": 123 }".to_string(),
elements: vec![ObjectElement {
name: Template {
delimiter: Some('"'),
value: "id".to_string(),
encoded: "id".to_string(),
space2: " ".to_string(),
value: Value::Number("123".to_string()),
space3: " ".to_string(),
assert_eq!("null".to_string(), Value::Null.to_string());
fn test_encoded() {
TemplateElement::Placeholder(Placeholder {
name: "name".to_string(),
.encoded(),
"{{name}}".to_string()
Template {
elements: vec![TemplateElement::Placeholder(Placeholder {
})],