use std::marker::PhantomData;
use crate::model::{
Block, BlockMetadata, DelimitedBlock, DelimitedBlockType, Document, InlineMacro, InlineNode,
Location,
};
pub(crate) fn walk_document_locations_mut<F: FnMut(&mut Location)>(
doc: &mut Document<'_>,
visit: &mut F,
) {
AstWalker::new(visit, |_| {}).document(doc);
}
pub(crate) fn walk_document_inline_nodes_mut<'a, F: FnMut(&mut InlineNode<'a>)>(
doc: &mut Document<'a>,
visit: &mut F,
) {
AstWalker::new(|_| {}, visit).document(doc);
}
pub(crate) fn walk_inline_locations_mut<F: FnMut(&mut Location)>(
node: &mut InlineNode<'_>,
visit: &mut F,
) {
AstWalker::new(visit, |_| {}).inline(node);
}
pub(crate) fn walk_inline_nodes_mut<'a, F: FnMut(&mut InlineNode<'a>)>(
node: &mut InlineNode<'a>,
visit: &mut F,
) {
AstWalker::new(|_| {}, visit).inline(node);
}
struct AstWalker<'a, FL, FI> {
visit_location: FL,
visit_inline: FI,
inline_lifetime: PhantomData<fn(&mut InlineNode<'a>)>,
}
impl<'a, FL, FI> AstWalker<'a, FL, FI>
where
FL: FnMut(&mut Location),
FI: FnMut(&mut InlineNode<'a>),
{
fn new(visit_location: FL, visit_inline: FI) -> Self {
Self {
visit_location,
visit_inline,
inline_lifetime: PhantomData,
}
}
fn document(&mut self, doc: &mut Document<'a>) {
(self.visit_location)(&mut doc.location);
if let Some(header) = &mut doc.header {
(self.visit_location)(&mut header.location);
self.metadata(&mut header.metadata);
self.inlines(header.title.inlines_mut());
if let Some(subtitle) = &mut header.subtitle {
self.inlines(subtitle.inlines_mut());
}
}
self.blocks(&mut doc.blocks);
for footnote in &mut doc.footnotes {
(self.visit_location)(&mut footnote.location);
self.inlines(&mut footnote.content);
}
for entry in &mut doc.toc_entries {
(self.visit_location)(&mut entry.location);
self.inlines(entry.title.inlines_mut());
}
for reference in doc.references.values_mut() {
(self.visit_location)(&mut reference.location);
if let Some(title) = &mut reference.title {
self.inlines(title.inlines_mut());
}
}
}
fn blocks(&mut self, blocks: &mut [Block<'a>]) {
for block in blocks {
self.block(block);
}
}
fn block(&mut self, block: &mut Block<'a>) {
(self.visit_location)(block.location_mut());
match block {
Block::Section(s) => {
self.metadata(&mut s.metadata);
self.inlines(s.title.inlines_mut());
self.blocks(&mut s.content);
}
Block::Paragraph(p) => {
self.metadata(&mut p.metadata);
self.inlines(p.title.inlines_mut());
self.inlines(&mut p.content);
}
Block::UnorderedList(l) => {
self.metadata(&mut l.metadata);
self.inlines(l.title.inlines_mut());
for item in &mut l.items {
(self.visit_location)(&mut item.location);
self.inlines(&mut item.principal);
self.blocks(&mut item.blocks);
}
}
Block::OrderedList(l) => {
self.metadata(&mut l.metadata);
self.inlines(l.title.inlines_mut());
for item in &mut l.items {
(self.visit_location)(&mut item.location);
self.inlines(&mut item.principal);
self.blocks(&mut item.blocks);
}
}
Block::DescriptionList(l) => {
self.metadata(&mut l.metadata);
self.inlines(l.title.inlines_mut());
for item in &mut l.items {
(self.visit_location)(&mut item.location);
self.optional_location(&mut item.delimiter_location);
for anchor in &mut item.anchors {
(self.visit_location)(&mut anchor.location);
}
self.inlines(&mut item.term);
self.inlines(&mut item.principal_text);
self.blocks(&mut item.description);
}
}
Block::CalloutList(l) => {
self.metadata(&mut l.metadata);
self.inlines(l.title.inlines_mut());
for item in &mut l.items {
(self.visit_location)(&mut item.location);
self.inlines(&mut item.principal);
self.blocks(&mut item.blocks);
}
}
Block::Admonition(a) => {
self.metadata(&mut a.metadata);
self.inlines(a.title.inlines_mut());
self.blocks(&mut a.blocks);
}
Block::DiscreteHeader(h) => {
self.metadata(&mut h.metadata);
self.inlines(h.title.inlines_mut());
}
Block::DelimitedBlock(d) => self.delimited_block(d),
Block::ThematicBreak(tb) => {
self.inlines(tb.title.inlines_mut());
for anchor in &mut tb.anchors {
(self.visit_location)(&mut anchor.location);
}
}
Block::Image(i) => {
self.metadata(&mut i.metadata);
self.inlines(i.title.inlines_mut());
}
Block::Audio(a) => self.metadata(&mut a.metadata),
Block::Video(v) => self.metadata(&mut v.metadata),
Block::TableOfContents(_)
| Block::DocumentAttribute(_)
| Block::PageBreak(_)
| Block::Comment(_) => {}
}
}
fn inlines(&mut self, nodes: &mut [InlineNode<'a>]) {
for node in nodes {
self.inline(node);
}
}
fn inline(&mut self, node: &mut InlineNode<'a>) {
(self.visit_location)(node.location_mut());
(self.visit_inline)(node);
match node {
InlineNode::BoldText(t) => self.inlines(&mut t.content),
InlineNode::ItalicText(t) => self.inlines(&mut t.content),
InlineNode::MonospaceText(t) => self.inlines(&mut t.content),
InlineNode::HighlightText(t) => self.inlines(&mut t.content),
InlineNode::SubscriptText(t) => self.inlines(&mut t.content),
InlineNode::SuperscriptText(t) => self.inlines(&mut t.content),
InlineNode::CurvedQuotationText(t) => self.inlines(&mut t.content),
InlineNode::CurvedApostropheText(t) => self.inlines(&mut t.content),
InlineNode::Macro(m) => self.inline_macro(m),
InlineNode::InlineAnchor(anchor) => {
if let Some(label) = &mut anchor.bibliography_label {
self.inlines(&mut label.content);
}
}
InlineNode::PlainText(_)
| InlineNode::RawText(_)
| InlineNode::VerbatimText(_)
| InlineNode::StandaloneCurvedApostrophe(_)
| InlineNode::LineBreak(_)
| InlineNode::CalloutRef(_) => {}
}
}
fn inline_macro(&mut self, m: &mut InlineMacro<'a>) {
match m {
InlineMacro::Footnote(f) => self.inlines(&mut f.content),
InlineMacro::Link(l) => self.inlines(&mut l.text),
InlineMacro::Url(u) => self.inlines(&mut u.text),
InlineMacro::Mailto(m) => self.inlines(&mut m.text),
InlineMacro::CrossReference(x) => self.inlines(&mut x.text),
InlineMacro::IndexTerm(term) => self.index_term(term),
InlineMacro::Icon(_)
| InlineMacro::Image(_)
| InlineMacro::Keyboard(_)
| InlineMacro::Button(_)
| InlineMacro::Menu(_)
| InlineMacro::Autolink(_)
| InlineMacro::Pass(_)
| InlineMacro::Stem(_) => {}
}
}
fn index_term(&mut self, term: &mut crate::IndexTerm<'a>) {
match &mut term.kind {
crate::IndexTermKind::Flow(term) => self.inlines(term),
crate::IndexTermKind::Concealed {
term,
secondary,
tertiary,
} => {
self.inlines(term);
if let Some(secondary) = secondary {
self.inlines(secondary);
}
if let Some(tertiary) = tertiary {
self.inlines(tertiary);
}
}
}
if let Some(relationship) = &mut term.relationship {
match relationship {
crate::IndexTermRelationship::See { target } => self.inlines(target),
crate::IndexTermRelationship::SeeAlso { targets } => {
for target in targets {
self.inlines(target);
}
}
}
}
if let Some(catalog) = &mut term.catalog {
(self.visit_location)(&mut catalog.location);
self.index_term(catalog);
}
}
fn metadata(&mut self, metadata: &mut BlockMetadata<'a>) {
self.optional_location(&mut metadata.location);
if let Some(anchor) = &mut metadata.id {
(self.visit_location)(&mut anchor.location);
}
for anchor in &mut metadata.anchors {
(self.visit_location)(&mut anchor.location);
}
for attribute in &mut metadata.positional_attributes {
self.optional_location(&mut attribute.location);
}
if let Some(attributes) = &mut metadata.retained_positional_attributes {
for attribute in attributes {
self.optional_location(&mut attribute.location);
}
}
if let Some(attribution) = &mut metadata.attribution {
self.inlines(attribution.inlines_mut());
}
if let Some(citetitle) = &mut metadata.citetitle {
self.inlines(citetitle.inlines_mut());
}
}
fn delimited_block(&mut self, d: &mut DelimitedBlock<'a>) {
self.metadata(&mut d.metadata);
self.inlines(d.title.inlines_mut());
self.optional_location(&mut d.open_delimiter_location);
self.optional_location(&mut d.close_delimiter_location);
match &mut d.inner {
DelimitedBlockType::DelimitedExample(blocks)
| DelimitedBlockType::DelimitedOpen(blocks)
| DelimitedBlockType::DelimitedSidebar(blocks)
| DelimitedBlockType::DelimitedQuote(blocks) => self.blocks(blocks),
DelimitedBlockType::DelimitedComment(nodes)
| DelimitedBlockType::DelimitedListing(nodes)
| DelimitedBlockType::DelimitedLiteral(nodes)
| DelimitedBlockType::DelimitedPass(nodes)
| DelimitedBlockType::DelimitedVerse(nodes) => self.inlines(nodes),
DelimitedBlockType::DelimitedTable(table) => {
(self.visit_location)(&mut table.location);
let rows = table
.header
.iter_mut()
.chain(table.rows.iter_mut())
.chain(table.footer.iter_mut());
for row in rows {
for col in &mut row.columns {
self.blocks(&mut col.content);
}
}
}
DelimitedBlockType::DelimitedStem(_) => {}
}
}
fn optional_location(&mut self, location: &mut Option<Location>) {
if let Some(location) = location {
(self.visit_location)(location);
}
}
}