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 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#[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}