use anyhow::{Context, Result};
use elementary_row_operation_verifier::cli::{Cli, Command};
use elementary_row_operation_verifier::parser::parse_file;
use elementary_row_operation_verifier::tui::TuiApp;
use elementary_row_operation_verifier::tui::check::CheckTui;
use elementary_row_operation_verifier::verifier::{VerificationResult, Verifier};
use std::fs;
use std::path::PathBuf;
fn main() -> Result<()> {
let cli = Cli::parse();
match &cli.command {
Some(Command::Check { path }) => {
run_check(path, &cli)?;
}
None => {
let file = cli
.file_path
.as_ref()
.context("No file specified. Usage: elementary-row-operation-verifier <FILE>")?;
run_single(file, &cli)?;
}
}
Ok(())
}
fn run_single(file: &str, cli: &Cli) -> Result<()> {
let path = PathBuf::from(file);
let _content = fs::read_to_string(&path)
.with_context(|| format!("Failed to read file: {}", path.display()))?;
let parsed = parse_file(&path)?;
if cli.verbose {
println!("File: {}", path.display());
println!("Steps found: {}", parsed.steps.len());
println!();
}
let verifier = Verifier::new();
let results = verifier.verify_file(&parsed);
if !cli.plain {
let mut app = TuiApp::new(path, parsed, results);
app.run()?;
} else {
print_results(&results, cli.verbose);
}
Ok(())
}
fn run_check(dir: &str, cli: &Cli) -> Result<()> {
let dir_path = PathBuf::from(dir);
if !dir_path.is_dir() {
anyhow::bail!("'{}' is not a directory", dir_path.display());
}
if !cli.plain {
let mut app = CheckTui::new(dir_path)?;
app.run()?;
} else {
run_check_plain(dir, cli)?;
}
Ok(())
}
fn run_check_plain(dir: &str, cli: &Cli) -> Result<()> {
let dir_path = PathBuf::from(dir);
if !dir_path.is_dir() {
anyhow::bail!("'{}' is not a directory", dir_path.display());
}
let mut lore_files: Vec<PathBuf> = Vec::new();
for entry in fs::read_dir(&dir_path)
.with_context(|| format!("Failed to read directory: {}", dir_path.display()))?
{
let entry = entry?;
let path = entry.path();
if path.extension().map_or(false, |ext| ext == "lore") {
lore_files.push(path);
}
}
lore_files.sort();
if lore_files.is_empty() {
println!("No .lore files found in '{}'", dir_path.display());
return Ok(());
}
println!(
"Checking {} .lore file(s) in '{}'",
lore_files.len(),
dir_path.display()
);
if cli.verbose {
println!();
}
let verifier = Verifier::new();
let mut total_correct: u32 = 0;
let mut total_incorrect: u32 = 0;
let mut total_errors: u32 = 0;
let mut file_results: Vec<(&PathBuf, Vec<VerificationResult>)> = Vec::new();
for file_path in &lore_files {
let parsed = match parse_file(file_path) {
Ok(p) => p,
Err(e) => {
eprintln!(" ⚠ {}: parse error - {}", file_path.display(), e);
total_errors += 1;
continue;
}
};
let results = verifier.verify_file(&parsed);
let (correct, incorrect, errors) = count_results(&results);
total_correct += correct;
total_incorrect += incorrect;
total_errors += errors;
if cli.verbose {
let file_name = file_path.file_name().unwrap_or_default().to_string_lossy();
println!(
" {}: {} correct, {} incorrect, {} errors",
file_name, correct, incorrect, errors
);
}
file_results.push((file_path, results));
}
println!();
println!("──────────────────────────────");
println!(
" Total: {} correct, {} incorrect, {} errors",
total_correct, total_incorrect, total_errors
);
if total_incorrect > 0 && !cli.plain && cli.verbose {
println!();
for (path, results) in &file_results {
let has_issue = results
.iter()
.any(|r| !matches!(r, VerificationResult::Correct { .. }));
if has_issue {
println!(
"── {} ──",
path.file_name().unwrap_or_default().to_string_lossy()
);
print_results(results, true);
}
}
} else if total_incorrect > 0 && !cli.plain {
println!(" Run with -v to see details, or check individual files with TUI.");
}
Ok(())
}
fn count_results(results: &[VerificationResult]) -> (u32, u32, u32) {
let mut correct = 0;
let mut incorrect = 0;
let mut errors = 0;
for r in results {
match r {
VerificationResult::Correct { .. } => correct += 1,
VerificationResult::Incorrect { .. } => incorrect += 1,
VerificationResult::Error { .. } => errors += 1,
}
}
(correct, incorrect, errors)
}
fn print_results(results: &[VerificationResult], verbose: bool) {
for result in results {
match result {
VerificationResult::Correct { line, operation } => {
if verbose {
println!(" ✓ Line {}: {}", line, operation);
}
}
VerificationResult::Incorrect {
line,
operation,
expected,
got,
} => {
println!(" ✗ Line {}: {}", line, operation);
println!(" Expected:\n{}", expected);
println!(" Got:\n{}", got);
}
VerificationResult::Error {
line,
operation,
message,
} => {
println!(" ⚠ Line {}: {} - {}", line, operation, message);
}
}
}
}