elementary-row-operation-verifier 0.0.1

A tool to verify the correctness of elementary row operations on matrices
Documentation
//! 文件解析入口

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)
}