pub mod common;
mod matrix_parser;
#[cfg(test)]
pub mod test;
pub mod types;
pub use types::{FileParseResult, OpKind, RawBlock, Step};
use crate::error::MatrixError;
use crate::filetype;
use crate::matrix;
use matrix_parser::parse_block_at;
use std::fs;
use std::path::Path;
pub fn parse_file(path: &Path) -> Result<FileParseResult, MatrixError> {
parse_content(&fs::read_to_string(path)?)
}
pub fn parse_content(content: &str) -> Result<FileParseResult, MatrixError> {
let lines: Vec<&str> = content.lines().collect();
let mut blocks = Vec::new();
let mut i = 0;
while i < lines.len() {
if lines[i].trim().is_empty() {
i += 1;
continue;
}
let (b, next) = parse_block_at(&lines, i)?;
blocks.push(b);
i = next;
}
if blocks.is_empty() {
return Err(MatrixError::InvalidFormat("No blocks".into()));
}
let first_empty = lines.iter().position(|l| l.trim().is_empty()).unwrap_or(0);
let (ori_blocks, step_blocks) = split_blocks(&lines, &blocks, first_empty);
let mut ori_rows = vec![];
for b in &ori_blocks {
ori_rows.extend(b.rows.clone());
}
let ori = matrix::from_rows(ori_rows).map_err(|e| MatrixError::InvalidFormat(e))?;
let op_kind = filetype::detect_kind(&lines[..first_empty.min(lines.len())]);
let mut steps = vec![];
for b in &step_blocks {
let m = matrix::from_rows(b.rows.clone()).map_err(|e| MatrixError::InvalidFormat(e))?;
let ops: Vec<(usize, String)> = b
.ops
.iter()
.enumerate()
.filter(|(_, s)| !s.is_empty() && s.as_str() != "." && s.as_str() != "_")
.map(|(idx, s)| (idx + 1, s.clone()))
.collect();
steps.push(Step {
kind: op_kind,
ops,
expected: m,
op_slices: b.op_slices.clone(),
col_widths: b.col_widths.clone(),
});
}
Ok(FileParseResult {
ori,
op_kind,
steps,
})
}
fn split_blocks(
lines: &[&str],
blocks: &[RawBlock],
first_empty: usize,
) -> (Vec<RawBlock>, Vec<RawBlock>) {
let mut ori = vec![];
let mut steps = vec![];
let mut bi = 0;
for li in 0..lines.len() {
if bi >= blocks.len() {
break;
}
if lines[li].trim().is_empty() {
continue;
}
if li == 0 || lines[li - 1].trim().is_empty() {
if li < first_empty {
ori.push(blocks[bi].clone());
} else {
steps.push(blocks[bi].clone());
}
bi += 1;
}
}
(ori, steps)
}