1pub fn esc(s: &str) -> String {
4 let mut o = String::with_capacity(s.len() + 2);
5 for c in s.chars() {
6 match c {
7 '"' => o.push_str("\\\""),
8 '\\' => o.push_str("\\\\"),
9 '\n' => o.push_str("\\n"),
10 '\r' => o.push_str("\\r"),
11 '\t' => o.push_str("\\t"),
12 c if (c as u32) < 0x20 => o.push_str(&format!("\\u{:04x}", c as u32)),
13 c => o.push(c),
14 }
15 }
16 o
17}
18
19pub fn str_field(body: &str, key: &str) -> Option<String> {
21 let pat = format!("\"{key}\"");
22 let mut from = 0;
23 while let Some(i) = body[from..].find(&pat) {
24 let mut rest = body[from + i + pat.len()..].trim_start();
25 if let Some(r) = rest.strip_prefix(':') {
26 rest = r.trim_start();
27 if let Some(r) = rest.strip_prefix('"') {
28 let mut out = String::new();
29 let mut it = r.chars();
30 while let Some(c) = it.next() {
31 match c {
32 '"' => return Some(out),
33 '\\' => match it.next()? {
34 'n' => out.push('\n'),
35 't' => out.push('\t'),
36 'r' => out.push('\r'),
37 'u' => {
38 let h: String = it.by_ref().take(4).collect();
39 out.push(char::from_u32(u32::from_str_radix(&h, 16).ok()?)?);
40 }
41 o => out.push(o),
42 },
43 c => out.push(c),
44 }
45 }
46 return None;
47 }
48 }
49 from += i + pat.len();
50 }
51 None
52}
53
54pub fn num_field(body: &str, key: &str) -> Option<f64> {
56 let pat = format!("\"{key}\"");
57 let i = body.find(&pat)?;
58 let rest = body[i + pat.len()..]
59 .trim_start()
60 .strip_prefix(':')?
61 .trim_start();
62 let end = rest
63 .find(|c: char| !(c.is_ascii_digit() || "+-.eE".contains(c)))
64 .unwrap_or(rest.len());
65 rest[..end].parse().ok()
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 #[test]
72 fn extracts_and_escapes() {
73 let b = r#"{"capabilities":{},"using":"link text","value":"a\"bA"}"#;
74 assert_eq!(str_field(b, "using").as_deref(), Some("link text"));
75 assert_eq!(str_field(b, "value").as_deref(), Some("a\"bA"));
76 assert_eq!(esc("a\"\n"), "a\\\"\\n");
77 assert_eq!(str_field(b, "missing"), None);
78 assert_eq!(num_field(r#"{"x": 12.5,"y":-3}"#, "y"), Some(-3.0));
79 assert_eq!(num_field(r#"{"x": 12.5}"#, "x"), Some(12.5));
80 }
81}