web_view/
escape.rs

1use std::fmt::{self, Write};
2
3/// Escape a string to pass it into JavaScript.
4///
5/// # Example
6///
7/// ```rust,no_run
8/// # use web_view::WebView;
9/// # use std::mem;
10/// #
11/// # let mut view: WebView<()> = unsafe { mem::uninitialized() };
12/// #
13/// let string = "Hello, world!";
14///
15/// // Calls the function callback with "Hello, world!" as its parameter.
16///
17/// view.eval(&format!("callback({});", web_view::escape(string)));
18/// ```
19pub fn escape(string: &str) -> Escaper {
20    Escaper(string)
21}
22
23// "All code points may appear literally in a string literal except for the
24// closing quote code points, U+005C (REVERSE SOLIDUS), U+000D (CARRIAGE
25// RETURN), U+2028 (LINE SEPARATOR), U+2029 (PARAGRAPH SEPARATOR), and U+000A
26// (LINE FEED)." - ES6 Specification
27
28pub struct Escaper<'a>(&'a str);
29
30const SPECIAL: &[char] = &[
31    '\n',       // U+000A (LINE FEED)
32    '\r',       // U+000D (CARRIAGE RETURN)
33    '\'',       // U+0027 (APOSTROPHE)
34    '\\',       // U+005C (REVERSE SOLIDUS)
35    '\u{2028}', // U+2028 (LINE SEPARATOR)
36    '\u{2029}', // U+2029 (PARAGRAPH SEPARATOR)
37];
38
39impl<'a> fmt::Display for Escaper<'a> {
40    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41        let &Escaper(mut string) = self;
42
43        f.write_char('\'')?;
44
45        while !string.is_empty() {
46            if let Some(i) = string.find(SPECIAL) {
47                if i > 0 {
48                    f.write_str(&string[..i])?;
49                }
50
51                let mut chars = string[i..].chars();
52
53                f.write_str(match chars.next().unwrap() {
54                    '\n' => "\\n",
55                    '\r' => "\\r",
56                    '\'' => "\\'",
57                    '\\' => "\\\\",
58                    '\u{2028}' => "\\u2028",
59                    '\u{2029}' => "\\u2029",
60                    _ => unreachable!(),
61                })?;
62
63                string = chars.as_str();
64            } else {
65                f.write_str(string)?;
66                break;
67            }
68        }
69
70        f.write_char('\'')?;
71
72        Ok(())
73    }
74}
75
76#[test]
77fn test() {
78    let plain = "ABC \n\r' abc \\  \u{2028}   \u{2029}123";
79    let escaped = escape(plain).to_string();
80    assert!(escaped == "'ABC \\n\\r\\' abc \\\\  \\u2028   \\u2029123'");
81}