1use std::fmt::Write;
4
5pub fn string(s: &str) -> String {
7 let mut out = String::with_capacity(s.len() + 2);
8 out.push('"');
9 for c in s.chars() {
10 match c {
11 '"' => out.push_str("\\\""),
12 '\\' => out.push_str("\\\\"),
13 '\n' => out.push_str("\\n"),
14 '\r' => out.push_str("\\r"),
15 '\t' => out.push_str("\\t"),
16 c if (c as u32) < 0x20 => {
17 let _ = write!(out, "\\u{:04x}", c as u32);
18 }
19 c => out.push(c),
20 }
21 }
22 out.push('"');
23 out
24}
25
26pub fn number(v: f64) -> String {
28 if v.is_finite() { format!("{v}") } else { "null".to_string() }
29}
30
31pub fn object(fields: &[(&str, String)]) -> String {
33 let body: Vec<String> = fields.iter().map(|(k, v)| format!("{}: {}", string(k), v)).collect();
34 format!("{{{}}}", body.join(", "))
35}
36
37pub fn array(items: &[String]) -> String {
39 format!("[{}]", items.join(", "))
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn escaping() {
48 assert_eq!(string("a\"b\\c\n"), "\"a\\\"b\\\\c\\n\"");
49 assert_eq!(number(f64::NAN), "null");
50 assert_eq!(object(&[("x", number(1.5))]), "{\"x\": 1.5}");
51 }
52}