1use std::io;
9
10use serde::Serialize;
11use serde::de::DeserializeOwned;
12use serde_json::ser::{CompactFormatter, Formatter, PrettyFormatter, Serializer};
13use serde_json::{Map, Value};
14
15pub fn to_string<T: Serialize + ?Sized>(value: &T) -> serde_json::Result<String> {
17 write(value, JsFormatter(CompactFormatter))
18}
19
20pub trait JsonValue: Serialize {}
22
23impl JsonValue for Value {}
24
25impl JsonValue for Map<String, Value> {}
26
27pub fn stringify(value: &impl JsonValue) -> String {
29 to_string(value).unwrap_or_default()
31}
32
33pub fn number(value: f64) -> Value {
36 if value.fract() == 0.0 && value.abs() <= MAX_SAFE_INTEGER as f64 {
37 Value::from(value as i64)
38 } else {
39 serde_json::Number::from_f64(value).map_or(Value::Null, Value::Number)
40 }
41}
42
43pub fn js_number(text: &str) -> f64 {
47 let text = text.trim_matches(|c: char| c.is_whitespace() || c == '\u{feff}');
48 if text.is_empty() {
49 return 0.0;
50 }
51 for (prefix, radix) in [("0x", 16), ("0o", 8), ("0b", 2)] {
52 if let Some(digits) = text
53 .get(..2)
54 .filter(|start| start.eq_ignore_ascii_case(prefix))
55 .and_then(|_| text.get(2..))
56 {
57 let value = digits.chars().try_fold(0.0_f64, |value, c| {
58 c.to_digit(radix)
59 .map(|digit| value * f64::from(radix) + f64::from(digit))
60 });
61 return value.filter(|_| !digits.is_empty()).unwrap_or(f64::NAN);
62 }
63 }
64 match text {
65 "Infinity" | "+Infinity" => return f64::INFINITY,
66 "-Infinity" => return f64::NEG_INFINITY,
67 _ => {}
68 }
69 if !text
71 .bytes()
72 .all(|byte| byte.is_ascii_digit() || b"+-.eE".contains(&byte))
73 {
74 return f64::NAN;
75 }
76 text.parse().unwrap_or(f64::NAN)
77}
78
79pub fn to_string_pretty<T: Serialize + ?Sized>(
81 value: &T,
82 indent: &str,
83) -> serde_json::Result<String> {
84 write(
85 value,
86 JsFormatter(PrettyFormatter::with_indent(indent.as_bytes())),
87 )
88}
89
90fn write<T: Serialize + ?Sized>(
91 value: &T,
92 formatter: impl Formatter,
93) -> serde_json::Result<String> {
94 let mut out = Vec::new();
95 value.serialize(&mut Serializer::with_formatter(&mut out, formatter))?;
96 Ok(String::from_utf8(out).expect("serde_json writes UTF-8"))
98}
99
100struct JsFormatter<F>(F);
103
104const MAX_SAFE_INTEGER: u64 = (1 << 53) - 1;
106
107impl<F: Formatter> Formatter for JsFormatter<F> {
108 fn write_f64<W: ?Sized + io::Write>(&mut self, writer: &mut W, value: f64) -> io::Result<()> {
109 writer.write_all(ryu_js::Buffer::new().format(value).as_bytes())
111 }
112
113 fn write_i64<W: ?Sized + io::Write>(&mut self, writer: &mut W, value: i64) -> io::Result<()> {
116 if value.unsigned_abs() > MAX_SAFE_INTEGER {
117 return self.write_f64(writer, value as f64);
118 }
119 self.0.write_i64(writer, value)
120 }
121
122 fn write_u64<W: ?Sized + io::Write>(&mut self, writer: &mut W, value: u64) -> io::Result<()> {
123 if value > MAX_SAFE_INTEGER {
124 return self.write_f64(writer, value as f64);
125 }
126 self.0.write_u64(writer, value)
127 }
128
129 fn begin_array<W: ?Sized + io::Write>(&mut self, writer: &mut W) -> io::Result<()> {
130 self.0.begin_array(writer)
131 }
132
133 fn end_array<W: ?Sized + io::Write>(&mut self, writer: &mut W) -> io::Result<()> {
134 self.0.end_array(writer)
135 }
136
137 fn begin_array_value<W: ?Sized + io::Write>(
138 &mut self,
139 writer: &mut W,
140 first: bool,
141 ) -> io::Result<()> {
142 self.0.begin_array_value(writer, first)
143 }
144
145 fn end_array_value<W: ?Sized + io::Write>(&mut self, writer: &mut W) -> io::Result<()> {
146 self.0.end_array_value(writer)
147 }
148
149 fn begin_object<W: ?Sized + io::Write>(&mut self, writer: &mut W) -> io::Result<()> {
150 self.0.begin_object(writer)
151 }
152
153 fn end_object<W: ?Sized + io::Write>(&mut self, writer: &mut W) -> io::Result<()> {
154 self.0.end_object(writer)
155 }
156
157 fn begin_object_key<W: ?Sized + io::Write>(
158 &mut self,
159 writer: &mut W,
160 first: bool,
161 ) -> io::Result<()> {
162 self.0.begin_object_key(writer, first)
163 }
164
165 fn end_object_key<W: ?Sized + io::Write>(&mut self, writer: &mut W) -> io::Result<()> {
166 self.0.end_object_key(writer)
167 }
168
169 fn begin_object_value<W: ?Sized + io::Write>(&mut self, writer: &mut W) -> io::Result<()> {
170 self.0.begin_object_value(writer)
171 }
172
173 fn end_object_value<W: ?Sized + io::Write>(&mut self, writer: &mut W) -> io::Result<()> {
174 self.0.end_object_value(writer)
175 }
176}
177
178pub fn parse<T: DeserializeOwned>(text: &str) -> serde_json::Result<T> {
181 serde_json::from_str(text.strip_prefix('\u{feff}').unwrap_or(text))
182}
183
184#[cfg(test)]
185mod tests {
186 use serde_json::{Value, json};
187
188 use super::*;
189
190 #[test]
191 fn reads_numbers_like_javascript() {
192 let number = |text| super::number(js_number(text));
193 assert_eq!(number("8080"), json!(8080));
194 assert_eq!(number(" 1.5e1 "), json!(15));
195 assert_eq!(number(""), json!(0));
196 assert_eq!(number("x"), Value::Null);
197 assert_eq!(number("0x1F90"), json!(8080));
198 assert_eq!(number(" 0o17 "), json!(15));
199 assert_eq!(number("0B101"), json!(5));
200 for nan in ["0x", "-0x10", "0x+1", "inf", "NaN", "1e", "1_000"] {
201 assert!(js_number(nan).is_nan(), "{nan}");
202 }
203 assert_eq!(js_number("-Infinity"), f64::NEG_INFINITY);
204 assert_eq!(number("Infinity"), Value::Null);
205 }
206
207 #[test]
208 fn parses_past_a_byte_order_mark() {
209 assert_eq!(
210 parse::<Value>("\u{feff}{\"a\":1}").ok(),
211 Some(json!({"a": 1}))
212 );
213 assert_eq!(parse::<Value>("[]").ok(), Some(json!([])));
214 assert!(parse::<Value>("\u{feff}\u{feff}[]").is_err());
215 }
216
217 #[test]
218 fn numbers_match_javascript() {
219 let cases: [(f64, &str); 12] = [
221 (0.0, "0"),
222 (-0.0, "0"),
223 (5.0, "5"),
224 (-42.0, "-42"),
225 (0.000003, "0.000003"),
226 (1e-7, "1e-7"),
227 (1e21, "1e+21"),
228 (0.1 + 0.2, "0.30000000000000004"),
229 (9007199254740992.0, "9007199254740992"),
230 (123.456, "123.456"),
231 (5e-324, "5e-324"),
232 (f64::MAX, "1.7976931348623157e+308"),
233 ];
234 for (value, expected) in cases {
235 assert_eq!(to_string(&value).unwrap(), expected, "{value:e}");
236 }
237 }
238
239 #[test]
240 fn large_integers_round_like_javascript() {
241 let value: Value = serde_json::from_str(
243 "[9007199254740991,9007199254740993,12345678901234567890,-12345678901234567]",
244 )
245 .unwrap();
246 assert_eq!(
247 to_string(&value).unwrap(),
248 "[9007199254740991,9007199254740992,12345678901234567000,-12345678901234568]"
249 );
250 }
251
252 #[test]
253 fn parsed_numbers_round_trip() {
254 let text = r#"[0,0.000003,0.0037020000000000004,1e+21,1893456000000,-1.5e-7]"#;
255 let value: Value = serde_json::from_str(text).unwrap();
256 assert_eq!(to_string(&value).unwrap(), text);
257 }
258
259 #[test]
260 fn pretty_matches_javascript_layout() {
261 let value = json!({ "a": [], "b": {}, "c": [1, { "d": null }] });
262 let expected = "{\n \"a\": [],\n \"b\": {},\n \"c\": [\n 1,\n {\n \"d\": null\n }\n ]\n}";
263 assert_eq!(to_string_pretty(&value, " ").unwrap(), expected);
264 }
265
266 #[test]
267 fn non_finite_numbers_are_null() {
268 assert_eq!(
269 to_string(&[f64::NAN, f64::INFINITY]).unwrap(),
270 "[null,null]"
271 );
272 }
273}