Skip to main content

dkit_core/format/
json.rs

1use std::io::{Read, Write};
2
3use indexmap::IndexMap;
4
5use crate::format::{FormatOptions, FormatReader, FormatWriter};
6use crate::value::Value;
7
8/// serde_json::Value → 내부 Value 변환
9pub fn from_json_value(v: serde_json::Value) -> Value {
10    match v {
11        serde_json::Value::Null => Value::Null,
12        serde_json::Value::Bool(b) => Value::Bool(b),
13        serde_json::Value::Number(n) => {
14            if let Some(i) = n.as_i64() {
15                Value::Integer(i)
16            } else if let Some(f) = n.as_f64() {
17                Value::Float(f)
18            } else {
19                // u64 that doesn't fit in i64
20                Value::Float(n.as_f64().unwrap_or(f64::NAN))
21            }
22        }
23        serde_json::Value::String(s) => Value::String(s),
24        serde_json::Value::Array(arr) => {
25            Value::Array(arr.into_iter().map(from_json_value).collect())
26        }
27        serde_json::Value::Object(map) => {
28            let obj: IndexMap<String, Value> = map
29                .into_iter()
30                .map(|(k, v)| (k, from_json_value(v)))
31                .collect();
32            Value::Object(obj)
33        }
34    }
35}
36
37/// 내부 Value → serde_json::Value 변환
38pub fn to_json_value(v: &Value) -> serde_json::Value {
39    match v {
40        Value::Null => serde_json::Value::Null,
41        Value::Bool(b) => serde_json::Value::Bool(*b),
42        Value::Integer(n) => serde_json::Value::Number((*n).into()),
43        Value::Float(f) => serde_json::Number::from_f64(*f)
44            .map(serde_json::Value::Number)
45            .unwrap_or(serde_json::Value::Null),
46        Value::String(s) => serde_json::Value::String(s.clone()),
47        Value::Array(arr) => serde_json::Value::Array(arr.iter().map(to_json_value).collect()),
48        Value::Object(map) => {
49            let obj: serde_json::Map<String, serde_json::Value> = map
50                .iter()
51                .map(|(k, v)| (k.clone(), to_json_value(v)))
52                .collect();
53            serde_json::Value::Object(obj)
54        }
55    }
56}
57
58/// JSON 포맷 Reader
59pub struct JsonReader;
60
61impl FormatReader for JsonReader {
62    fn read(&self, input: &str) -> anyhow::Result<Value> {
63        let json_val: serde_json::Value = serde_json::from_str(input).map_err(|e| {
64            let line = e.line();
65            let column = e.column();
66            let line_text = input
67                .lines()
68                .nth(line.saturating_sub(1))
69                .unwrap_or("")
70                .to_string();
71            crate::error::DkitError::ParseErrorAt {
72                format: "JSON".to_string(),
73                source: Box::new(e),
74                line,
75                column,
76                line_text,
77            }
78        })?;
79        Ok(from_json_value(json_val))
80    }
81
82    fn read_from_reader(&self, mut reader: impl Read) -> anyhow::Result<Value> {
83        let json_val: serde_json::Value = serde_json::from_reader(&mut reader).map_err(|e| {
84            crate::error::DkitError::ParseError {
85                format: "JSON".to_string(),
86                source: Box::new(e),
87            }
88        })?;
89        Ok(from_json_value(json_val))
90    }
91}
92
93/// JSON 포맷 Writer
94#[derive(Default)]
95pub struct JsonWriter {
96    options: FormatOptions,
97}
98
99impl JsonWriter {
100    pub fn new(options: FormatOptions) -> Self {
101        Self { options }
102    }
103}
104
105impl FormatWriter for JsonWriter {
106    fn write(&self, value: &Value) -> anyhow::Result<String> {
107        let json_val = to_json_value(value);
108        let output = if self.options.compact {
109            serde_json::to_string(&json_val)
110        } else if self.options.pretty {
111            serde_json::to_string_pretty(&json_val)
112        } else {
113            serde_json::to_string(&json_val)
114        };
115        output.map_err(|e| {
116            crate::error::DkitError::WriteError {
117                format: "JSON".to_string(),
118                source: Box::new(e),
119            }
120            .into()
121        })
122    }
123
124    fn write_to_writer(&self, value: &Value, mut writer: impl Write) -> anyhow::Result<()> {
125        let json_val = to_json_value(value);
126        let result = if self.options.compact {
127            serde_json::to_writer(&mut writer, &json_val)
128        } else if self.options.pretty {
129            serde_json::to_writer_pretty(&mut writer, &json_val)
130        } else {
131            serde_json::to_writer(&mut writer, &json_val)
132        };
133        result.map_err(|e| crate::error::DkitError::WriteError {
134            format: "JSON".to_string(),
135            source: Box::new(e),
136        })?;
137        Ok(())
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144
145    // --- from_json_value 변환 테스트 ---
146
147    #[test]
148    fn test_convert_null() {
149        let v = from_json_value(serde_json::Value::Null);
150        assert_eq!(v, Value::Null);
151    }
152
153    #[test]
154    fn test_convert_bool() {
155        assert_eq!(
156            from_json_value(serde_json::Value::Bool(true)),
157            Value::Bool(true)
158        );
159    }
160
161    #[test]
162    fn test_convert_integer() {
163        let v = from_json_value(serde_json::json!(42));
164        assert_eq!(v, Value::Integer(42));
165    }
166
167    #[test]
168    fn test_convert_float() {
169        let v = from_json_value(serde_json::json!(3.14));
170        assert_eq!(v, Value::Float(3.14));
171    }
172
173    #[test]
174    fn test_convert_string() {
175        let v = from_json_value(serde_json::json!("hello"));
176        assert_eq!(v, Value::String("hello".to_string()));
177    }
178
179    #[test]
180    fn test_convert_array() {
181        let v = from_json_value(serde_json::json!([1, "two", null]));
182        let arr = v.as_array().unwrap();
183        assert_eq!(arr.len(), 3);
184        assert_eq!(arr[0], Value::Integer(1));
185        assert_eq!(arr[1], Value::String("two".to_string()));
186        assert_eq!(arr[2], Value::Null);
187    }
188
189    #[test]
190    fn test_convert_object() {
191        let v = from_json_value(serde_json::json!({"name": "dkit", "version": 1}));
192        let obj = v.as_object().unwrap();
193        assert_eq!(obj.get("name"), Some(&Value::String("dkit".to_string())));
194        assert_eq!(obj.get("version"), Some(&Value::Integer(1)));
195    }
196
197    #[test]
198    fn test_convert_nested() {
199        let v = from_json_value(serde_json::json!({
200            "users": [
201                {"name": "Alice", "age": 30},
202                {"name": "Bob", "age": 25}
203            ]
204        }));
205        let users = v
206            .as_object()
207            .unwrap()
208            .get("users")
209            .unwrap()
210            .as_array()
211            .unwrap();
212        assert_eq!(users.len(), 2);
213        assert_eq!(
214            users[0].as_object().unwrap().get("name"),
215            Some(&Value::String("Alice".to_string()))
216        );
217    }
218
219    // --- to_json_value 왕복 변환 테스트 ---
220
221    #[test]
222    fn test_roundtrip_primitives() {
223        let values = vec![
224            Value::Null,
225            Value::Bool(false),
226            Value::Integer(100),
227            Value::Float(2.718),
228            Value::String("test".to_string()),
229        ];
230        for v in values {
231            let json = to_json_value(&v);
232            let back = from_json_value(json);
233            assert_eq!(back, v);
234        }
235    }
236
237    #[test]
238    fn test_roundtrip_complex() {
239        let mut map = IndexMap::new();
240        map.insert(
241            "key".to_string(),
242            Value::Array(vec![Value::Integer(1), Value::Null]),
243        );
244        let original = Value::Object(map);
245        let json = to_json_value(&original);
246        let back = from_json_value(json);
247        assert_eq!(back, original);
248    }
249
250    // --- JsonReader 테스트 ---
251
252    #[test]
253    fn test_reader_simple_object() {
254        let reader = JsonReader;
255        let v = reader.read(r#"{"name": "dkit", "count": 42}"#).unwrap();
256        let obj = v.as_object().unwrap();
257        assert_eq!(obj.get("name"), Some(&Value::String("dkit".to_string())));
258        assert_eq!(obj.get("count"), Some(&Value::Integer(42)));
259    }
260
261    #[test]
262    fn test_reader_array() {
263        let reader = JsonReader;
264        let v = reader.read("[1, 2, 3]").unwrap();
265        assert_eq!(v.as_array().unwrap().len(), 3);
266    }
267
268    #[test]
269    fn test_reader_invalid_json() {
270        let reader = JsonReader;
271        let result = reader.read("{invalid}");
272        assert!(result.is_err());
273    }
274
275    #[test]
276    fn test_reader_empty_object() {
277        let reader = JsonReader;
278        let v = reader.read("{}").unwrap();
279        assert!(v.as_object().unwrap().is_empty());
280    }
281
282    #[test]
283    fn test_reader_empty_array() {
284        let reader = JsonReader;
285        let v = reader.read("[]").unwrap();
286        assert!(v.as_array().unwrap().is_empty());
287    }
288
289    #[test]
290    fn test_reader_from_reader() {
291        let reader = JsonReader;
292        let input = r#"{"x": 1}"#.as_bytes();
293        let v = reader.read_from_reader(input).unwrap();
294        assert_eq!(v.as_object().unwrap().get("x"), Some(&Value::Integer(1)));
295    }
296
297    #[test]
298    fn test_reader_from_reader_invalid() {
299        let reader = JsonReader;
300        let input = b"not json" as &[u8];
301        assert!(reader.read_from_reader(input).is_err());
302    }
303
304    // --- JsonWriter 테스트 ---
305
306    #[test]
307    fn test_writer_pretty() {
308        let writer = JsonWriter::default(); // pretty: true by default
309        let v = Value::Object({
310            let mut m = IndexMap::new();
311            m.insert("a".to_string(), Value::Integer(1));
312            m
313        });
314        let output = writer.write(&v).unwrap();
315        assert!(output.contains('\n'));
316        assert!(output.contains("  ")); // indentation
317    }
318
319    #[test]
320    fn test_writer_compact() {
321        let writer = JsonWriter::new(FormatOptions {
322            compact: true,
323            pretty: false,
324            ..Default::default()
325        });
326        let v = Value::Object({
327            let mut m = IndexMap::new();
328            m.insert("a".to_string(), Value::Integer(1));
329            m
330        });
331        let output = writer.write(&v).unwrap();
332        assert_eq!(output, r#"{"a":1}"#);
333    }
334
335    #[test]
336    fn test_writer_null() {
337        let writer = JsonWriter::new(FormatOptions {
338            compact: true,
339            ..Default::default()
340        });
341        assert_eq!(writer.write(&Value::Null).unwrap(), "null");
342    }
343
344    #[test]
345    fn test_writer_to_writer() {
346        let writer = JsonWriter::new(FormatOptions {
347            compact: true,
348            ..Default::default()
349        });
350        let mut buf = Vec::new();
351        writer
352            .write_to_writer(&Value::Integer(42), &mut buf)
353            .unwrap();
354        assert_eq!(String::from_utf8(buf).unwrap(), "42");
355    }
356
357    #[test]
358    fn test_writer_nan_becomes_null() {
359        let writer = JsonWriter::new(FormatOptions {
360            compact: true,
361            ..Default::default()
362        });
363        let output = writer.write(&Value::Float(f64::NAN)).unwrap();
364        assert_eq!(output, "null");
365    }
366
367    // --- 대규모 데이터 테스트 ---
368
369    #[test]
370    fn test_large_array() {
371        let reader = JsonReader;
372        let arr: Vec<String> = (0..1000).map(|i| format!("{i}")).collect();
373        let json = serde_json::to_string(&arr).unwrap();
374        let v = reader.read(&json).unwrap();
375        assert_eq!(v.as_array().unwrap().len(), 1000);
376    }
377
378    // --- 특수 케이스 ---
379
380    #[test]
381    fn test_unicode_string() {
382        let reader = JsonReader;
383        let v = reader.read(r#"{"emoji": "🎉", "korean": "한글"}"#).unwrap();
384        let obj = v.as_object().unwrap();
385        assert_eq!(obj.get("emoji"), Some(&Value::String("🎉".to_string())));
386        assert_eq!(obj.get("korean"), Some(&Value::String("한글".to_string())));
387    }
388
389    #[test]
390    fn test_negative_numbers() {
391        let reader = JsonReader;
392        let v = reader
393            .read(r#"{"neg_int": -42, "neg_float": -3.14}"#)
394            .unwrap();
395        let obj = v.as_object().unwrap();
396        assert_eq!(obj.get("neg_int"), Some(&Value::Integer(-42)));
397        assert_eq!(obj.get("neg_float"), Some(&Value::Float(-3.14)));
398    }
399
400    #[test]
401    fn test_deeply_nested() {
402        let reader = JsonReader;
403        let v = reader.read(r#"{"a": {"b": {"c": {"d": 1}}}}"#).unwrap();
404        let d = v
405            .as_object()
406            .unwrap()
407            .get("a")
408            .unwrap()
409            .as_object()
410            .unwrap()
411            .get("b")
412            .unwrap()
413            .as_object()
414            .unwrap()
415            .get("c")
416            .unwrap()
417            .as_object()
418            .unwrap()
419            .get("d")
420            .unwrap();
421        assert_eq!(d, &Value::Integer(1));
422    }
423}