CLI: render-transition refuses --t values that would share one file name (#423)

This commit is contained in:
MarekSpodBiedry
2026-10-13 17:34:17 +02:00
parent 457406a7a5
commit eff88eaf59
+45 -10
View File
@@ -200,6 +200,7 @@ fn render_transition(args: &[String]) -> Result<(), String> {
if ts.is_empty() {
return Err("render-transition needs --t".into());
}
let paths = frame_paths(&out, &ts)?;
let mut s = Session::new();
open(&mut s, Some(&input), false)?;
let st = s.doc().map_err(|e| e.to_string())?;
@@ -217,27 +218,39 @@ fn render_transition(args: &[String]) -> Result<(), String> {
(prev.map(|p| render::render_slide(doc, p, &opts)), render::render_slide(doc, i, &opts), parts)
});
let aspect = sw / doc.slide_size.height.max(1.0);
for t in &ts {
for (k, t) in ts.iter().enumerate() {
let img = match &cached {
Some((old, new, parts)) => {
render::composite_transition(old.as_ref(), new, parts.as_ref(), &render::transition_frame(&tr.kind, &tr.option, *t, aspect))
}
None => render::render_transition(doc, i, *t, &opts),
};
let path = if ts.len() == 1 {
out.clone()
} else {
let p = std::path::Path::new(&out);
let stem = p.file_stem().and_then(|x| x.to_str()).unwrap_or("frame");
let name = format!("{stem}-t{t:.3}.png");
p.with_file_name(name).to_string_lossy().into_owned()
};
std::fs::write(&path, img.to_png()).map_err(|e| format!("{path}: {e}"))?;
let path = &paths[k];
std::fs::write(path, img.to_png()).map_err(|e| format!("{path}: {e}"))?;
outln!("{path} ({}×{}) {} {} t={t}", img.width, img.height, tr.kind, tr.option);
}
Ok(())
}
/// Output path of each frame. A single frame goes to OUT; several get `-t<value>` (three decimals)
/// before the extension. Values that round to the same name would overwrite each other, so that is an error.
fn frame_paths(out: &str, ts: &[f64]) -> Result<Vec<String>, String> {
if ts.len() == 1 {
return Ok(vec![out.to_string()]);
}
let p = std::path::Path::new(out);
let stem = p.file_stem().and_then(|x| x.to_str()).unwrap_or("frame");
let mut paths: Vec<String> = Vec::with_capacity(ts.len());
for t in ts {
let path = p.with_file_name(format!("{stem}-t{t:.3}.png")).to_string_lossy().into_owned();
if paths.contains(&path) {
return Err(format!("--t values round to the same three decimals: more than one frame would be written to {path}"));
}
paths.push(path);
}
Ok(paths)
}
fn convert(args: &[String]) -> Result<(), String> {
let (Some(input), Some(out)) = (args.first(), args.get(1)) else { return Err("convert needs IN and OUT".into()) };
let mut s = Session::new();
@@ -355,3 +368,25 @@ fn mcp(args: &[String]) -> Result<(), String> {
let stdin = std::io::stdin();
server.serve(stdin.lock(), std::io::stdout()).map_err(|e| e.to_string())
}
#[cfg(test)]
mod tests {
use super::frame_paths;
#[test]
fn single_frame_uses_out_as_is() {
assert_eq!(frame_paths("a/b.png", &[0.5]), Ok(vec!["a/b.png".to_string()]));
}
#[test]
fn frames_get_distinct_names() {
let paths = frame_paths("f.png", &[0.0, 0.25, 1.0]).unwrap_or_default();
assert_eq!(paths, ["f-t0.000.png", "f-t0.250.png", "f-t1.000.png"]);
}
#[test]
fn values_rounding_to_one_name_are_rejected() {
assert!(frame_paths("f.png", &[0.1001, 0.1002]).is_err());
assert!(frame_paths("f.png", &[0.5, 0.5]).is_err());
}
}