Clean up CLI, add arguments and some validation

This commit is contained in:
2025-07-06 12:44:50 -07:00
parent 3ff40241ee
commit 218700616c
3 changed files with 89 additions and 15 deletions
+81 -15
View File
@@ -5,26 +5,68 @@ mod peg;
mod preprocess;
mod world;
use anyhow::anyhow;
use anyhow::{Result, anyhow};
use error::Context as ErrContext;
use labels::process_labels;
use lexopt::prelude::*;
use preprocess::eval::Context;
use std::{env, error::Error, fs, path::PathBuf, process::exit};
use std::{
env, fs,
path::{Path, PathBuf},
process::exit,
};
use world::World;
fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: {} WORLD_FILE", args[0]);
fn main() -> Result<()> {
let mut input_file = None;
let mut output_file = None;
let mut parser = lexopt::Parser::from_env();
let mut has_args = false;
while let Some(arg) = parser.next()? {
has_args = true;
match arg {
Short('o') | Long("output") => {
output_file = Some(parser.value()?.string()?);
}
Value(val) => {
if input_file.is_none() {
input_file = Some(val.string()?);
} else {
return Err(anyhow!("Unknown arg: {}", val.to_string_lossy()));
}
}
_ => return Err(arg.unexpected().into()),
}
}
if !has_args {
eprintln!("Usage: {} INPUT -o OUTPUT", env::args().next().unwrap());
exit(1);
}
let world_filename = &args[1];
let bytes = fs::read(world_filename)?;
let input_filename = input_file.ok_or_else(|| anyhow!("No input file specified"))?;
let output_filename = output_file.ok_or_else(|| anyhow!("No output file specified"))?;
// Prevent overwriting input file
let input_path = Path::new(&input_filename).canonicalize()?;
let output_path = Path::new(&output_filename);
let output_dir = output_path
.parent()
.filter(|x| !x.as_os_str().is_empty())
.unwrap_or_else(|| Path::new("."))
.canonicalize()?;
let output_path = output_dir.join(output_path.file_name().unwrap());
if input_path == output_path {
eprintln!("Error: Output file cannot be the same as input file");
exit(1);
}
let bytes = fs::read(&input_path)?;
let mut world = World::from_bytes(&bytes)?;
// Prepare to evaluate macros from the world file's directory
let world_pathbuf = PathBuf::from(&world_filename);
let world_pathbuf = PathBuf::from(&input_path);
let world_dir = world_pathbuf
.parent()
.ok_or(anyhow!("Couldn't get world's directory"))?;
@@ -39,7 +81,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// Resolve labels to proper ZZT-OOP
let base_ctx = ErrContext::new();
let ctx = base_ctx.with_file_path(&world_filename);
let ctx = base_ctx.with_file_path(&input_filename);
for (i, board) in world.boards.iter_mut().enumerate() {
let ctx = ctx.with_board(i);
*board = process_labels(&board, &ctx);
@@ -47,15 +89,39 @@ fn main() -> Result<(), Box<dyn Error>> {
// Print diagnostics
let messages = base_ctx.into_messages();
for (i, message) in messages.iter().enumerate() {
if i > 0 {
println!();
for message in messages.iter() {
println!("{}\n", message.rich_format(&world));
}
if !messages.is_empty() {
let mut warnings = 0;
let mut errors = 0;
for message in messages {
match message.level {
error::Level::Error => errors += 1,
error::Level::Warning => warnings += 1,
}
}
let plural = |x| if x == 1 { "" } else { "s" };
if errors == 0 {
println!(
"Compilation succeeded with {warnings} warning{}.",
plural(warnings)
)
} else {
println!(
"Compilation failed with {warnings} warning{} and {errors} error{}.",
plural(warnings),
plural(errors)
);
exit(1);
}
println!("{}", message.rich_format(&world));
}
// Try to write a modified world file
fs::write("tmp.zzt", world.to_bytes()?)?;
let world_bytes = world
.to_bytes()
.map_err(|e| anyhow!("Couldn't serialize world, {}", e))?;
fs::write(&output_path, world_bytes)?;
Ok(())
}