hmd 0.4.13

Custom Markdown Engine for my personal blog.
Documentation
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
}