use super::prelude::*;
use crate::tree::{Alignment, Table, TableCell, TableRow, TableType};
use std::mem;
use std::num::NonZeroU32;
#[derive(Debug)]
struct TableCellStart {
align: Option<Alignment>,
header: bool,
column_span: NonZeroU32,
}
pub const RULE_TABLE: Rule = Rule {
name: "table",
position: LineRequirement::StartOfLine,
try_consume_fn,
};
fn try_consume_fn<'r, 't>(
parser: &mut Parser<'r, 't>,
) -> ParseResult<'r, 't, Elements<'t>> {
debug!("Trying to parse simple table");
let mut rows = Vec::new();
let mut errors = Vec::new();
let mut _paragraph_break = false;
'table: loop {
debug!("Parsing next table row");
let mut cells = Vec::new();
macro_rules! build_row {
() => {
rows.push(TableRow {
cells: mem::take(&mut cells),
attributes: AttributeMap::new(),
})
};
}
macro_rules! finish_table {
() => {
if rows.is_empty() {
return Err(parser.make_err(ParseErrorKind::RuleFailed));
} else {
break 'table;
}
};
}
'row: loop {
debug!("Parsing next table cell");
let mut elements = Vec::new();
let TableCellStart {
align,
header,
column_span,
} = match parse_cell_start(parser)? {
Some(cell_start) => cell_start,
None => finish_table!(),
};
macro_rules! build_cell {
() => {
cells.push(TableCell {
elements: mem::take(&mut elements),
header,
column_span,
align,
attributes: AttributeMap::new(),
})
};
}
'cell: loop {
trace!("Parsing next element (length {})", elements.len());
match parser.next_two_tokens() {
(
Token::TableColumn
| Token::TableColumnTitle
| Token::TableColumnCenter
| Token::TableColumnRight,
Some(next),
) => {
trace!(
"Ending cell, row, or table (next token '{}')",
next.name(),
);
match next {
Token::ParagraphBreak | Token::InputEnd => {
build_cell!();
build_row!();
parser.step()?;
break 'table;
}
Token::LineBreak => {
build_cell!();
parser.step_n(2)?;
break 'row;
}
_ => break 'cell,
}
}
(Token::Whitespace, _) if elements.is_empty() => {
trace!("Ignoring leading whitespace");
parser.step()?;
continue 'cell;
}
(
Token::Whitespace,
Some(
Token::TableColumn
| Token::TableColumnTitle
| Token::TableColumnCenter
| Token::TableColumnRight,
),
) => {
trace!("Ignoring trailing whitespace");
parser.step()?;
continue 'cell;
}
(Token::LineBreak | Token::ParagraphBreak | Token::InputEnd, _) => {
trace!("Invalid termination tokens in table, ending");
finish_table!();
}
_ => {
trace!("Consuming cell contents as elements");
let new_elements =
consume(parser)?.chain(&mut errors, &mut _paragraph_break);
elements.extend(new_elements);
}
}
}
build_cell!();
}
build_row!();
}
let attributes = AttributeMap::new();
let table = Table {
rows,
attributes,
table_type: TableType::Simple,
};
ok!(false; Element::Table(table), errors)
}
fn parse_cell_start(parser: &mut Parser) -> Result<Option<TableCellStart>, ParseError> {
let mut span = 0;
macro_rules! increase_span {
() => {{
span += 1;
parser.step()?;
}};
}
let (align, header) = loop {
match parser.current().token {
Token::TableColumnTitle => {
increase_span!();
break (None, true);
}
Token::TableColumnCenter => {
increase_span!();
break (Some(Alignment::Center), false);
}
Token::TableColumnRight => {
increase_span!();
break (Some(Alignment::Right), false);
}
Token::TableColumn => increase_span!(),
_ if span > 0 => break (None, false),
_ => return Ok(None),
}
};
let column_span =
NonZeroU32::new(span).expect("Cell start exited without column span");
Ok(Some(TableCellStart {
align,
header,
column_span,
}))
}