use super::cell::{CellAlign, GridCell};
use super::hline::{clean_cell_content, clean_hline_args, extract_hline_range, HLine};
#[derive(Debug, Clone)]
pub struct GridRow {
pub cells: Vec<GridCell>,
pub hlines_before: Vec<HLine>,
}
impl GridRow {
pub fn new() -> Self {
GridRow {
cells: Vec::new(),
hlines_before: Vec::new(),
}
}
}
impl Default for GridRow {
fn default() -> Self {
Self::new()
}
}
pub struct TableGridParser {
col_coverage: Vec<usize>,
pub rows: Vec<GridRow>,
pub default_alignments: Vec<CellAlign>,
pending_hlines: Vec<HLine>,
}
impl TableGridParser {
pub fn new(alignments: Vec<CellAlign>) -> Self {
TableGridParser {
col_coverage: Vec::new(),
rows: Vec::new(),
default_alignments: alignments,
pending_hlines: Vec::new(),
}
}
pub fn add_hline(&mut self) {
self.pending_hlines.push(HLine::full());
}
pub fn add_partial_hline(&mut self, start: usize, end: usize) {
self.pending_hlines.push(HLine::partial(start, end));
}
pub fn process_row(&mut self, raw_cells: Vec<String>) {
let mut row = GridRow::new();
row.hlines_before.append(&mut self.pending_hlines);
let mut input_idx = 0;
let mut current_col = 0;
while input_idx < raw_cells.len() {
if current_col >= self.col_coverage.len() {
self.col_coverage.resize(current_col + 1, 0);
}
if self.col_coverage[current_col] > 0 {
let raw = &raw_cells[input_idx];
let cell = GridCell::parse(raw);
let span = cell.colspan;
for i in 0..span {
if current_col + i < self.col_coverage.len()
&& self.col_coverage[current_col + i] > 0
{
self.col_coverage[current_col + i] -= 1;
}
}
input_idx += 1;
current_col += span;
} else {
let raw = &raw_cells[input_idx];
let cell = GridCell::parse(raw);
let rows_to_cover = cell.rowspan.saturating_sub(1);
if current_col + cell.colspan > self.col_coverage.len() {
self.col_coverage.resize(current_col + cell.colspan, 0);
}
for i in 0..cell.colspan {
self.col_coverage[current_col + i] = rows_to_cover;
}
if raw != "\\" {
row.cells.push(cell.clone());
} else {
row.cells.push(GridCell::empty());
}
input_idx += 1;
current_col += cell.colspan;
}
}
if !row.cells.is_empty() || !row.hlines_before.is_empty() {
self.rows.push(row);
}
}
pub fn generate_typst(&self, col_count: usize) -> String {
use std::fmt::Write;
let mut output = String::new();
let col_tuple: Vec<&str> = vec!["auto"; col_count.max(1)];
let _ = writeln!(output, "#table(");
let _ = writeln!(output, " columns: ({}),", col_tuple.join(", "));
if !self.default_alignments.is_empty() {
let aligns: Vec<&str> = self
.default_alignments
.iter()
.map(|a| a.to_typst())
.collect();
let _ = writeln!(output, " align: ({}),", aligns.join(", "));
}
for row in &self.rows {
for hline in &row.hlines_before {
let _ = writeln!(output, " {},", hline.to_typst());
}
if !row.cells.is_empty() {
let cells_str: Vec<String> = row.cells.iter().map(|c| c.to_typst()).collect();
let _ = writeln!(output, " {},", cells_str.join(", "));
}
}
for hline in &self.pending_hlines {
let _ = writeln!(output, " {},", hline.to_typst());
}
output.push_str(")\n");
output
}
}
pub fn parse_with_grid_parser(content: &str, alignments: Vec<CellAlign>) -> String {
let col_count = alignments.len().max(1);
let mut parser = TableGridParser::new(alignments);
for row_str in content.split("|||ROW|||") {
let row_str = row_str.trim();
if row_str.is_empty() {
continue;
}
if row_str.contains("|||HLINE|||") {
let hline_info = extract_hline_range(row_str);
match hline_info {
Some((start, end)) => parser.add_partial_hline(start, end),
None => parser.add_hline(),
}
}
let clean_row = row_str.replace("|||HLINE|||", "");
let clean_row = clean_hline_args(&clean_row);
if clean_row.trim().is_empty() {
continue;
}
let raw_cells: Vec<String> = clean_row
.split("|||CELL|||")
.map(clean_cell_content)
.collect();
parser.process_row(raw_cells);
}
if parser.rows.is_empty() && content.contains("|||CELL|||") {
let clean_content = content.replace("|||HLINE|||", "");
let raw_cells: Vec<String> = clean_content
.split("|||CELL|||")
.map(clean_cell_content)
.collect();
parser.process_row(raw_cells);
}
parser.generate_typst(col_count)
}