use std::error::Error; use std::fs::File; use std::io::BufWriter; use std::process::ExitCode; mod animalia; mod definitions; fn do_write(path: &str, write_func: W) -> Result<(), Box> where W: FnOnce(BufWriter) -> Result<(), E>, { println!("Writing output to {path}..."); let tmp_name = format!(".{path}.tmp"); let file = File::create(&tmp_name).map_err(|e| format!("Error creating {tmp_name}: {e}"))?; let writer = BufWriter::new(file); write_func(writer).map_err(|e| format!("Error writing to {tmp_name}: {e}"))?; _ = std::fs::remove_file(path); // OK if this already exists std::fs::rename(&tmp_name, path) .map_err(|e| format!("Error renaming {tmp_name} => {path}: {e}"))?; Ok(()) } fn try_main() -> Result<(), Box> { let mut args = std::env::args_os().skip(1); let command = args.next(); let no_command = "No command specified. Commands available: - definitions - animalia"; let Some(command) = command else { return Err(no_command.into()); }; if command == "-h" || command == "--help" { return Err(no_command.into()); } let mut command_args = vec![]; for arg in args { let Some(arg) = arg.to_str() else { return Err(format!( "Bad UTF-8 in argument: {}", arg.to_string_lossy().escape_debug() ) .into()); }; command_args.push(arg.to_owned()); } match &command.to_string_lossy()[..] { "definitions" => definitions::main(command_args), "animalia" => animalia::main(command_args), x => Err(format!("Unrecognized command: {x}").into()), } } fn main() -> ExitCode { use std::time::Instant; let start_time = Instant::now(); if let Err(e) = try_main() { eprintln!("Error: {e}"); return ExitCode::FAILURE; } println!( "Time taken: {:?}", Instant::now().duration_since(start_time) ); ExitCode::SUCCESS }