use crate::model::block::{
Alignment, BlockContent, Caption, Column, DefinitionList, FrontMatter, HasAlignment,
HasCaption, HeadingLevel, Label, List, ListItem, ListKind, Table,
};
use crate::model::document::Metadata;
use crate::model::inline::{Character, HyperLink, Image, InlineContent, Math, SpanStyle, Text};
use crate::model::{block::HasLabel, HasInnerContent};
use crate::model::{Document, HasStyles};
#[allow(unused_variables)]
pub trait DocumentVisitor {
fn start_document(&self) -> crate::error::Result<()> {
Ok(())
}
fn start_metadata(&self) -> crate::error::Result<()> {
Ok(())
}
fn metadata(&self, metadatum: &Metadata) -> crate::error::Result<()> {
Ok(())
}
fn end_metadata(&self) -> crate::error::Result<()> {
Ok(())
}
fn block_visitor(&self) -> Option<&dyn BlockVisitor> {
None
}
fn end_document(&self) -> crate::error::Result<()> {
Ok(())
}
}
#[allow(unused_variables)]
pub trait BlockVisitor {
fn start_block(&self) -> crate::error::Result<()> {
Ok(())
}
fn start_abstract(&self) -> crate::error::Result<()> {
Ok(())
}
fn end_abstract(&self) -> crate::error::Result<()> {
Ok(())
}
fn comment(&self, value: &str) -> crate::error::Result<()> {
Ok(())
}
fn front_matter(&self, value: &FrontMatter) -> crate::error::Result<()> {
Ok(())
}
fn start_heading(
&self,
level: &HeadingLevel,
label: &Option<Label>,
) -> crate::error::Result<()> {
Ok(())
}
fn end_heading(&self, level: &HeadingLevel, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn image(
&self,
value: &Image,
caption: &Option<Caption>,
label: &Option<Label>,
) -> crate::error::Result<()> {
Ok(())
}
fn math(
&self,
value: &Math,
caption: &Option<Caption>,
label: &Option<Label>,
) -> crate::error::Result<()> {
Ok(())
}
fn start_list(&self, kind: &ListKind, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn end_list(&self, kind: &ListKind, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn start_list_item(&self, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn end_list_item(&self, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn start_definition_list(&self, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn end_definition_list(&self, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn start_definition(&self, term: &str, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn start_definition_list_text(&self) -> crate::error::Result<()> {
Ok(())
}
fn end_definition_list_text(&self) -> crate::error::Result<()> {
Ok(())
}
fn end_definition(&self, term: &str, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn formatted(&self, value: &str, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn code_block(
&self,
code: &str,
language: &Option<String>,
caption: &Option<Caption>,
label: &Option<Label>,
) -> crate::error::Result<()> {
Ok(())
}
fn start_paragraph(
&self,
alignment: &Alignment,
label: &Option<Label>,
) -> crate::error::Result<()> {
Ok(())
}
fn end_paragraph(
&self,
alignment: &Alignment,
label: &Option<Label>,
) -> crate::error::Result<()> {
Ok(())
}
fn start_quote(&self, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn end_quote(&self, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn thematic_break(&self) -> crate::error::Result<()> {
Ok(())
}
fn end_block(&self) -> crate::error::Result<()> {
Ok(())
}
fn table_visitor(&self) -> Option<&dyn TableVisitor> {
None
}
fn inline_visitor(&self) -> Option<&dyn InlineVisitor> {
None
}
}
#[allow(unused_variables)]
pub trait TableVisitor {
fn start_table(
&self,
caption: &Option<Caption>,
label: &Option<Label>,
) -> crate::error::Result<()> {
Ok(())
}
fn start_table_header_row(&self) -> crate::error::Result<()> {
Ok(())
}
fn table_header_cell(
&self,
column_cell: &Column,
column_idx: usize,
) -> crate::error::Result<()> {
Ok(())
}
fn end_table_header_row(&self) -> crate::error::Result<()> {
Ok(())
}
fn start_table_row(&self, row: usize) -> crate::error::Result<()> {
Ok(())
}
fn start_table_cell(
&self,
column_idx: usize,
label: &Option<Label>,
) -> crate::error::Result<()> {
Ok(())
}
fn end_table_cell(&self, column_idx: usize, label: &Option<Label>) -> crate::error::Result<()> {
Ok(())
}
fn end_table_row(&self, row: usize) -> crate::error::Result<()> {
Ok(())
}
fn end_table(
&self,
caption: &Option<Caption>,
label: &Option<Label>,
) -> crate::error::Result<()> {
Ok(())
}
fn inline_visitor(&self) -> Option<&dyn InlineVisitor> {
None
}
}
#[allow(unused_variables)]
pub trait InlineVisitor {
fn link(&self, value: &HyperLink) -> crate::error::Result<()> {
Ok(())
}
fn image(&self, value: &Image) -> crate::error::Result<()> {
Ok(())
}
fn text(&self, value: &Text) -> crate::error::Result<()> {
Ok(())
}
fn math(&self, value: &Math) -> crate::error::Result<()> {
Ok(())
}
fn character(&self, value: &Character) -> crate::error::Result<()> {
Ok(())
}
fn line_break(&self) -> crate::error::Result<()> {
Ok(())
}
fn start_span(&self, styles: &[SpanStyle]) -> crate::error::Result<()> {
Ok(())
}
fn end_span(&self, styles: &[SpanStyle]) -> crate::error::Result<()> {
Ok(())
}
}
pub fn walk_document(doc: &Document, visitor: &impl DocumentVisitor) -> crate::error::Result<()> {
visitor.start_document()?;
if doc.has_metadata() {
visitor.start_metadata()?;
for datum in doc.metadata() {
visitor.metadata(datum)?;
}
visitor.end_metadata()?;
}
if let Some(block_visitor) = visitor.block_visitor() {
if let Some(abstract_block) = doc.abstract_block() {
block_visitor.start_block()?;
block_visitor.start_abstract()?;
if let Some(inline_visitor) = block_visitor.inline_visitor() {
walk_inline(abstract_block.inner(), inline_visitor)?;
}
block_visitor.end_abstract()?;
block_visitor.end_block()?;
}
walk_all_blocks(doc.inner(), block_visitor)?;
}
visitor.end_document()
}
fn walk_all_blocks(
blocks: &[BlockContent],
visitor: &dyn BlockVisitor,
) -> crate::error::Result<()> {
for block in blocks {
walk_block(block, visitor)?;
}
Ok(())
}
fn walk_block(block: &BlockContent, visitor: &dyn BlockVisitor) -> crate::error::Result<()> {
visitor.start_block()?;
match block {
BlockContent::Comment(v) => visitor.comment(v)?,
BlockContent::FrontMatter(v) => visitor.front_matter(v)?,
BlockContent::Heading(v) => {
visitor.start_heading(v.level(), v.label())?;
if let Some(inline_visitor) = visitor.inline_visitor() {
walk_inline(v.inner(), inline_visitor)?;
}
visitor.end_heading(v.level(), v.label())?;
}
BlockContent::ImageBlock(v) => visitor.image(v.inner(), v.caption(), v.label())?,
BlockContent::MathBlock(v) => visitor.math(v.inner(), v.caption(), v.label())?,
BlockContent::List(v) => walk_list(v, visitor)?,
BlockContent::DefinitionList(v) => walk_definition_list(v, visitor)?,
BlockContent::Formatted(v) => visitor.formatted(v.inner(), v.label())?,
BlockContent::CodeBlock(v) => {
visitor.code_block(v.code(), v.language(), v.caption(), v.label())?
}
BlockContent::Paragraph(v) => {
visitor.start_paragraph(v.alignment(), v.label())?;
if let Some(inline_visitor) = visitor.inline_visitor() {
walk_inline(v.inner(), inline_visitor)?;
}
visitor.end_paragraph(v.alignment(), v.label())?;
}
BlockContent::Quote(v) => {
visitor.start_quote(v.label())?;
walk_all_blocks(v.inner(), visitor)?;
visitor.end_quote(v.label())?;
}
BlockContent::Table(v) => {
if let Some(table_visitor) = visitor.table_visitor() {
walk_table(v, table_visitor)?;
}
}
BlockContent::ThematicBreak => visitor.thematic_break()?,
}
visitor.end_block()?;
Ok(())
}
fn walk_list(list: &List, visitor: &dyn BlockVisitor) -> crate::error::Result<()> {
visitor.start_list(list.kind(), list.label())?;
for inner in list.inner() {
match inner {
ListItem::List(v) => {
walk_list(&v, visitor)?;
}
ListItem::Item(v) => {
visitor.start_list_item(v.label())?;
if let Some(inline_visitor) = visitor.inline_visitor() {
walk_inline(v.inner(), inline_visitor)?;
}
visitor.end_list_item(v.label())?;
}
}
}
visitor.end_list(list.kind(), list.label())
}
fn walk_definition_list(
list: &DefinitionList,
visitor: &dyn BlockVisitor,
) -> crate::error::Result<()> {
visitor.start_definition_list(list.label())?;
for v in list.inner() {
if let Some(inline_visitor) = visitor.inline_visitor() {
visitor.start_definition(v.term(), v.label())?;
visitor.start_definition_list_text()?;
walk_inline(v.text().inner(), inline_visitor)?;
visitor.end_definition_list_text()?;
visitor.end_definition(v.term(), v.label())?;
}
}
visitor.end_definition_list(list.label())
}
fn walk_table(table: &Table, visitor: &dyn TableVisitor) -> crate::error::Result<()> {
visitor.start_table(table.caption(), table.label())?;
if table.has_columns() {
visitor.start_table_header_row()?;
for (i, col) in table.columns().iter().enumerate() {
visitor.table_header_cell(col, i)?;
}
visitor.end_table_header_row()?;
}
for (i, row) in table.rows().iter().enumerate() {
visitor.start_table_row(i)?;
for (j, cell) in row.cells().iter().enumerate() {
visitor.start_table_cell(j, cell.label())?;
if let Some(inline_visitor) = visitor.inline_visitor() {
walk_inline(cell.inner(), inline_visitor)?;
}
visitor.end_table_cell(j, cell.label())?;
}
visitor.end_table_row(i)?;
}
visitor.end_table(table.caption(), table.label())
}
fn walk_inline(inline: &[InlineContent], visitor: &dyn InlineVisitor) -> crate::error::Result<()> {
for inline in inline {
match inline {
InlineContent::HyperLink(v) => visitor.link(v)?,
InlineContent::Image(v) => visitor.image(v)?,
InlineContent::Text(v) => visitor.text(v)?,
InlineContent::Math(v) => visitor.math(v)?,
InlineContent::Character(v) => visitor.character(v)?,
InlineContent::LineBreak => visitor.line_break()?,
InlineContent::Span(v) => {
visitor.start_span(v.styles())?;
walk_inline(v.inner(), visitor)?;
visitor.end_span(v.styles())?;
}
}
}
Ok(())
}