Skip to main content

wp_data_fmt/
json.rs

1#[allow(deprecated)]
2use crate::formatter::StaticDataFormatter;
3use crate::formatter::{RecordFormatter, ValueFormatter};
4use serde_json::{Value as JsonValue, json};
5use wp_model_core::model::{DataRecord, DataType, FieldStorage, Value, types::value::ObjectValue};
6
7#[derive(Debug, Default)]
8pub struct Json;
9
10#[allow(deprecated)]
11impl StaticDataFormatter for Json {
12    type Output = String;
13    fn stdfmt_null() -> String {
14        "null".to_string()
15    }
16    fn stdfmt_bool(value: &bool) -> String {
17        json!(value).to_string()
18    }
19    fn stdfmt_string(value: &str) -> String {
20        serde_json::to_string(value).unwrap_or_else(|_| "\"\"".to_string())
21    }
22    fn stdfmt_i64(value: &i64) -> String {
23        json!(value).to_string()
24    }
25    fn stdfmt_f64(value: &f64) -> String {
26        if value.is_nan() {
27            "null".to_string()
28        } else if value.is_infinite() {
29            if value.is_sign_positive() {
30                "\"Infinity\"".to_string()
31            } else {
32                "\"-Infinity\"".to_string()
33            }
34        } else {
35            json!(value).to_string()
36        }
37    }
38    fn stdfmt_ip_addr(value: &std::net::IpAddr) -> String {
39        json!(value.to_string()).to_string()
40    }
41    fn stdfmt_datetime(value: &chrono::NaiveDateTime) -> String {
42        json!(value.to_string()).to_string()
43    }
44    fn stdfmt_object(value: &ObjectValue) -> String {
45        let mut json_obj = serde_json::Map::new();
46        for (k, v) in value.iter() {
47            json_obj.insert(k.to_string(), to_json_value(v.get_value()));
48        }
49        json!(json_obj).to_string()
50    }
51    fn stdfmt_array(value: &[FieldStorage]) -> String {
52        let items: Vec<String> = value
53            .iter()
54            .map(|field| match field.get_value() {
55                Value::Chars(s) => Self::stdfmt_string(s),
56                _ => Self::stdfmt_value(field.get_value()),
57            })
58            .collect();
59        format!("[{}]", items.join(","))
60    }
61    fn stdfmt_field(field: &FieldStorage) -> String {
62        if field.get_name().is_empty() {
63            Self::stdfmt_value(field.get_value())
64        } else {
65            format!(
66                "\"{}\":{}",
67                field.get_name(),
68                Self::stdfmt_value(field.get_value())
69            )
70        }
71    }
72    fn stdfmt_record(record: &DataRecord) -> String {
73        let mut items = Vec::new();
74        for field in record
75            .items
76            .iter()
77            .filter(|f| *f.get_meta() != DataType::Ignore)
78        {
79            items.push(Self::stdfmt_field(field));
80        }
81        format!("{{{}}}", items.join(","))
82    }
83}
84
85#[allow(deprecated)]
86impl crate::formatter::DataFormat for Json {
87    type Output = String;
88    fn format_null(&self) -> String {
89        Self::stdfmt_null()
90    }
91    fn format_bool(&self, v: &bool) -> String {
92        Self::stdfmt_bool(v)
93    }
94    fn format_string(&self, v: &str) -> String {
95        Self::stdfmt_string(v)
96    }
97    fn format_i64(&self, v: &i64) -> String {
98        Self::stdfmt_i64(v)
99    }
100    fn format_f64(&self, v: &f64) -> String {
101        Self::stdfmt_f64(v)
102    }
103    fn format_ip(&self, v: &std::net::IpAddr) -> String {
104        Self::stdfmt_ip_addr(v)
105    }
106    fn format_datetime(&self, v: &chrono::NaiveDateTime) -> String {
107        Self::stdfmt_datetime(v)
108    }
109    fn format_object(&self, v: &ObjectValue) -> String {
110        Self::stdfmt_object(v)
111    }
112    fn format_array(&self, v: &[FieldStorage]) -> String {
113        Self::stdfmt_array(v)
114    }
115    fn format_field(&self, f: &FieldStorage) -> String {
116        Self::stdfmt_field(f)
117    }
118    fn format_record(&self, r: &DataRecord) -> String {
119        Self::stdfmt_record(r)
120    }
121}
122
123fn to_json_value(value: &Value) -> JsonValue {
124    match value {
125        Value::Bool(v) => JsonValue::Bool(*v),
126        Value::Chars(v) => JsonValue::String(v.to_string()),
127        Value::Digit(v) => JsonValue::Number((*v).into()),
128        Value::Float(v) => {
129            if v.is_nan() {
130                JsonValue::Null
131            } else if v.is_infinite() {
132                if v.is_sign_positive() {
133                    JsonValue::String("Infinity".to_string())
134                } else {
135                    JsonValue::String("-Infinity".to_string())
136                }
137            } else {
138                JsonValue::Number(serde_json::Number::from_f64(*v).unwrap_or(0.into()))
139            }
140        }
141        Value::IpAddr(v) => JsonValue::String(v.to_string()),
142        Value::Time(v) => JsonValue::String(v.to_string()),
143        Value::Obj(v) => {
144            let mut map = serde_json::Map::new();
145            for (k, field) in v.iter() {
146                map.insert(k.to_string(), to_json_value(field.get_value()));
147            }
148            JsonValue::Object(map)
149        }
150        Value::Array(v) => {
151            JsonValue::Array(v.iter().map(|f| to_json_value(f.get_value())).collect())
152        }
153        _ => JsonValue::Null,
154    }
155}
156
157#[cfg(test)]
158#[allow(deprecated)]
159mod tests {
160    use super::*;
161    use crate::formatter::DataFormat;
162    use std::net::IpAddr;
163    use std::str::FromStr;
164    use wp_model_core::model::DataField;
165
166    #[test]
167    fn test_json_stdfmt_null() {
168        assert_eq!(Json::stdfmt_null(), "null");
169    }
170
171    #[test]
172    fn test_json_stdfmt_bool() {
173        assert_eq!(Json::stdfmt_bool(&true), "true");
174        assert_eq!(Json::stdfmt_bool(&false), "false");
175    }
176
177    #[test]
178    fn test_json_stdfmt_string() {
179        assert_eq!(Json::stdfmt_string("hello"), "\"hello\"");
180        assert_eq!(Json::stdfmt_string(""), "\"\"");
181        // Special chars should be escaped
182        assert_eq!(Json::stdfmt_string("say \"hi\""), "\"say \\\"hi\\\"\"");
183    }
184
185    #[test]
186    fn test_json_stdfmt_i64() {
187        assert_eq!(Json::stdfmt_i64(&0), "0");
188        assert_eq!(Json::stdfmt_i64(&42), "42");
189        assert_eq!(Json::stdfmt_i64(&-100), "-100");
190    }
191
192    #[test]
193    fn test_json_stdfmt_f64_normal() {
194        assert_eq!(Json::stdfmt_f64(&3.24), "3.24");
195        assert_eq!(Json::stdfmt_f64(&0.0), "0.0");
196        assert_eq!(Json::stdfmt_f64(&-2.5), "-2.5");
197    }
198
199    #[test]
200    fn test_json_stdfmt_f64_special() {
201        assert_eq!(Json::stdfmt_f64(&f64::NAN), "null");
202        assert_eq!(Json::stdfmt_f64(&f64::INFINITY), "\"Infinity\"");
203        assert_eq!(Json::stdfmt_f64(&f64::NEG_INFINITY), "\"-Infinity\"");
204    }
205
206    #[test]
207    fn test_json_stdfmt_ip_addr() {
208        let ipv4 = IpAddr::from_str("192.168.1.1").unwrap();
209        assert_eq!(Json::stdfmt_ip_addr(&ipv4), "\"192.168.1.1\"");
210
211        let ipv6 = IpAddr::from_str("::1").unwrap();
212        assert_eq!(Json::stdfmt_ip_addr(&ipv6), "\"::1\"");
213    }
214
215    #[test]
216    fn test_json_stdfmt_datetime() {
217        let dt = chrono::NaiveDateTime::parse_from_str("2024-01-15 10:30:45", "%Y-%m-%d %H:%M:%S")
218            .unwrap();
219        let result = Json::stdfmt_datetime(&dt);
220        assert!(result.starts_with('"'));
221        assert!(result.ends_with('"'));
222        assert!(result.contains("2024"));
223    }
224
225    #[test]
226    fn test_json_stdfmt_field_with_name() {
227        let field = FieldStorage::from_owned(DataField::from_chars("name", "Alice"));
228        let result = Json::stdfmt_field(&field);
229        assert_eq!(result, "\"name\":\"Alice\"");
230    }
231
232    #[test]
233    fn test_json_stdfmt_field_without_name() {
234        let field = FieldStorage::from_owned(DataField::from_chars("", "value"));
235        let result = Json::stdfmt_field(&field);
236        assert_eq!(result, "\"value\"");
237    }
238
239    #[test]
240    fn test_json_stdfmt_record() {
241        let record = DataRecord {
242            id: Default::default(),
243            items: vec![
244                FieldStorage::from_owned(DataField::from_chars("name", "Alice")),
245                FieldStorage::from_owned(DataField::from_digit("age", 30)),
246            ],
247        };
248        let result = Json::stdfmt_record(&record);
249        assert!(result.starts_with('{'));
250        assert!(result.ends_with('}'));
251        assert!(result.contains("\"name\":\"Alice\""));
252        assert!(result.contains("\"age\":30"));
253    }
254
255    #[test]
256    fn test_json_dataformat_impl() {
257        let json = Json;
258        assert_eq!(json.format_null(), "null");
259        assert_eq!(json.format_bool(&true), "true");
260        assert_eq!(json.format_string("test"), "\"test\"");
261        assert_eq!(json.format_i64(&42), "42");
262        assert_eq!(json.format_f64(&3.24), "3.24");
263    }
264
265    #[test]
266    fn test_to_json_value_basic() {
267        assert_eq!(to_json_value(&Value::Bool(true)), JsonValue::Bool(true));
268        assert_eq!(
269            to_json_value(&Value::Chars("hi".into())),
270            JsonValue::String("hi".into())
271        );
272        assert_eq!(
273            to_json_value(&Value::Digit(42)),
274            JsonValue::Number(42.into())
275        );
276    }
277
278    #[test]
279    fn test_to_json_value_float_special() {
280        assert_eq!(to_json_value(&Value::Float(f64::NAN)), JsonValue::Null);
281        assert_eq!(
282            to_json_value(&Value::Float(f64::INFINITY)),
283            JsonValue::String("Infinity".into())
284        );
285    }
286
287    #[test]
288    fn test_json_stdfmt_array() {
289        let arr = vec![
290            FieldStorage::from_owned(DataField::from_digit("", 1)),
291            FieldStorage::from_owned(DataField::from_digit("", 2)),
292            FieldStorage::from_owned(DataField::from_digit("", 3)),
293        ];
294        let result = Json::stdfmt_array(&arr);
295        assert_eq!(result, "[1,2,3]");
296    }
297
298    fn make_record_with_obj() -> DataRecord {
299        let mut obj = ObjectValue::new();
300        obj.insert(
301            "ssl_cipher".to_string(),
302            FieldStorage::from_owned(DataField::from_chars("ssl_cipher", "ECDHE")),
303        );
304        DataRecord {
305            id: Default::default(),
306            items: vec![
307                FieldStorage::from_owned(DataField::from_digit("status", 200)),
308                FieldStorage::from_owned(DataField::from_obj("extends", obj)),
309                FieldStorage::from_owned(DataField::from_digit("length", 50)),
310            ],
311        }
312    }
313
314    #[test]
315    fn test_format_record_with_obj_no_newlines() {
316        let json = Json;
317        let record = make_record_with_obj();
318        let result = json.format_record(&record);
319        assert!(
320            !result.contains('\n'),
321            "record output should not contain newlines: {}",
322            result
323        );
324        assert!(result.contains("\"ssl_cipher\""));
325        assert!(result.contains("\"ECDHE\""));
326        assert!(result.contains("\"status\":200"));
327    }
328
329    #[test]
330    fn test_fmt_record_with_obj_no_newlines() {
331        let json = Json;
332        let record = make_record_with_obj();
333        let result = json.fmt_record(&record);
334        assert!(
335            !result.contains('\n'),
336            "record output should not contain newlines: {}",
337            result
338        );
339        assert!(result.contains("\"ssl_cipher\""));
340        assert!(result.contains("\"ECDHE\""));
341    }
342
343    #[test]
344    fn test_old_new_api_consistency_nested() {
345        let json = Json;
346        let record = make_record_with_obj();
347        assert_eq!(json.format_record(&record), json.fmt_record(&record));
348    }
349}
350
351// ============================================================================
352// 新 trait 实现:ValueFormatter + RecordFormatter
353// ============================================================================
354
355#[allow(clippy::items_after_test_module)]
356impl ValueFormatter for Json {
357    type Output = String;
358
359    fn format_value(&self, value: &Value) -> String {
360        match value {
361            Value::Null => "null".to_string(),
362            Value::Bool(v) => json!(v).to_string(),
363            Value::Chars(v) => serde_json::to_string(v).unwrap_or_else(|_| "\"\"".to_string()),
364            Value::Digit(v) => json!(v).to_string(),
365            Value::Float(v) => {
366                if v.is_nan() {
367                    "null".to_string()
368                } else if v.is_infinite() {
369                    if v.is_sign_positive() {
370                        "\"Infinity\"".to_string()
371                    } else {
372                        "\"-Infinity\"".to_string()
373                    }
374                } else {
375                    json!(v).to_string()
376                }
377            }
378            Value::IpAddr(v) => json!(v.to_string()).to_string(),
379            Value::Time(v) => json!(v.to_string()).to_string(),
380            Value::Obj(v) => {
381                let mut json_obj = serde_json::Map::new();
382                for (k, field) in v.iter() {
383                    json_obj.insert(k.to_string(), to_json_value(field.get_value()));
384                }
385                json!(json_obj).to_string()
386            }
387            Value::Array(v) => {
388                let items: Vec<String> = v
389                    .iter()
390                    .map(|field| self.format_value(field.get_value()))
391                    .collect();
392                format!("[{}]", items.join(","))
393            }
394            _ => json!(value.to_string()).to_string(),
395        }
396    }
397}
398
399impl RecordFormatter for Json {
400    fn fmt_field(&self, field: &FieldStorage) -> String {
401        if field.get_name().is_empty() {
402            self.format_value(field.get_value())
403        } else {
404            format!(
405                "\"{}\":{}",
406                field.get_name(),
407                self.format_value(field.get_value())
408            )
409        }
410    }
411
412    fn fmt_record(&self, record: &DataRecord) -> String {
413        let mut items = Vec::new();
414        for field in record
415            .items
416            .iter()
417            .filter(|f| *f.get_meta() != DataType::Ignore)
418        {
419            items.push(self.fmt_field(field));
420        }
421        format!("{{{}}}", items.join(","))
422    }
423}