Skip to main content

rightkit_control/
json.rs

1//! Minimal JSON helpers for the WebDriver wire (flat string extraction only).
2
3pub 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
19/// First string value for `"key": "..."` anywhere in `body`.
20pub 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
54/// First numeric value for `"key": 12.5` anywhere in `body`.
55pub 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}