Skip to main content

dkit_core/format/
json.rs

1use std::io::{Read, Write};
2
3use indexmap::IndexMap;
4use serde::Serialize;
5
6use crate::format::{FormatOptions, FormatReader, FormatWriter};
7use crate::value::Value;
8
9/// Recursively sort all object keys alphabetically
10fn sort_value_keys(value: &Value) -> Value {
11    match value {
12        Value::Object(map) => {
13            let mut sorted: IndexMap<String, Value> = IndexMap::new();
14            let mut keys: Vec<&String> = map.keys().collect();
15            keys.sort();
16            for key in keys {
17                sorted.insert(key.clone(), sort_value_keys(&map[key]));
18            }
19            Value::Object(sorted)
20        }
21        Value::Array(arr) => Value::Array(arr.iter().map(sort_value_keys).collect()),
22        other => other.clone(),
23    }
24}
25
26/// serde_json::Value → 내부 Value 변환
27pub fn from_json_value(v: serde_json::Value) -> Value {
28    match v {
29        serde_json::Value::Null => Value::Null,
30        serde_json::Value::Bool(b) => Value::Bool(b),
31        serde_json::Value::Number(n) => {
32            if let Some(i) = n.as_i64() {
33                Value::Integer(i)
34            } else if let Some(f) = n.as_f64() {
35                Value::Float(f)
36            } else {
37                // u64 that doesn't fit in i64
38                Value::Float(n.as_f64().unwrap_or(f64::NAN))
39            }
40        }
41        serde_json::Value::String(s) => Value::String(s),
42        serde_json::Value::Array(arr) => {
43            Value::Array(arr.into_iter().map(from_json_value).collect())
44        }
45        serde_json::Value::Object(map) => {
46            let obj: IndexMap<String, Value> = map
47                .into_iter()
48                .map(|(k, v)| (k, from_json_value(v)))
49                .collect();
50            Value::Object(obj)
51        }
52    }
53}
54
55/// 내부 Value → serde_json::Value 변환
56pub fn to_json_value(v: &Value) -> serde_json::Value {
57    match v {
58        Value::Null => serde_json::Value::Null,
59        Value::Bool(b) => serde_json::Value::Bool(*b),
60        Value::Integer(n) => serde_json::Value::Number((*n).into()),
61        Value::Float(f) => serde_json::Number::from_f64(*f)
62            .map(serde_json::Value::Number)
63            .unwrap_or(serde_json::Value::Null),
64        Value::String(s) => serde_json::Value::String(s.clone()),
65        Value::Array(arr) => serde_json::Value::Array(arr.iter().map(to_json_value).collect()),
66        Value::Object(map) => {
67            let obj: serde_json::Map<String, serde_json::Value> = map
68                .iter()
69                .map(|(k, v)| (k.clone(), to_json_value(v)))
70                .collect();
71            serde_json::Value::Object(obj)
72        }
73    }
74}
75
76/// JSON 포맷 Reader
77pub struct JsonReader;
78
79impl FormatReader for JsonReader {
80    fn read(&self, input: &str) -> anyhow::Result<Value> {
81        let json_val: serde_json::Value = serde_json::from_str(input).map_err(|e| {
82            let line = e.line();
83            let column = e.column();
84            let line_text = input
85                .lines()
86                .nth(line.saturating_sub(1))
87                .unwrap_or("")
88                .to_string();
89            crate::error::DkitError::ParseErrorAt {
90                format: "JSON".to_string(),
91                source: Box::new(e),
92                line,
93                column,
94                line_text,
95            }
96        })?;
97        Ok(from_json_value(json_val))
98    }
99
100    fn read_from_reader(&self, mut reader: impl Read) -> anyhow::Result<Value> {
101        let json_val: serde_json::Value = serde_json::from_reader(&mut reader).map_err(|e| {
102            crate::error::DkitError::ParseError {
103                format: "JSON".to_string(),
104                source: Box::new(e),
105            }
106        })?;
107        Ok(from_json_value(json_val))
108    }
109}
110
111/// JSON 포맷 Writer
112#[derive(Default)]
113pub struct JsonWriter {
114    options: FormatOptions,
115}
116
117impl JsonWriter {
118    pub fn new(options: FormatOptions) -> Self {
119        Self { options }
120    }
121}
122
123impl JsonWriter {
124    /// Serialize a serde_json::Value with custom indent string
125    fn serialize_with_indent(json_val: &serde_json::Value, indent: &str) -> anyhow::Result<String> {
126        let mut buf = Vec::new();
127        let formatter = serde_json::ser::PrettyFormatter::with_indent(indent.as_bytes());
128        let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);
129        serde_json::Value::serialize(json_val, &mut ser).map_err(|e| {
130            crate::error::DkitError::WriteError {
131                format: "JSON".to_string(),
132                source: Box::new(e),
133            }
134        })?;
135        Ok(String::from_utf8(buf)?)
136    }
137
138    fn serialize_with_indent_to_writer(
139        json_val: &serde_json::Value,
140        indent: &str,
141        writer: impl Write,
142    ) -> anyhow::Result<()> {
143        let formatter = serde_json::ser::PrettyFormatter::with_indent(indent.as_bytes());
144        let mut ser = serde_json::Serializer::with_formatter(writer, formatter);
145        serde_json::Value::serialize(json_val, &mut ser).map_err(|e| {
146            crate::error::DkitError::WriteError {
147                format: "JSON".to_string(),
148                source: Box::new(e),
149            }
150        })?;
151        Ok(())
152    }
153
154    /// Resolve the indent string from options
155    fn resolve_indent(&self) -> Option<String> {
156        self.options.indent.as_ref().map(|v| {
157            if v.eq_ignore_ascii_case("tab") {
158                "\t".to_string()
159            } else if let Ok(n) = v.parse::<usize>() {
160                " ".repeat(n)
161            } else {
162                // fallback: default 2 spaces
163                "  ".to_string()
164            }
165        })
166    }
167
168    fn prepare_value(&self, value: &Value) -> serde_json::Value {
169        let value = if self.options.sort_keys {
170            sort_value_keys(value)
171        } else {
172            value.clone()
173        };
174        to_json_value(&value)
175    }
176}
177
178impl FormatWriter for JsonWriter {
179    fn write(&self, value: &Value) -> anyhow::Result<String> {
180        let json_val = self.prepare_value(value);
181        let output = if self.options.compact {
182            serde_json::to_string(&json_val).map_err(|e| crate::error::DkitError::WriteError {
183                format: "JSON".to_string(),
184                source: Box::new(e),
185            })?
186        } else if let Some(indent) = self.resolve_indent() {
187            Self::serialize_with_indent(&json_val, &indent)?
188        } else if self.options.pretty {
189            serde_json::to_string_pretty(&json_val).map_err(|e| {
190                crate::error::DkitError::WriteError {
191                    format: "JSON".to_string(),
192                    source: Box::new(e),
193                }
194            })?
195        } else {
196            serde_json::to_string(&json_val).map_err(|e| crate::error::DkitError::WriteError {
197                format: "JSON".to_string(),
198                source: Box::new(e),
199            })?
200        };
201        Ok(output)
202    }
203
204    fn write_to_writer(&self, value: &Value, mut writer: impl Write) -> anyhow::Result<()> {
205        let json_val = self.prepare_value(value);
206        if self.options.compact {
207            serde_json::to_writer(&mut writer, &json_val)
208        } else if let Some(indent) = self.resolve_indent() {
209            return Self::serialize_with_indent_to_writer(&json_val, &indent, &mut writer);
210        } else if self.options.pretty {
211            serde_json::to_writer_pretty(&mut writer, &json_val)
212        } else {
213            serde_json::to_writer(&mut writer, &json_val)
214        }
215        .map_err(|e| crate::error::DkitError::WriteError {
216            format: "JSON".to_string(),
217            source: Box::new(e),
218        })?;
219        Ok(())
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    // --- from_json_value 변환 테스트 ---
228
229    #[test]
230    fn test_convert_null() {
231        let v = from_json_value(serde_json::Value::Null);
232        assert_eq!(v, Value::Null);
233    }
234
235    #[test]
236    fn test_convert_bool() {
237        assert_eq!(
238            from_json_value(serde_json::Value::Bool(true)),
239            Value::Bool(true)
240        );
241    }
242
243    #[test]
244    fn test_convert_integer() {
245        let v = from_json_value(serde_json::json!(42));
246        assert_eq!(v, Value::Integer(42));
247    }
248
249    #[test]
250    fn test_convert_float() {
251        let v = from_json_value(serde_json::json!(3.14));
252        assert_eq!(v, Value::Float(3.14));
253    }
254
255    #[test]
256    fn test_convert_string() {
257        let v = from_json_value(serde_json::json!("hello"));
258        assert_eq!(v, Value::String("hello".to_string()));
259    }
260
261    #[test]
262    fn test_convert_array() {
263        let v = from_json_value(serde_json::json!([1, "two", null]));
264        let arr = v.as_array().unwrap();
265        assert_eq!(arr.len(), 3);
266        assert_eq!(arr[0], Value::Integer(1));
267        assert_eq!(arr[1], Value::String("two".to_string()));
268        assert_eq!(arr[2], Value::Null);
269    }
270
271    #[test]
272    fn test_convert_object() {
273        let v = from_json_value(serde_json::json!({"name": "dkit", "version": 1}));
274        let obj = v.as_object().unwrap();
275        assert_eq!(obj.get("name"), Some(&Value::String("dkit".to_string())));
276        assert_eq!(obj.get("version"), Some(&Value::Integer(1)));
277    }
278
279    #[test]
280    fn test_convert_nested() {
281        let v = from_json_value(serde_json::json!({
282            "users": [
283                {"name": "Alice", "age": 30},
284                {"name": "Bob", "age": 25}
285            ]
286        }));
287        let users = v
288            .as_object()
289            .unwrap()
290            .get("users")
291            .unwrap()
292            .as_array()
293            .unwrap();
294        assert_eq!(users.len(), 2);
295        assert_eq!(
296            users[0].as_object().unwrap().get("name"),
297            Some(&Value::String("Alice".to_string()))
298        );
299    }
300
301    // --- to_json_value 왕복 변환 테스트 ---
302
303    #[test]
304    fn test_roundtrip_primitives() {
305        let values = vec![
306            Value::Null,
307            Value::Bool(false),
308            Value::Integer(100),
309            Value::Float(2.718),
310            Value::String("test".to_string()),
311        ];
312        for v in values {
313            let json = to_json_value(&v);
314            let back = from_json_value(json);
315            assert_eq!(back, v);
316        }
317    }
318
319    #[test]
320    fn test_roundtrip_complex() {
321        let mut map = IndexMap::new();
322        map.insert(
323            "key".to_string(),
324            Value::Array(vec![Value::Integer(1), Value::Null]),
325        );
326        let original = Value::Object(map);
327        let json = to_json_value(&original);
328        let back = from_json_value(json);
329        assert_eq!(back, original);
330    }
331
332    // --- JsonReader 테스트 ---
333
334    #[test]
335    fn test_reader_simple_object() {
336        let reader = JsonReader;
337        let v = reader.read(r#"{"name": "dkit", "count": 42}"#).unwrap();
338        let obj = v.as_object().unwrap();
339        assert_eq!(obj.get("name"), Some(&Value::String("dkit".to_string())));
340        assert_eq!(obj.get("count"), Some(&Value::Integer(42)));
341    }
342
343    #[test]
344    fn test_reader_array() {
345        let reader = JsonReader;
346        let v = reader.read("[1, 2, 3]").unwrap();
347        assert_eq!(v.as_array().unwrap().len(), 3);
348    }
349
350    #[test]
351    fn test_reader_invalid_json() {
352        let reader = JsonReader;
353        let result = reader.read("{invalid}");
354        assert!(result.is_err());
355    }
356
357    #[test]
358    fn test_reader_empty_object() {
359        let reader = JsonReader;
360        let v = reader.read("{}").unwrap();
361        assert!(v.as_object().unwrap().is_empty());
362    }
363
364    #[test]
365    fn test_reader_empty_array() {
366        let reader = JsonReader;
367        let v = reader.read("[]").unwrap();
368        assert!(v.as_array().unwrap().is_empty());
369    }
370
371    #[test]
372    fn test_reader_from_reader() {
373        let reader = JsonReader;
374        let input = r#"{"x": 1}"#.as_bytes();
375        let v = reader.read_from_reader(input).unwrap();
376        assert_eq!(v.as_object().unwrap().get("x"), Some(&Value::Integer(1)));
377    }
378
379    #[test]
380    fn test_reader_from_reader_invalid() {
381        let reader = JsonReader;
382        let input = b"not json" as &[u8];
383        assert!(reader.read_from_reader(input).is_err());
384    }
385
386    // --- JsonWriter 테스트 ---
387
388    #[test]
389    fn test_writer_pretty() {
390        let writer = JsonWriter::default(); // pretty: true by default
391        let v = Value::Object({
392            let mut m = IndexMap::new();
393            m.insert("a".to_string(), Value::Integer(1));
394            m
395        });
396        let output = writer.write(&v).unwrap();
397        assert!(output.contains('\n'));
398        assert!(output.contains("  ")); // indentation
399    }
400
401    #[test]
402    fn test_writer_compact() {
403        let writer = JsonWriter::new(FormatOptions {
404            compact: true,
405            pretty: false,
406            ..Default::default()
407        });
408        let v = Value::Object({
409            let mut m = IndexMap::new();
410            m.insert("a".to_string(), Value::Integer(1));
411            m
412        });
413        let output = writer.write(&v).unwrap();
414        assert_eq!(output, r#"{"a":1}"#);
415    }
416
417    #[test]
418    fn test_writer_null() {
419        let writer = JsonWriter::new(FormatOptions {
420            compact: true,
421            ..Default::default()
422        });
423        assert_eq!(writer.write(&Value::Null).unwrap(), "null");
424    }
425
426    #[test]
427    fn test_writer_to_writer() {
428        let writer = JsonWriter::new(FormatOptions {
429            compact: true,
430            ..Default::default()
431        });
432        let mut buf = Vec::new();
433        writer
434            .write_to_writer(&Value::Integer(42), &mut buf)
435            .unwrap();
436        assert_eq!(String::from_utf8(buf).unwrap(), "42");
437    }
438
439    #[test]
440    fn test_writer_nan_becomes_null() {
441        let writer = JsonWriter::new(FormatOptions {
442            compact: true,
443            ..Default::default()
444        });
445        let output = writer.write(&Value::Float(f64::NAN)).unwrap();
446        assert_eq!(output, "null");
447    }
448
449    // --- 대규모 데이터 테스트 ---
450
451    #[test]
452    fn test_large_array() {
453        let reader = JsonReader;
454        let arr: Vec<String> = (0..1000).map(|i| format!("{i}")).collect();
455        let json = serde_json::to_string(&arr).unwrap();
456        let v = reader.read(&json).unwrap();
457        assert_eq!(v.as_array().unwrap().len(), 1000);
458    }
459
460    // --- 특수 케이스 ---
461
462    #[test]
463    fn test_unicode_string() {
464        let reader = JsonReader;
465        let v = reader.read(r#"{"emoji": "🎉", "korean": "한글"}"#).unwrap();
466        let obj = v.as_object().unwrap();
467        assert_eq!(obj.get("emoji"), Some(&Value::String("🎉".to_string())));
468        assert_eq!(obj.get("korean"), Some(&Value::String("한글".to_string())));
469    }
470
471    #[test]
472    fn test_negative_numbers() {
473        let reader = JsonReader;
474        let v = reader
475            .read(r#"{"neg_int": -42, "neg_float": -3.14}"#)
476            .unwrap();
477        let obj = v.as_object().unwrap();
478        assert_eq!(obj.get("neg_int"), Some(&Value::Integer(-42)));
479        assert_eq!(obj.get("neg_float"), Some(&Value::Float(-3.14)));
480    }
481
482    #[test]
483    fn test_deeply_nested() {
484        let reader = JsonReader;
485        let v = reader.read(r#"{"a": {"b": {"c": {"d": 1}}}}"#).unwrap();
486        let d = v
487            .as_object()
488            .unwrap()
489            .get("a")
490            .unwrap()
491            .as_object()
492            .unwrap()
493            .get("b")
494            .unwrap()
495            .as_object()
496            .unwrap()
497            .get("c")
498            .unwrap()
499            .as_object()
500            .unwrap()
501            .get("d")
502            .unwrap();
503        assert_eq!(d, &Value::Integer(1));
504    }
505
506    // --- --indent, --sort-keys, --compact 테스트 ---
507
508    #[test]
509    fn test_writer_indent_2_spaces() {
510        let writer = JsonWriter::new(FormatOptions {
511            indent: Some("2".to_string()),
512            ..Default::default()
513        });
514        let v = Value::Object({
515            let mut m = IndexMap::new();
516            m.insert("a".to_string(), Value::Integer(1));
517            m
518        });
519        let output = writer.write(&v).unwrap();
520        assert!(output.contains("\n  \"a\""));
521        assert!(!output.contains("    \"a\"")); // not 4 spaces
522    }
523
524    #[test]
525    fn test_writer_indent_4_spaces() {
526        let writer = JsonWriter::new(FormatOptions {
527            indent: Some("4".to_string()),
528            ..Default::default()
529        });
530        let v = Value::Object({
531            let mut m = IndexMap::new();
532            m.insert("x".to_string(), Value::Integer(42));
533            m
534        });
535        let output = writer.write(&v).unwrap();
536        assert!(output.contains("\n    \"x\""));
537    }
538
539    #[test]
540    fn test_writer_indent_tab() {
541        let writer = JsonWriter::new(FormatOptions {
542            indent: Some("tab".to_string()),
543            ..Default::default()
544        });
545        let v = Value::Object({
546            let mut m = IndexMap::new();
547            m.insert("a".to_string(), Value::Integer(1));
548            m
549        });
550        let output = writer.write(&v).unwrap();
551        assert!(output.contains("\n\t\"a\""));
552    }
553
554    #[test]
555    fn test_writer_sort_keys() {
556        let writer = JsonWriter::new(FormatOptions {
557            compact: true,
558            sort_keys: true,
559            ..Default::default()
560        });
561        let v = Value::Object({
562            let mut m = IndexMap::new();
563            m.insert("zebra".to_string(), Value::Integer(1));
564            m.insert("apple".to_string(), Value::Integer(2));
565            m.insert("mango".to_string(), Value::Integer(3));
566            m
567        });
568        let output = writer.write(&v).unwrap();
569        assert_eq!(output, r#"{"apple":2,"mango":3,"zebra":1}"#);
570    }
571
572    #[test]
573    fn test_writer_sort_keys_nested() {
574        let writer = JsonWriter::new(FormatOptions {
575            compact: true,
576            sort_keys: true,
577            ..Default::default()
578        });
579        let v = Value::Object({
580            let mut m = IndexMap::new();
581            m.insert(
582                "z".to_string(),
583                Value::Object({
584                    let mut inner = IndexMap::new();
585                    inner.insert("b".to_string(), Value::Integer(2));
586                    inner.insert("a".to_string(), Value::Integer(1));
587                    inner
588                }),
589            );
590            m.insert("a".to_string(), Value::Integer(0));
591            m
592        });
593        let output = writer.write(&v).unwrap();
594        assert_eq!(output, r#"{"a":0,"z":{"a":1,"b":2}}"#);
595    }
596
597    #[test]
598    fn test_writer_indent_and_sort_keys() {
599        let writer = JsonWriter::new(FormatOptions {
600            indent: Some("2".to_string()),
601            sort_keys: true,
602            ..Default::default()
603        });
604        let v = Value::Object({
605            let mut m = IndexMap::new();
606            m.insert("c".to_string(), Value::Integer(3));
607            m.insert("a".to_string(), Value::Integer(1));
608            m.insert("b".to_string(), Value::Integer(2));
609            m
610        });
611        let output = writer.write(&v).unwrap();
612        // Keys should be sorted and indent should be 2 spaces
613        assert!(output.contains("\"a\": 1"));
614        let keys_pos_a = output.find("\"a\"").unwrap();
615        let keys_pos_b = output.find("\"b\"").unwrap();
616        let keys_pos_c = output.find("\"c\"").unwrap();
617        assert!(keys_pos_a < keys_pos_b);
618        assert!(keys_pos_b < keys_pos_c);
619    }
620
621    #[test]
622    fn test_writer_compact_overrides_indent() {
623        let writer = JsonWriter::new(FormatOptions {
624            compact: true,
625            indent: Some("4".to_string()),
626            ..Default::default()
627        });
628        let v = Value::Object({
629            let mut m = IndexMap::new();
630            m.insert("a".to_string(), Value::Integer(1));
631            m
632        });
633        let output = writer.write(&v).unwrap();
634        assert_eq!(output, r#"{"a":1}"#);
635    }
636
637    #[test]
638    fn test_writer_to_writer_with_indent() {
639        let writer = JsonWriter::new(FormatOptions {
640            indent: Some("2".to_string()),
641            ..Default::default()
642        });
643        let v = Value::Object({
644            let mut m = IndexMap::new();
645            m.insert("key".to_string(), Value::String("val".to_string()));
646            m
647        });
648        let mut buf = Vec::new();
649        writer.write_to_writer(&v, &mut buf).unwrap();
650        let output = String::from_utf8(buf).unwrap();
651        assert!(output.contains("\n  \"key\""));
652    }
653
654    #[test]
655    fn test_writer_to_writer_with_sort_keys() {
656        let writer = JsonWriter::new(FormatOptions {
657            compact: true,
658            sort_keys: true,
659            ..Default::default()
660        });
661        let v = Value::Object({
662            let mut m = IndexMap::new();
663            m.insert("z".to_string(), Value::Integer(1));
664            m.insert("a".to_string(), Value::Integer(2));
665            m
666        });
667        let mut buf = Vec::new();
668        writer.write_to_writer(&v, &mut buf).unwrap();
669        let output = String::from_utf8(buf).unwrap();
670        assert_eq!(output, r#"{"a":2,"z":1}"#);
671    }
672}