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

            
19
/**
20
 * Serde-json can not be easily used for serialization here because of the orphan rule.
21
 * It seems easier just to reimplement it from scratch (around 50 lines of code)
22
 */
23

            
24
#[allow(dead_code)]
25
#[derive(Clone, Debug, PartialEq, Eq)]
26
pub enum JValue {
27
    Number(String),
28
    String(String),
29
    Boolean(bool),
30
    List(Vec<JValue>),
31
    Object(Vec<(String, JValue)>),
32
    Null,
33
}
34

            
35
impl JValue {
36
4743
    pub fn format(&self) -> String {
37
4743
        match self {
38
6
            JValue::Null => "null".to_string(),
39
267
            JValue::Number(n) => n.to_string(),
40
2292
            JValue::String(s) => format!("\"{}\"", s.chars().map(format_char).collect::<String>()),
41
60
            JValue::Boolean(b) => b.to_string(),
42
231
            JValue::List(elem) => {
43
231
                let s = elem
44
231
                    .iter()
45
1043
                    .map(|e| e.format())
46
231
                    .collect::<Vec<String>>()
47
231
                    .join(",");
48
231
                format!("[{s}]")
49
            }
50
1887
            JValue::Object(key_values) => {
51
1887
                let s = key_values
52
1887
                    .iter()
53
4358
                    .map(|(k, v)| format!("\"{}\":{}", k, v.format()))
54
1887
                    .collect::<Vec<String>>()
55
1887
                    .join(",");
56
1887
                format!("{{{s}}}")
57
            }
58
        }
59
    }
60
}
61

            
62
28308
fn format_char(c: char) -> String {
63
28308
    if c == '"' {
64
135
        "\\\"".to_string()
65
28173
    } else if c == '\\' {
66
30
        "\\\\".to_string()
67
28143
    } else if c == '\x08' {
68
        "\\b".to_string()
69
28143
    } else if c == '\x0c' {
70
        "\\f".to_string()
71
28143
    } else if c == '\n' {
72
321
        "\\n".to_string()
73
27822
    } else if c == '\r' {
74
        "\\r".to_string()
75
27822
    } else if c == '\t' {
76
3
        "\\t".to_string()
77
27819
    } else if c.is_control() {
78
        format!("\\u{:04x}", c as u32)
79
    } else {
80
27819
        c.to_string()
81
    }
82
}
83

            
84
#[cfg(test)]
85
pub mod tests {
86
    use super::*;
87

            
88
    #[test]
89
    pub fn test_format_char() {
90
        assert_eq!(format_char('a'), "a");
91
        assert_eq!(format_char('"'), "\\\""); //    \"
92
        assert_eq!(format_char('\n'), "\\n");
93
        assert_eq!(format_char('\x07'), "\\u0007");
94
    }
95

            
96
    #[test]
97
    pub fn format_scalars() {
98
        assert_eq!(JValue::Null.format(), "null");
99
        assert_eq!(JValue::Number("1.0".to_string()).format(), "1.0");
100
        assert_eq!(JValue::String("hello".to_string()).format(), "\"hello\"");
101
        assert_eq!(JValue::Boolean(true).format(), "true");
102
    }
103

            
104
    #[test]
105
    pub fn format_string() {
106
        assert_eq!(JValue::String("hello".to_string()).format(), "\"hello\"");
107
        assert_eq!(JValue::String("\"".to_string()).format(), r#""\"""#);
108
    }
109

            
110
    #[test]
111
    pub fn format_list() {
112
        assert_eq!(
113
            JValue::List(vec![
114
                JValue::Number("1".to_string()),
115
                JValue::Number("2".to_string()),
116
                JValue::Number("3".to_string())
117
            ])
118
            .format(),
119
            "[1,2,3]"
120
        );
121
    }
122

            
123
    #[test]
124
    pub fn format_object() {
125
        assert_eq!(
126
            JValue::Object(vec![
127
                ("name".to_string(), JValue::String("Bob".to_string())),
128
                ("age".to_string(), JValue::Number("20".to_string())),
129
            ])
130
            .format(),
131
            r#"{"name":"Bob","age":20}"#
132
        );
133
    }
134
}