1use json5 as json5_crate;
2use serde::{Serialize, de::DeserializeOwned};
3
4use crate::format::{FormatOptions, Formatted, compute_indent};
5
6pub fn parse_json5<T>(
8 text: &str,
9 options: Option<FormatOptions>,
10) -> Result<Formatted<T>, json5_crate::Error>
11where
12 T: DeserializeOwned,
13{
14 let opts = options.unwrap_or_default();
15 let value = json5_crate::from_str(text)?;
16 Ok(Formatted::new(text, value, &opts))
17}
18
19pub fn stringify_json5<T>(
21 formatted: &Formatted<T>,
22 options: Option<FormatOptions>,
23) -> Result<String, json5_crate::Error>
24where
25 T: Serialize,
26{
27 let opts = options.unwrap_or_default();
28 let _indent = compute_indent(&formatted.format, &opts);
29 let json5 = json5_crate::to_string(&formatted.value)?;
33 Ok(format!(
34 "{}{}{}",
35 formatted.format.whitespace_start, json5, formatted.format.whitespace_end
36 ))
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use serde_json::Value as JsonValue;
43
44 const JSON5_FIXTURE: &str = r#"
45{
46 types: {
47 boolean: true,
48 integer: 1,
49 float: 3.14,
50 string: 'hello',
51 array: [
52 1,
53 2,
54 3,
55 ],
56 object: {
57 key: 'value',
58 },
59 null: null,
60 date: '1979-05-27T07:32:00-08:00',
61 },
62}
63"#;
64
65 #[test]
66 fn json5_parse_matches_structure() {
67 #[derive(Debug, serde::Deserialize)]
68 struct Types {
69 boolean: bool,
70 integer: i64,
71 float: f64,
72 string: String,
73 array: Vec<i64>,
74 object: serde_json::Value,
75 null: Option<serde_json::Value>,
76 date: String,
77 }
78
79 #[derive(Debug, serde::Deserialize)]
80 struct Root {
81 types: Types,
82 }
83
84 let formatted = parse_json5::<Root>(JSON5_FIXTURE, None).unwrap();
85 assert!(formatted.value.types.boolean);
86 assert_eq!(formatted.value.types.integer, 1);
87 assert!((formatted.value.types.float - 3.14).abs() < f64::EPSILON);
88 assert_eq!(formatted.value.types.string, "hello");
89 assert_eq!(formatted.value.types.array, vec![1, 2, 3]);
90 assert_eq!(formatted.value.types.object["key"].as_str(), Some("value"));
91 assert!(formatted.value.types.null.is_none());
92 assert_eq!(
93 formatted.value.types.date,
94 "1979-05-27T07:32:00-08:00".to_string()
95 );
96 }
97
98 #[test]
99 fn json5_stringify_exact_normalized() {
100 let formatted = parse_json5::<JsonValue>(JSON5_FIXTURE, None).unwrap();
101 let out = stringify_json5(&formatted, None).unwrap();
102 let expected: JsonValue = ::json5::from_str(JSON5_FIXTURE).unwrap();
103 let expected_str = ::json5::to_string(&expected).unwrap();
104 let expected_str = format!("\n{}", expected_str);
105 assert_eq!(out.trim(), expected_str.trim());
106 }
107
108 #[test]
109 fn json5_preserves_outer_whitespace() {
110 let text = " \n{ types: { boolean: true } }\n\t";
111 let formatted = parse_json5::<JsonValue>(text, None).unwrap();
112 let out = stringify_json5(&formatted, None).unwrap();
113
114 assert!(out.starts_with(" \n"));
115 assert!(out.ends_with("\n\t"));
116 }
117}