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

            
34
impl JValue {
35
4842
    pub fn format(&self) -> String {
36
4842
        match self {
37
6
            JValue::Null => "null".to_string(),
38
273
            JValue::Number(n) => n.to_string(),
39
2343
            JValue::String(s) => format!("\"{}\"", s.chars().map(format_char).collect::<String>()),
40
60
            JValue::Boolean(b) => b.to_string(),
41
237
            JValue::List(elem) => {
42
237
                let s = elem
43
237
                    .iter()
44
1072
                    .map(|e| e.format())
45
237
                    .collect::<Vec<String>>()
46
237
                    .join(",");
47
237
                format!("[{s}]")
48
            }
49
1923
            JValue::Object(key_values) => {
50
1923
                let s = key_values
51
1923
                    .iter()
52
4439
                    .map(|(k, v)| {
53
3798
                        format!(
54
3798
                            "\"{}\":{}",
55
3798
                            k.chars().map(format_char).collect::<String>(),
56
3798
                            v.format()
57
3798
                        )
58
4439
                    })
59
1923
                    .collect::<Vec<String>>()
60
1923
                    .join(",");
61
1923
                format!("{{{s}}}")
62
            }
63
        }
64
    }
65
}
66

            
67
49017
fn format_char(c: char) -> String {
68
49017
    if c == '"' {
69
135
        "\\\"".to_string()
70
48882
    } else if c == '\\' {
71
30
        "\\\\".to_string()
72
48852
    } else if c == '\x08' {
73
        "\\b".to_string()
74
48852
    } else if c == '\x0c' {
75
        "\\f".to_string()
76
48852
    } else if c == '\n' {
77
321
        "\\n".to_string()
78
48531
    } else if c == '\r' {
79
        "\\r".to_string()
80
48531
    } else if c == '\t' {
81
3
        "\\t".to_string()
82
48528
    } else if c.is_control() {
83
        format!("\\u{:04x}", c as u32)
84
    } else {
85
48528
        c.to_string()
86
    }
87
}
88

            
89
#[cfg(test)]
90
pub mod tests {
91
    use super::*;
92

            
93
    #[test]
94
    pub fn test_format_char() {
95
        assert_eq!(format_char('a'), "a");
96
        assert_eq!(format_char('"'), "\\\""); //    \"
97
        assert_eq!(format_char('\n'), "\\n");
98
        assert_eq!(format_char('\x07'), "\\u0007");
99
    }
100

            
101
    #[test]
102
    pub fn format_scalars() {
103
        assert_eq!(JValue::Null.format(), "null");
104
        assert_eq!(JValue::Number("1.0".to_string()).format(), "1.0");
105
        assert_eq!(JValue::String("hello".to_string()).format(), "\"hello\"");
106
        assert_eq!(JValue::Boolean(true).format(), "true");
107
    }
108

            
109
    #[test]
110
    pub fn format_string() {
111
        assert_eq!(JValue::String("hello".to_string()).format(), "\"hello\"");
112
        assert_eq!(JValue::String("\"".to_string()).format(), r#""\"""#);
113
    }
114

            
115
    #[test]
116
    pub fn format_list() {
117
        assert_eq!(
118
            JValue::List(vec![
119
                JValue::Number("1".to_string()),
120
                JValue::Number("2".to_string()),
121
                JValue::Number("3".to_string())
122
            ])
123
            .format(),
124
            "[1,2,3]"
125
        );
126
    }
127
    #[test]
128
    pub fn test_format_special_characters() {
129
        let value = JValue::Object(vec![(
130
            "sp\"ecial\\key".to_string(),
131
            JValue::String("sp\nvalue\twith\x08control".to_string()),
132
        )]);
133

            
134
        assert_eq!(
135
            value.format(),
136
            r#"{"sp\"ecial\\key":"sp\nvalue\twith\bcontrol"}"#
137
        );
138
    }
139

            
140
    #[test]
141
    pub fn format_object() {
142
        assert_eq!(
143
            JValue::Object(vec![
144
                ("name".to_string(), JValue::String("Bob".to_string())),
145
                ("age".to_string(), JValue::Number("20".to_string())),
146
            ])
147
            .format(),
148
            r#"{"name":"Bob","age":20}"#
149
        );
150
    }
151
}