lightml/
operations.rs

1use super::*;
2
3/** TODO Problems:
4- Doesn't escape character codes
5- Lots of spacing probkems
6*/
7pub fn inner_text(element: &Element) -> String {
8    fn inner_text_(element: &Element, buf: &mut String) {
9        if let "math" | "svg" | "title" = element.tag_name.as_str() {
10            return;
11        }
12        if let ElementChildren::Children(ref children) = element.children {
13            for child in children {
14                match child {
15                    Node::Element(element) => {
16                        inner_text_(element, buf);
17                    }
18                    Node::TextNode(content) => {
19                        buf.push_str(&unescape_string_content(content));
20                    }
21                    Node::Comment(..) => {}
22                }
23            }
24        }
25    }
26
27    let mut s = String::new();
28    inner_text_(element, &mut s);
29    s
30}
31
32/// Modified version of <https://github.com/parcel-bundler/parcel/blob/f86f5f27c3a6553e70bd35652f19e6ab8d8e4e4a/crates/dev-dep-resolver/src/lib.rs#L368-L380>
33#[must_use]
34pub fn unescape_string_content(on: &str) -> Cow<'_, str> {
35    let mut result = Cow::Borrowed("");
36    let mut start = 0;
37    for (index, _matched) in on.match_indices('&') {
38        result += &on[start..index];
39        let after = &on[(index + '&'.len_utf8())..];
40        if after.starts_with("quot;") {
41            start = index + "quot;".len();
42            result += "\"";
43        } else if after.starts_with("amp;") {
44            start = index + "amp;".len();
45            result += "&";
46        } else if after.starts_with("lt;") {
47            start = index + "lt;".len();
48            result += "<";
49        } else if after.starts_with("gt;") {
50            start = index + "gt;".len();
51            result += ">";
52        } else if let Some(after) = after.strip_prefix('#') {
53            let mut found = false;
54            for (idx, chr) in after.char_indices() {
55                if let ';' = chr {
56                    // TODO unwraps
57                    let item = &after[..idx];
58                    let code: u32 = if let Some(rest) = item.strip_prefix(['x', 'X']) {
59                        let mut value = 0u32;
60                        for c in rest.as_bytes() {
61                            value <<= 4; // 16=2^4
62                            match c {
63                                b'0'..=b'9' => {
64                                    value += u32::from(c - b'0');
65                                }
66                                b'a'..=b'f' => {
67                                    value += u32::from(c - b'a') + 10;
68                                }
69                                b'A'..=b'F' => {
70                                    value += u32::from(c - b'A') + 10;
71                                }
72                                chr => panic!("{chr:?}"),
73                            }
74                        }
75                        value
76                    } else {
77                        item.parse().unwrap()
78                    };
79                    let entity = char::from_u32(code).unwrap().to_string();
80                    // Cannot += to result because of lifetimes
81                    let mut out = result.into_owned();
82                    out.push_str(&entity);
83                    result = Cow::Owned(out);
84                    start = index + "&#".len() + idx + ";".len();
85                    found = true;
86                    break;
87                }
88            }
89            if !found {
90                panic!()
91            }
92        }
93    }
94    result += &on[start..];
95    result
96}