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