evobench_tools/utillib/
html_util.rs

1//! Should add these to ahtml crate.
2
3use ahtml::{AId, AllocatorType, HtmlAllocator, Node, ToASlice, att, flat::Flat};
4use anyhow::Result;
5
6// Utils for `Flat`
7
8/// How many items the Flat contains.
9pub fn flat_len<T: AllocatorType>(slf: &Flat<T>) -> u32 {
10    match slf {
11        Flat::None => 0,
12        Flat::One(_) => 1,
13        Flat::Two(_, _) => 2,
14        Flat::Slice(s) => s.len(),
15    }
16}
17
18pub fn flat_get<'t>(slf: &Flat<Node>, index: u32, html: &'t HtmlAllocator) -> Option<&'t Node> {
19    match slf {
20        Flat::None => None,
21        Flat::One(v) => {
22            if index == 0 {
23                html.get_node(*v)
24            } else {
25                None
26            }
27        }
28        Flat::Two(a, b) => {
29            let v = match index {
30                0 => a,
31                1 => b,
32                _ => return None,
33            };
34            html.get_node(*v)
35        }
36        Flat::Slice(s) => html.get_node(s.get(index, html)?),
37    }
38}
39
40/// Strip outer `div` and `p` HTML elements and return the body of the
41/// inner-most of them. E.g. `<div><p>Some <b>text</b>.</p></div>`
42/// becomes `Some <b>text</b>.`. If not possible, returns the original
43/// node. If `node` is not in `html`, silently returns the original
44/// node. If `keep_if_attributes` is true, only strips elements when
45/// they have no attributes.
46pub fn extract_paragraph_body(
47    node: AId<Node>,
48    keep_if_attributes: bool,
49    html: &HtmlAllocator,
50) -> Flat<Node> {
51    let mut body = Flat::One(node);
52    loop {
53        // If `body` is just 1 item, that is an element, and the
54        // element is "div" or "p", unwrap its body.
55        if flat_len(&body) == 1 {
56            let node = flat_get(&body, 0, html).expect("checked len is 1");
57            if let Some(element) = node.as_element() {
58                match element.meta.tag_name.as_str() {
59                    "div" | "p" => {
60                        if (!keep_if_attributes) || element.attr.len() == 0 {
61                            body = Flat::Slice(element.body)
62                        } else {
63                            break;
64                        }
65                    }
66                    _ => break,
67                }
68            } else {
69                break;
70            }
71        } else {
72            break;
73        }
74    }
75    body
76}
77
78/// Make an anchor (target for `#foo` style links) in the best compatible way
79pub fn anchor(
80    anchor_name: &str,
81    body: impl ToASlice<Node>,
82    html: &HtmlAllocator,
83) -> Result<AId<Node>> {
84    html.a([att("name", anchor_name), att("id", anchor_name)], body)
85}