asciidoc-parser 0.18.0

Parser for AsciiDoc format
Documentation
use std::fmt;

use crate::{
    HasSpan,
    blocks::{ColumnStyle, HorizontalAlignment, IsBlock, VerticalAlignment},
    tests::fixtures::{Span, attributes::Attrlist, blocks::Block, content::Content},
};

#[derive(Eq, PartialEq)]
pub(crate) struct TableBlock {
    pub columns: &'static [TableColumn],
    pub header_row: Option<TableRow>,
    pub body_rows: &'static [TableRow],
    pub footer_row: Option<TableRow>,
    pub source: Span,
    pub title_source: Option<Span>,
    pub title: Option<&'static str>,
    pub caption: Option<&'static str>,
    pub number: Option<usize>,
    pub anchor: Option<Span>,
    pub anchor_reftext: Option<Span>,
    pub attrlist: Option<Attrlist>,
}

impl fmt::Debug for TableBlock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TableBlock")
            .field("columns", &self.columns)
            .field("header_row", &self.header_row)
            .field("body_rows", &self.body_rows)
            .field("footer_row", &self.footer_row)
            .field("source", &self.source)
            .field("title_source", &self.title_source)
            .field("title", &self.title)
            .field("caption", &self.caption)
            .field("number", &self.number)
            .field("anchor", &self.anchor)
            .field("anchor_reftext", &self.anchor_reftext)
            .field("attrlist", &self.attrlist)
            .finish()
    }
}

#[derive(Debug, Eq, PartialEq)]
pub(crate) struct TableColumn {
    pub width: usize,
    pub h_align: HorizontalAlignment,
    pub v_align: VerticalAlignment,
    pub style: ColumnStyle,
}

#[derive(Debug, Eq, PartialEq)]
pub(crate) struct TableRow {
    pub cells: &'static [TableCell],
}

#[derive(Debug, Eq, PartialEq)]
pub(crate) struct TableCell {
    pub content: TableCellContent,
}

#[derive(Debug, Eq, PartialEq)]
pub(crate) enum TableCellContent {
    Simple(Content),
    AsciiDoc(&'static [Block]),
}

impl<'src> PartialEq<crate::blocks::TableBlock<'src>> for TableBlock {
    fn eq(&self, other: &crate::blocks::TableBlock<'src>) -> bool {
        fixture_eq_observed(self, other)
    }
}

impl PartialEq<TableBlock> for crate::blocks::TableBlock<'_> {
    fn eq(&self, other: &TableBlock) -> bool {
        fixture_eq_observed(other, self)
    }
}

fn columns_eq(fixture: &[TableColumn], observed: &[crate::blocks::TableColumn]) -> bool {
    fixture.len() == observed.len()
        && fixture.iter().zip(observed.iter()).all(|(f, o)| {
            f.width == o.width()
                && f.h_align == o.h_align()
                && f.v_align == o.v_align()
                && f.style == o.style()
        })
}

fn cell_content_eq(fixture: &TableCellContent, observed: &crate::blocks::TableCellContent) -> bool {
    match (fixture, observed) {
        (TableCellContent::Simple(f), crate::blocks::TableCellContent::Simple(o)) => f == o,
        (TableCellContent::AsciiDoc(f), crate::blocks::TableCellContent::AsciiDoc(o)) => {
            let o = o.blocks();
            f.len() == o.len() && f.iter().zip(o.iter()).all(|(fb, ob)| fb == ob)
        }
        _ => false,
    }
}

fn cells_eq(fixture: &[TableCell], observed: &[crate::blocks::TableCell]) -> bool {
    fixture.len() == observed.len()
        && fixture
            .iter()
            .zip(observed.iter())
            .all(|(f, o)| cell_content_eq(&f.content, o.content()))
}

fn row_eq(fixture: &TableRow, observed: &crate::blocks::TableRow) -> bool {
    cells_eq(fixture.cells, observed.cells())
}

fn rows_eq(fixture: &[TableRow], observed: &[crate::blocks::TableRow]) -> bool {
    fixture.len() == observed.len()
        && fixture
            .iter()
            .zip(observed.iter())
            .all(|(f, o)| row_eq(f, o))
}

fn opt_row_eq(fixture: &Option<TableRow>, observed: Option<&crate::blocks::TableRow>) -> bool {
    match (fixture, observed) {
        (None, None) => true,
        (Some(f), Some(o)) => row_eq(f, o),
        _ => false,
    }
}

fn fixture_eq_observed(fixture: &TableBlock, observed: &crate::blocks::TableBlock) -> bool {
    if !columns_eq(fixture.columns, observed.columns()) {
        return false;
    }

    if !opt_row_eq(&fixture.header_row, observed.header_row()) {
        return false;
    }

    if !rows_eq(fixture.body_rows, observed.body_rows()) {
        return false;
    }

    if !opt_row_eq(&fixture.footer_row, observed.footer_row()) {
        return false;
    }

    if fixture.title_source.is_some() != observed.title_source().is_some() {
        return false;
    }

    if let Some(ref fixture_title_source) = fixture.title_source
        && let Some(ref observed_title_source) = observed.title_source()
        && fixture_title_source != observed_title_source
    {
        return false;
    }

    if fixture.title.is_some() != observed.title().is_some() {
        return false;
    }

    if let Some(ref fixture_title) = fixture.title
        && let Some(ref observed_title) = observed.title()
        && fixture_title != observed_title
    {
        return false;
    }

    if fixture.caption.is_some() != observed.caption().is_some() {
        return false;
    }

    if let Some(ref fixture_caption) = fixture.caption
        && let Some(ref observed_caption) = observed.caption()
        && fixture_caption != observed_caption
    {
        return false;
    }

    if fixture.number != observed.number() {
        return false;
    }

    if fixture.anchor.is_some() != observed.anchor().is_some() {
        return false;
    }

    if let Some(ref fixture_anchor) = fixture.anchor
        && let Some(ref observed_anchor) = observed.anchor()
        && fixture_anchor != observed_anchor
    {
        return false;
    }

    if fixture.anchor_reftext.is_some() != observed.anchor_reftext().is_some() {
        return false;
    }

    if let Some(ref fixture_anchor_reftext) = fixture.anchor_reftext
        && let Some(ref observed_anchor_reftext) = observed.anchor_reftext()
        && fixture_anchor_reftext != observed_anchor_reftext
    {
        return false;
    }

    if fixture.attrlist.is_some() != observed.attrlist().is_some() {
        return false;
    }

    if let Some(ref fixture_attrlist) = fixture.attrlist
        && let Some(ref observed_attrlist) = observed.attrlist()
        && &fixture_attrlist != observed_attrlist
    {
        return false;
    }

    fixture.source == observed.span()
}