wp_data_fmt/
json.rs

1use crate::formatter::StaticDataFormatter;
2use serde_json::{Value as JsonValue, json};
3use wp_model_core::model::{DataField, DataRecord, DataType, Value, types::value::ObjectValue};
4
5#[derive(Debug, Default)]
6pub struct Json;
7impl StaticDataFormatter for Json {
8    type Output = String;
9    fn stdfmt_null() -> String {
10        "null".to_string()
11    }
12    fn stdfmt_bool(value: &bool) -> String {
13        json!(value).to_string()
14    }
15    fn stdfmt_string(value: &str) -> String {
16        serde_json::to_string(value).unwrap_or_else(|_| "\"\"".to_string())
17    }
18    fn stdfmt_i64(value: &i64) -> String {
19        json!(value).to_string()
20    }
21    fn stdfmt_f64(value: &f64) -> String {
22        if value.is_nan() {
23            "null".to_string()
24        } else if value.is_infinite() {
25            if value.is_sign_positive() {
26                "\"Infinity\"".to_string()
27            } else {
28                "\"-Infinity\"".to_string()
29            }
30        } else {
31            json!(value).to_string()
32        }
33    }
34    fn stdfmt_ip_addr(value: &std::net::IpAddr) -> String {
35        json!(value.to_string()).to_string()
36    }
37    fn stdfmt_datetime(value: &chrono::NaiveDateTime) -> String {
38        json!(value.to_string()).to_string()
39    }
40    fn stdfmt_object(value: &ObjectValue) -> String {
41        let mut json_obj = serde_json::Map::new();
42        for (k, v) in value.iter() {
43            json_obj.insert(k.to_string(), to_json_value(v.get_value()));
44        }
45        json!(json_obj).to_string()
46    }
47    fn stdfmt_array(value: &[DataField]) -> String {
48        let items: Vec<String> = value
49            .iter()
50            .map(|field| match field.get_value() {
51                Value::Chars(s) => Self::stdfmt_string(s),
52                _ => Self::stdfmt_value(field.get_value()),
53            })
54            .collect();
55        format!("[{}]", items.join(","))
56    }
57    fn stdfmt_field(field: &DataField) -> String {
58        if field.get_name().is_empty() {
59            Self::stdfmt_value(field.get_value())
60        } else {
61            format!(
62                "\"{}\":{}",
63                field.get_name(),
64                Self::stdfmt_value(field.get_value())
65            )
66        }
67    }
68    fn stdfmt_record(record: &DataRecord) -> String {
69        let mut items = Vec::new();
70        for field in record
71            .items
72            .iter()
73            .filter(|f| *f.get_meta() != DataType::Ignore)
74        {
75            items.push(Self::stdfmt_field(field));
76        }
77        format!("{{{}}}", items.join(","))
78    }
79}
80
81impl crate::formatter::DataFormat for Json {
82    type Output = String;
83    fn format_null(&self) -> String {
84        Self::stdfmt_null()
85    }
86    fn format_bool(&self, v: &bool) -> String {
87        Self::stdfmt_bool(v)
88    }
89    fn format_string(&self, v: &str) -> String {
90        Self::stdfmt_string(v)
91    }
92    fn format_i64(&self, v: &i64) -> String {
93        Self::stdfmt_i64(v)
94    }
95    fn format_f64(&self, v: &f64) -> String {
96        Self::stdfmt_f64(v)
97    }
98    fn format_ip(&self, v: &std::net::IpAddr) -> String {
99        Self::stdfmt_ip_addr(v)
100    }
101    fn format_datetime(&self, v: &chrono::NaiveDateTime) -> String {
102        Self::stdfmt_datetime(v)
103    }
104    fn format_object(&self, v: &ObjectValue) -> String {
105        Self::stdfmt_object(v)
106    }
107    fn format_array(&self, v: &[DataField]) -> String {
108        Self::stdfmt_array(v)
109    }
110    fn format_field(&self, f: &DataField) -> String {
111        Self::stdfmt_field(f)
112    }
113    fn format_record(&self, r: &DataRecord) -> String {
114        Self::stdfmt_record(r)
115    }
116}
117
118fn to_json_value(value: &Value) -> JsonValue {
119    match value {
120        Value::Bool(v) => JsonValue::Bool(*v),
121        Value::Chars(v) => JsonValue::String(v.to_string()),
122        Value::Digit(v) => JsonValue::Number((*v).into()),
123        Value::Float(v) => {
124            if v.is_nan() {
125                JsonValue::Null
126            } else if v.is_infinite() {
127                if v.is_sign_positive() {
128                    JsonValue::String("Infinity".to_string())
129                } else {
130                    JsonValue::String("-Infinity".to_string())
131                }
132            } else {
133                JsonValue::Number(serde_json::Number::from_f64(*v).unwrap_or(0.into()))
134            }
135        }
136        Value::IpAddr(v) => JsonValue::String(v.to_string()),
137        Value::Time(v) => JsonValue::String(v.to_string()),
138        Value::Obj(v) => {
139            let mut map = serde_json::Map::new();
140            for (k, field) in v.iter() {
141                map.insert(k.to_string(), to_json_value(field.get_value()));
142            }
143            JsonValue::Object(map)
144        }
145        Value::Array(v) => {
146            JsonValue::Array(v.iter().map(|f| to_json_value(f.get_value())).collect())
147        }
148        _ => JsonValue::Null,
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155    use crate::formatter::DataFormat;
156    use std::net::IpAddr;
157    use std::str::FromStr;
158
159    #[test]
160    fn test_json_stdfmt_null() {
161        assert_eq!(Json::stdfmt_null(), "null");
162    }
163
164    #[test]
165    fn test_json_stdfmt_bool() {
166        assert_eq!(Json::stdfmt_bool(&true), "true");
167        assert_eq!(Json::stdfmt_bool(&false), "false");
168    }
169
170    #[test]
171    fn test_json_stdfmt_string() {
172        assert_eq!(Json::stdfmt_string("hello"), "\"hello\"");
173        assert_eq!(Json::stdfmt_string(""), "\"\"");
174        // Special chars should be escaped
175        assert_eq!(Json::stdfmt_string("say \"hi\""), "\"say \\\"hi\\\"\"");
176    }
177
178    #[test]
179    fn test_json_stdfmt_i64() {
180        assert_eq!(Json::stdfmt_i64(&0), "0");
181        assert_eq!(Json::stdfmt_i64(&42), "42");
182        assert_eq!(Json::stdfmt_i64(&-100), "-100");
183    }
184
185    #[test]
186    fn test_json_stdfmt_f64_normal() {
187        assert_eq!(Json::stdfmt_f64(&3.24), "3.24");
188        assert_eq!(Json::stdfmt_f64(&0.0), "0.0");
189        assert_eq!(Json::stdfmt_f64(&-2.5), "-2.5");
190    }
191
192    #[test]
193    fn test_json_stdfmt_f64_special() {
194        assert_eq!(Json::stdfmt_f64(&f64::NAN), "null");
195        assert_eq!(Json::stdfmt_f64(&f64::INFINITY), "\"Infinity\"");
196        assert_eq!(Json::stdfmt_f64(&f64::NEG_INFINITY), "\"-Infinity\"");
197    }
198
199    #[test]
200    fn test_json_stdfmt_ip_addr() {
201        let ipv4 = IpAddr::from_str("192.168.1.1").unwrap();
202        assert_eq!(Json::stdfmt_ip_addr(&ipv4), "\"192.168.1.1\"");
203
204        let ipv6 = IpAddr::from_str("::1").unwrap();
205        assert_eq!(Json::stdfmt_ip_addr(&ipv6), "\"::1\"");
206    }
207
208    #[test]
209    fn test_json_stdfmt_datetime() {
210        let dt = chrono::NaiveDateTime::parse_from_str("2024-01-15 10:30:45", "%Y-%m-%d %H:%M:%S")
211            .unwrap();
212        let result = Json::stdfmt_datetime(&dt);
213        assert!(result.starts_with('"'));
214        assert!(result.ends_with('"'));
215        assert!(result.contains("2024"));
216    }
217
218    #[test]
219    fn test_json_stdfmt_field_with_name() {
220        let field = DataField::from_chars("name", "Alice");
221        let result = Json::stdfmt_field(&field);
222        assert_eq!(result, "\"name\":\"Alice\"");
223    }
224
225    #[test]
226    fn test_json_stdfmt_field_without_name() {
227        let field = DataField::from_chars("", "value");
228        let result = Json::stdfmt_field(&field);
229        assert_eq!(result, "\"value\"");
230    }
231
232    #[test]
233    fn test_json_stdfmt_record() {
234        let record = DataRecord {
235            items: vec![
236                DataField::from_chars("name", "Alice"),
237                DataField::from_digit("age", 30),
238            ],
239        };
240        let result = Json::stdfmt_record(&record);
241        assert!(result.starts_with('{'));
242        assert!(result.ends_with('}'));
243        assert!(result.contains("\"name\":\"Alice\""));
244        assert!(result.contains("\"age\":30"));
245    }
246
247    #[test]
248    fn test_json_dataformat_impl() {
249        let json = Json;
250        assert_eq!(json.format_null(), "null");
251        assert_eq!(json.format_bool(&true), "true");
252        assert_eq!(json.format_string("test"), "\"test\"");
253        assert_eq!(json.format_i64(&42), "42");
254        assert_eq!(json.format_f64(&3.24), "3.24");
255    }
256
257    #[test]
258    fn test_to_json_value_basic() {
259        assert_eq!(to_json_value(&Value::Bool(true)), JsonValue::Bool(true));
260        assert_eq!(
261            to_json_value(&Value::Chars("hi".into())),
262            JsonValue::String("hi".into())
263        );
264        assert_eq!(
265            to_json_value(&Value::Digit(42)),
266            JsonValue::Number(42.into())
267        );
268    }
269
270    #[test]
271    fn test_to_json_value_float_special() {
272        assert_eq!(to_json_value(&Value::Float(f64::NAN)), JsonValue::Null);
273        assert_eq!(
274            to_json_value(&Value::Float(f64::INFINITY)),
275            JsonValue::String("Infinity".into())
276        );
277    }
278
279    #[test]
280    fn test_json_stdfmt_array() {
281        let arr = vec![
282            DataField::from_digit("", 1),
283            DataField::from_digit("", 2),
284            DataField::from_digit("", 3),
285        ];
286        let result = Json::stdfmt_array(&arr);
287        assert_eq!(result, "[1,2,3]");
288    }
289}