Skip to main content

textlane_html/actions/hast/
gemoji.rs

1use textlane_core::hast::{Element, Node, Property, PropertyValue};
2use textlane_core::{Allocator, ArenaVec, find_gemoji_shortcodes, read_gemoji_label};
3
4use crate::helpers::prose::{create_text, replace_prose};
5use crate::models::options::Gemoji;
6
7/// Turn the gemoji shortcodes in text, `:rocket:`, into their emoji; with `accessible`,
8/// each in a `<span role="img" aria-label="rocket emoji">`.
9pub fn replace_shortcodes<'a>(
10    node: &mut Node<'a>,
11    options: &Gemoji,
12    alloc: &'a Allocator,
13) {
14    replace_prose(
15        node,
16        &options.ignore,
17        &options.ignore_attributes,
18        alloc,
19        |text, out| {
20            let found = find_gemoji_shortcodes(text);
21            if found.is_empty() {
22                return false;
23            }
24            let mut plain = String::new();
25            let mut last = 0;
26            for (start, end, emoji) in found {
27                plain.push_str(&text[last..start]);
28                match options.accessible {
29                    true => {
30                        if !plain.is_empty() {
31                            out.push(create_text(&std::mem::take(&mut plain), alloc));
32                        }
33                        let label = read_gemoji_label(&text[start + 1..end - 1]);
34                        out.push(create_label(emoji, &format!("{label} emoji"), alloc));
35                    }
36                    false => plain.push_str(emoji),
37                }
38                last = end;
39            }
40            plain.push_str(&text[last..]);
41            if !plain.is_empty() {
42                out.push(create_text(&plain, alloc));
43            }
44            true
45        },
46    );
47}
48
49fn create_label<'a>(emoji: &str, label: &str, alloc: &'a Allocator) -> Node<'a> {
50    let mut properties = ArenaVec::with_capacity_in(2, alloc);
51    properties.push(Property {
52        name: "role",
53        value: PropertyValue::String("img"),
54    });
55    properties.push(Property {
56        name: "ariaLabel",
57        value: PropertyValue::String(alloc.alloc_str(label)),
58    });
59    let mut children = ArenaVec::with_capacity_in(1, alloc);
60    children.push(create_text(emoji, alloc));
61    Node::Element(Element {
62        tag_name: "span",
63        properties,
64        children,
65        position: None,
66        data: None,
67        content: None,
68    })
69}
70
71#[cfg(test)]
72#[path = "gemoji_unit.rs"]
73mod unit;