use std::{env, path::PathBuf, process::ExitCode};
use rassa_check::{
DEFAULT_SCRIPT, ImageFormat, render_report_to_image_bytes, render_script,
render_script_file_to_image,
};
#[derive(Debug)]
struct Args {
input: Option<PathBuf>,
output: PathBuf,
format: Option<ImageFormat>,
time_ms: i64,
width: i32,
height: i32,
}
fn main() -> ExitCode {
match run() {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("rassa-check: {error}");
eprintln!(
"usage: rassa-check [--input file.ass] [--output out.png|out.jpg|out.pgm] [--format png|jpg|pgm] [--time-ms 500] [--width 640] [--height 360]"
);
ExitCode::FAILURE
}
}
}
fn run() -> Result<(), String> {
let args = parse_args(env::args().skip(1))?;
let format = args
.format
.or_else(|| ImageFormat::from_path(&args.output))
.ok_or_else(|| format!("cannot infer output format from {}", args.output.display()))?;
let report = if let Some(input) = &args.input {
render_script_file_to_image(
input,
&args.output,
args.time_ms,
args.width,
args.height,
format,
)
.map_err(|error| error.to_string())?
} else {
let report = render_script(DEFAULT_SCRIPT, args.time_ms, args.width, args.height)
.map_err(|error| error.to_string())?;
let image =
render_report_to_image_bytes(&report, format).map_err(|error| error.to_string())?;
std::fs::write(&args.output, image)
.map_err(|error| format!("failed to write {}: {error}", args.output.display()))?;
report
};
println!(
"render ok: format={:?} planes={} lit_pixels={} bounds={:?} output={}",
format,
report.plane_count,
report.lit_pixels,
report.bounds,
args.output.display()
);
Ok(())
}
fn parse_args(args: impl IntoIterator<Item = String>) -> Result<Args, String> {
let mut parsed = Args {
input: None,
output: PathBuf::from("rassa-check.png"),
format: None,
time_ms: 500,
width: 640,
height: 360,
};
let mut args = args.into_iter();
while let Some(arg) = args.next() {
match arg.as_str() {
"--input" | "-i" => parsed.input = Some(next_path(&mut args, &arg)?),
"--output" | "-o" => parsed.output = next_path(&mut args, &arg)?,
"--format" | "-f" => {
let value = next_value(&mut args, &arg)?;
parsed.format = Some(
ImageFormat::parse(&value)
.ok_or_else(|| format!("unsupported --format: {value}"))?,
);
}
"--time-ms" | "-t" => {
parsed.time_ms = next_value(&mut args, &arg)?
.parse()
.map_err(|_| "invalid --time-ms".to_string())?
}
"--width" | "-w" => {
parsed.width = next_value(&mut args, &arg)?
.parse()
.map_err(|_| "invalid --width".to_string())?
}
"--height" | "-h" => {
parsed.height = next_value(&mut args, &arg)?
.parse()
.map_err(|_| "invalid --height".to_string())?
}
"--help" => return Err("help requested".to_string()),
value if !value.starts_with('-') && parsed.input.is_none() => {
parsed.input = Some(PathBuf::from(value));
}
value if !value.starts_with('-') => {
parsed.output = PathBuf::from(value);
}
_ => return Err(format!("unknown argument: {arg}")),
}
}
Ok(parsed)
}
fn next_value(args: &mut impl Iterator<Item = String>, flag: &str) -> Result<String, String> {
args.next()
.ok_or_else(|| format!("missing value after {flag}"))
}
fn next_path(args: &mut impl Iterator<Item = String>, flag: &str) -> Result<PathBuf, String> {
Ok(PathBuf::from(next_value(args, flag)?))
}