use crate::constants::{HYPHEN, NEWLINE};
use crate::node_pool::NodeID;
use crate::parse::parse_object;
use crate::types::{Cursor, Expr, MarkupKind, MatchError, ParseOpts, Parseable, Parser, Result};
#[derive(Debug, Clone)]
pub struct Table {
pub rows: usize,
pub cols: usize,
pub children: Vec<NodeID>,
}
#[derive(Debug, Clone)]
pub struct TableCell(pub Vec<NodeID>);
#[derive(Debug, Clone)]
pub enum TableRow {
Rule, Standard(Vec<NodeID>),
}
impl<'a> Parseable<'a> for Table {
fn parse(
parser: &mut Parser<'a>,
mut cursor: Cursor<'a>,
parent: Option<NodeID>,
mut parse_opts: ParseOpts,
) -> Result<NodeID> {
let start = cursor.index;
parse_opts.markup.insert(MarkupKind::Table);
let reserve_id = parser.pool.reserve_id();
let mut children: Vec<NodeID> = Vec::new();
let mut rows = 0;
let mut cols = 0;
while let Ok(row_id) = TableRow::parse(parser, cursor, Some(reserve_id), parse_opts) {
let obj = &parser.pool[row_id];
children.push(row_id);
rows += 1;
if let Expr::TableRow(TableRow::Standard(node_ids)) = &obj.obj {
cols = cols.max(node_ids.len());
}
cursor.index = parser.pool[row_id].end;
}
Ok(parser.alloc_with_id(
Self {
rows,
cols,
children,
},
start,
cursor.index,
parent,
reserve_id,
))
}
}
impl<'a> Parseable<'a> for TableRow {
fn parse(
parser: &mut Parser<'a>,
mut cursor: Cursor<'a>,
parent: Option<NodeID>,
parse_opts: ParseOpts,
) -> Result<NodeID> {
let start = cursor.index;
cursor.is_index_valid()?;
cursor.skip_ws();
cursor.word("|")?;
if cursor.try_curr()? == HYPHEN {
cursor.adv_till_byte(b'\n');
return Ok(parser
.pool
.alloc(Self::Rule, start, cursor.index + 1, parent));
}
let mut children: Vec<NodeID> = Vec::new();
while let Ok(table_cell_id) = TableCell::parse(parser, cursor, parent, parse_opts) {
let node_item = &parser.pool[table_cell_id];
children.push(table_cell_id);
cursor.index = node_item.end;
if cursor.curr() == NEWLINE {
cursor.next();
break;
}
}
Ok(parser
.pool
.alloc(Self::Standard(children), start, cursor.index, parent))
}
}
impl<'a> Parseable<'a> for TableCell {
fn parse(
parser: &mut Parser<'a>,
mut cursor: Cursor<'a>,
parent: Option<NodeID>,
parse_opts: ParseOpts,
) -> Result<NodeID> {
let start = cursor.index;
let mut content_vec: Vec<NodeID> = Vec::new();
loop {
match parse_object(parser, cursor, parent, parse_opts) {
Ok(id) => {
cursor.index = parser.pool[id].end;
content_vec.push(id);
}
Err(MatchError::MarkupEnd(kind)) => {
if cursor.curr() != NEWLINE {
cursor.next();
}
break;
}
Err(_) => break,
}
}
let new_id = parser.pool.reserve_id();
for id in &mut content_vec {
parser.pool[*id].parent = Some(new_id);
}
if let Some(last_id) = content_vec.last() {
let last_item = &mut parser.pool[*last_id];
if let Expr::Plain(plains) = last_item.obj {
let repl_str = plains.trim_end();
if repl_str.trim_end().is_empty() {
content_vec.pop();
} else {
last_item.obj = Expr::Plain(repl_str);
}
}
}
Ok(parser
.pool
.alloc_with_id(Self(content_vec), start, cursor.index, parent, new_id))
}
}
#[cfg(test)]
mod tests {
use crate::parse_org;
#[test]
fn basic_table() {
let input = r"
|one|two|
|three|four|
";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
fn table_eof_1() {
let input = r"
|one|two|
|three|four|
";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
#[should_panic]
fn table_eof_2() {
let input = r"
|one|two|
|three|four|";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
fn table_no_nl() {
let input = r"
|one|two
|three|four
";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
fn table_with_hrule() {
let input = r"
|one|two
|--------|
|three|four
";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
fn table_markup_1() {
let input = r"
|one|tw *o* |
|three|four|
";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
fn table_empty_cells() {
let input = r"
||a|
|b||
";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
fn table_aligned_cells() {
let input = r"
|one two |three|
|s | |
";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
fn table_uneven_cols() {
let input = r"
|one two |three|||||
|s | |
";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
fn table_indented() {
let input = r"
word
|one two |three|
|s | |
|four | five|
word
";
let pool = parse_org(input);
pool.print_tree();
}
#[test]
fn table_indented_list() {
let input = r"
- one
- two
|one two |three|
|s | |
|four | five|
- three
";
let pool = parse_org(input);
pool.print_tree();
}
}