use crate::error::MatrixError;
use crate::parser::common::{ColDef, col_widths, parse_marker, slice_all};
use crate::parser::types::RawBlock;
pub fn parse_block(lines: &[&str]) -> Result<RawBlock, MatrixError> {
let defs = parse_marker(lines[0])?;
let has_op_col = defs.iter().any(|d| !d.is_num);
if has_op_col {
parse_row_block(lines, &defs)
} else {
parse_col_block(lines, &defs)
}
}
fn parse_row_block(lines: &[&str], defs: &[ColDef]) -> Result<RawBlock, MatrixError> {
let ncols = defs.iter().filter(|d| d.is_num).count();
let op_col_idx = defs.iter().position(|d| !d.is_num);
let mut data_rows = vec![];
let mut ops = vec![];
for line in &lines[1..] {
if line.trim().is_empty() {
continue;
}
let slices = slice_all(line, defs);
let nums: Vec<f64> = slices
.iter()
.take(ncols)
.filter_map(|s| s.parse::<f64>().ok())
.collect();
if nums.len() == ncols {
data_rows.push(nums);
ops.push(
slices
.get(op_col_idx.unwrap_or(0))
.cloned()
.unwrap_or_default(),
);
}
}
Ok(RawBlock {
rows: data_rows,
ops,
op_slices: vec![],
col_widths: col_widths(defs),
})
}
fn parse_col_block(lines: &[&str], defs: &[ColDef]) -> Result<RawBlock, MatrixError> {
let ncols = defs.iter().filter(|d| d.is_num).count();
let mut data_rows = vec![];
let mut op_rows: Vec<Vec<String>> = vec![];
for line in &lines[1..] {
if line.trim().is_empty() || line.trim() == "_" {
continue;
}
let slices = slice_all(line, defs);
let nums: Vec<f64> = slices
.iter()
.take(ncols)
.filter_map(|s| s.parse::<f64>().ok())
.collect();
if nums.len() == ncols && op_rows.is_empty() {
data_rows.push(nums);
} else {
op_rows.push(slices);
}
}
let mut ops = vec![String::new(); ncols];
for ci in 0..ncols {
let mut op = String::new();
for row in &op_rows {
let cell = row.get(ci).map(|s| s.as_str()).unwrap_or("");
if cell == "|" {
continue;
}
if !cell.is_empty() {
op = cell.to_string();
break;
}
}
ops[ci] = op;
}
Ok(RawBlock {
rows: data_rows,
ops,
op_slices: op_rows,
col_widths: col_widths(defs),
})
}
pub fn detect_kind(lines: &[&str]) -> crate::parser::OpKind {
use crate::parser::OpKind;
if lines.is_empty() {
return OpKind::Row;
}
let first = lines[0];
let has_op_col = first.chars().any(|c| c == '|' || c == '_');
let has_trail = lines.iter().any(|l| l.trim() == "_");
match (has_op_col, has_trail) {
(true, _) => OpKind::Row,
(false, true) => OpKind::Col,
_ => OpKind::Row,
}
}