use std::collections::BTreeMap;
use super::tree::{Element, Node, attr_value};
const NOTEREF_ROLE: &str = "doc-noteref";
pub(super) const ENDNOTES_ROLE: &str = "doc-endnotes";
const BACKLINK_ROLE: &str = "doc-backlink";
pub(super) fn has_role(e: &Element, role: &str) -> bool {
attr_value(e, "role").is_some_and(|value| value == role)
}
pub(super) fn noteref_target(e: &Element) -> Option<String> {
if e.name != "a" || !has_role(e, NOTEREF_ROLE) {
return None;
}
let href = attr_value(e, "href")?;
href.strip_prefix('#').map(str::to_owned)
}
pub(super) fn collect_note_defs(nodes: &[&Node]) -> BTreeMap<String, Vec<Node>> {
let mut defs = BTreeMap::new();
for node in nodes {
collect_from_node(node, false, &mut defs);
}
defs
}
fn collect_from_node(node: &Node, in_endnotes: bool, defs: &mut BTreeMap<String, Vec<Node>>) {
let Node::Element(e) = node else { return };
let inside = in_endnotes || has_role(e, ENDNOTES_ROLE);
if inside
&& e.name == "li"
&& let Some(id) = attr_value(e, "id")
{
defs.entry(id)
.or_insert_with(|| strip_backlinks(&e.children));
}
for child in &e.children {
collect_from_node(child, inside, defs);
}
}
fn strip_backlinks(nodes: &[Node]) -> Vec<Node> {
nodes
.iter()
.filter_map(|node| match node {
Node::Element(e) if e.name == "a" && has_role(e, BACKLINK_ROLE) => None,
Node::Element(e) => {
let mut cloned = e.clone();
cloned.children = strip_backlinks(&e.children);
Some(Node::Element(cloned))
}
Node::Text(_) => Some(node.clone()),
})
.collect()
}