use crate::markdown::line::{Line, LineType};
use crate::markdown::inline::{render_inline, render_inline_raw};
use crate::consts::*;
use crate::utils::into_v16;
#[derive(Debug)]
enum TableAlignment {
Left, Center, Right
}
impl TableAlignment {
fn render(&self) -> Vec<u16> {
match self {
TableAlignment::Center => into_v16("<td class=\"align_center\">"),
TableAlignment::Right => into_v16("<td class=\"align_right\">"),
TableAlignment::Left => into_v16("<td class=\"align_left\">"),
}
}
}
pub fn merge_table(lines: &Vec<Line>) -> Vec<Line> {
let mut result = Vec::with_capacity(lines.len());
let mut is_inside_table = false;
let mut curr_table = vec![];
for (ind, ln) in lines.iter().enumerate() {
if is_inside_table {
if is_valid_table_row(ln) {
curr_table.push(ln.clone());
}
else {
result.push(construct_table(curr_table));
result.push(ln.clone());
curr_table = vec![];
is_inside_table = false;
}
}
else {
if ln.line_type == LineType::TableDelimiter {
if ind > 0 && is_valid_table_row(&lines[ind - 1]) && row_to_cells(&ln.content).len() == row_to_cells(&lines[ind - 1].content).len() {
curr_table.push(result.pop().unwrap());
curr_table.push(ln.clone());
is_inside_table = true;
}
else {
let mut ln_ = ln.clone();
ln_.line_type = LineType::Paragraph;
ln_ = render_inline(ln_);
result.push(ln_);
}
}
else {
result.push(ln.clone());
}
}
}
if is_inside_table {
result.push(construct_table(curr_table));
}
result
}
fn construct_table(lines: Vec<Line>) -> Line {
let table_head = row_to_cells(&lines[0].content);
let table_body = lines[2..].iter().map(|ln| row_to_cells(&ln.content)).collect::<Vec<Vec<Vec<u16>>>>();
let mut result = vec![];
let alignments = row_to_cells(&lines[1].content).iter().map(|cell| parse_column_alignment(cell)).collect::<Vec<TableAlignment>>();
result.push(into_v16("<table><thead>"));
for th in table_head.iter() {
result.push(into_v16("<th>"));
result.push(render_inline_raw(th));
result.push(into_v16("</th>"));
}
result.push(into_v16("</thead><tbody>"));
for tr in table_body.iter() {
result.push(into_v16("<tr>"));
for (ind, td) in tr.iter().enumerate() {
if ind == alignments.len() {
break;
}
result.push(alignments[ind].render());
result.push(render_inline_raw(td));
result.push(into_v16("</td>"));
}
result.push(into_v16("</tr>"));
}
result.push(into_v16("</tbody></table>"));
Line {
content: result.concat(),
indent: 0,
line_type: LineType::RenderedTable
}
}
fn row_to_cells(row: &Vec<u16>) -> Vec<Vec<u16>> {
let result = row.split(|c| *c == U16_VERTICAL_BAR).map(|c| c.to_vec()).collect::<Vec<Vec<u16>>>();
result[1..result.len() - 1].to_vec()
}
fn parse_column_alignment(content: &Vec<u16>) -> TableAlignment {
if content.len() < 2 {
TableAlignment::Left
}
else if content[0] == U16_COLON {
if content[content.len() - 1] == U16_COLON {
TableAlignment::Center
}
else {
TableAlignment::Left
}
}
else if content[content.len() - 1] == U16_COLON {
TableAlignment::Right
}
else {
TableAlignment::Left
}
}
fn is_valid_table_row(line: &Line) -> bool {
(line.line_type == LineType::Paragraph || line.line_type == LineType::TableDelimiter)
&& line.content.len() > 2
&& line.content[0] == U16_VERTICAL_BAR && line.content[line.content.len() - 1] == U16_VERTICAL_BAR
}