1use serde::{Serialize, de::DeserializeOwned};
2
3use crate::format::{FormatOptions, Formatted, compute_indent, wrap_whitespace};
4
5pub fn parse_json<T>(text: &str, options: Option<FormatOptions>) -> serde_json::Result<Formatted<T>>
7where
8 T: DeserializeOwned,
9{
10 let opts = options.unwrap_or_default();
11 let value = serde_json::from_str(text)?;
12 Ok(Formatted::new(text, value, &opts))
13}
14
15pub fn stringify_json<T>(
17 formatted: &Formatted<T>,
18 options: Option<FormatOptions>,
19) -> serde_json::Result<String>
20where
21 T: Serialize,
22{
23 stringify_json_with_format(&formatted.value, &formatted.format, options)
24}
25
26pub(crate) fn stringify_json_with_format<T>(
28 value: &T,
29 format: &crate::format::FormatInfo,
30 options: Option<FormatOptions>,
31) -> serde_json::Result<String>
32where
33 T: Serialize,
34{
35 let opts = options.unwrap_or_default();
36 let indent = compute_indent(format, &opts);
37 let json = serde_json::to_string_pretty(value)?;
38 let indent_str = " ".repeat(indent);
39
40 let indented = json
41 .lines()
42 .map(|line| {
43 if line.is_empty() {
44 line.to_string()
45 } else {
46 let trimmed = line.trim_start();
47 let mut s = String::with_capacity(indent_str.len() + trimmed.len());
48 s.push_str(&indent_str);
49 s.push_str(trimmed);
50 s
51 }
52 })
53 .collect::<Vec<_>>()
54 .join("\n");
55 Ok(wrap_whitespace(&indented, format))
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::{FormatInfo, Formatted};
62 use serde_json::Value as JsonValue;
63
64 const FIXTURE: &str = r#"
66{
67 "types": {
68 "boolean": true,
69 "integer": 1,
70 "float": 3.14,
71 "string": "hello",
72 "array": [
73 1,
74 2,
75 3
76 ],
77 "object": {
78 "key": "value"
79 },
80 "null": null,
81 "date": "1979-05-27T07:32:00-08:00"
82 }
83}
84"#;
85
86 #[test]
87 fn parse_ok() {
88 #[derive(Debug, serde::Deserialize)]
89 struct Types {
90 boolean: bool,
91 integer: i64,
92 float: f64,
93 string: String,
94 array: Vec<i64>,
95 object: serde_json::Value,
96 null: Option<serde_json::Value>,
97 date: String,
98 }
99
100 #[derive(Debug, serde::Deserialize)]
101 struct Root {
102 types: Types,
103 }
104
105 let formatted = parse_json::<Root>(FIXTURE, None).unwrap();
106 assert!(formatted.value.types.boolean);
107 assert_eq!(formatted.value.types.integer, 1);
108 assert!((formatted.value.types.float - 3.14).abs() < f64::EPSILON);
109 assert_eq!(formatted.value.types.string, "hello");
110 assert_eq!(formatted.value.types.array, vec![1, 2, 3]);
111 assert_eq!(formatted.value.types.object["key"].as_str(), Some("value"));
112 assert!(formatted.value.types.null.is_none());
113 assert_eq!(
114 formatted.value.types.date,
115 "1979-05-27T07:32:00-08:00".to_string()
116 );
117 }
118
119 #[test]
120 fn stringify_roundtrip() {
121 let formatted = parse_json::<JsonValue>(FIXTURE, None).unwrap();
122 let out = stringify_json(&formatted, None).unwrap();
123 let out_val: JsonValue = serde_json::from_str(&out).unwrap();
124 let expected_val: JsonValue = serde_json::from_str(FIXTURE).unwrap();
125 assert_eq!(out_val, expected_val);
126 }
127
128 #[test]
129 fn stringify_from_raw_preserves_structure() {
130 let value: JsonValue = serde_json::from_str(FIXTURE).unwrap();
131 let formatted = Formatted {
132 value,
133 format: FormatInfo {
134 sample: None,
135 whitespace_start: String::new(),
136 whitespace_end: String::new(),
137 },
138 };
139 let out = stringify_json(&formatted, None).unwrap();
140 let out_val: JsonValue = serde_json::from_str(&out).unwrap();
141 let expected_val: JsonValue = serde_json::from_str(FIXTURE).unwrap();
142 assert_eq!(out_val, expected_val);
143 }
144
145 #[test]
146 fn stringify_respects_explicit_indent() {
147 let formatted = parse_json::<JsonValue>(FIXTURE, None).unwrap();
148 let mut opts = FormatOptions::default();
149 opts.indent = Some(4);
150
151 let out = stringify_json(&formatted, Some(opts)).unwrap();
152
153 let mut lines = out.lines();
155 assert_eq!(lines.next(), Some(""));
156 if let Some(second) = lines.next() {
157 let prefix = &second[..4.min(second.len())];
158 assert_eq!(prefix, " ");
159 } else {
160 panic!("expected at least two lines in JSON output");
161 }
162 }
163
164 #[test]
165 fn preserves_outer_whitespace() {
166 let text = " \n{ \"a\": 1 }\n\t";
167 let formatted = parse_json::<JsonValue>(text, None).unwrap();
168 let out = stringify_json(&formatted, None).unwrap();
169
170 assert!(out.starts_with(" \n"));
171 assert!(out.ends_with("\n\t"));
172 }
173}