use alloc::collections::BTreeMap;
use alloc::string::String;
use crate::ast::{Block, Definition, HtmlContainerContent, Inline};
use super::escape::escape_text;
pub struct DefMap {
entries: BTreeMap<String, Definition>,
}
impl DefMap {
pub fn build(blocks: &[Block]) -> Self {
let mut entries = BTreeMap::new();
collect_defs(blocks, &mut entries);
Self { entries }
}
pub fn resolve(&self, identifier: &str) -> Option<&Definition> {
self.entries.get(&normalize_label(identifier))
}
}
fn collect_defs(blocks: &[Block], out: &mut BTreeMap<String, Definition>) {
for block in blocks {
match block {
Block::Definition(def) => {
let key = normalize_label(&def.identifier);
out.entry(key).or_insert_with(|| def.clone());
}
Block::BlockQuote(bq) => collect_defs(&bq.children, out),
Block::Alert(alert) => collect_defs(&alert.children, out),
Block::List(list) => {
for item in &list.children {
collect_defs(&item.children, out);
}
}
Block::FootnoteDefinition(fd) => collect_defs(&fd.children, out),
Block::HtmlContainer(container) => {
if let HtmlContainerContent::Blocks(children) = &container.content {
collect_defs(children, out);
}
}
Block::ContainerDirective(dir) => collect_defs(&dir.children, out),
_ => {}
}
}
}
pub fn normalize_label(label: &str) -> String {
let mut collapsed = String::with_capacity(label.len());
let mut in_ws = false;
let mut started = false;
for ch in label.chars() {
if ch.is_whitespace() {
in_ws = true;
continue;
}
if in_ws && started {
collapsed.push(' ');
}
in_ws = false;
started = true;
collapsed.push(ch);
}
collapsed.to_uppercase().to_lowercase()
}
pub fn flatten_alt(inlines: &[Inline]) -> String {
let mut out = String::new();
flatten_into(inlines, &mut out);
out
}
fn flatten_into(inlines: &[Inline], out: &mut String) {
for inline in inlines {
match inline {
Inline::Text(t) => out.push_str(&t.value),
Inline::Escape(e) => out.push(e.value),
Inline::CharacterReference(c) => out.push_str(&c.value().unwrap_or_default()),
Inline::Code(c) => out.push_str(&c.value),
Inline::Math(m) => out.push_str(&m.value),
Inline::Emphasis(n) => flatten_into(&n.children, out),
Inline::Strong(n) => flatten_into(&n.children, out),
Inline::Delete(n) => flatten_into(&n.children, out),
Inline::Mark(n) => flatten_into(&n.children, out),
Inline::Link(n) => flatten_into(&n.children, out),
Inline::Autolink(n) => out.push_str(&n.text),
Inline::LinkReference(n) => flatten_into(&n.children, out),
Inline::Image(n) => flatten_into(&n.alt, out),
Inline::ImageReference(n) => flatten_into(&n.alt, out),
Inline::InlineFootnote(n) => flatten_into(&n.children, out),
Inline::SoftBreak(_) => out.push('\n'),
Inline::LineBreak(_) => out.push('\n'),
Inline::Html(h) => out.push_str(&h.value),
Inline::WikiLink(w) => out.push_str(&w.decoded_label()),
Inline::Shortcode(s) => match s.glyph() {
Some(glyph) => out.push_str(glyph),
None => {
out.push(':');
out.push_str(&s.name);
out.push(':');
}
},
Inline::Tag(t) => {
out.push('#');
out.push_str(&t.name);
}
Inline::FootnoteReference(_) => {}
Inline::TextDirective(d) => flatten_into(&d.label, out),
}
}
}
pub fn escaped_alt(alt: &[Inline]) -> String {
escape_text(&flatten_alt(alt))
}