Files
pdfcraft/apps/pdfcraft-cli/tests/split.rs
T
2627081935 Issue #249: pdfcraft-cli split creates --out-dir when it does not exist (#256)
`split --out-dir DIR` wrote each part with `std::fs::write` and never
created DIR, so a folder that did not exist yet failed on the first part
with the part's path and a raw OS error ("The system cannot find the path
specified. (os error 3)") — easy to read as the split itself failing.

The folder is now created with `create_dir_all` once the parts have been
computed, before any is written; a folder that can't be created is
reported as `split: --out-dir DIR: <error>`, naming the folder rather
than the first part.

Tests (apps/pdfcraft-cli/tests/split.rs): a nested folder that does not
exist is created and holds every part listed on stdout; an `--out-dir`
that is an existing file fails with a message naming the option and
leaves the file as it was.

Co-authored-by: wstock <1394687+wstock@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-08 07:26:03 -04:00

77 lines
3.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! `split --out-dir DIR` creates DIR when it does not exist yet (#249), and says so when it can't.
use std::path::PathBuf;
use std::process::{Command, Output};
const BIN: &str = env!("CARGO_BIN_EXE_pdfcraft-cli");
/// A PDF with `n` empty 200×300 pt pages.
fn fixture(n: usize) -> Vec<u8> {
let mut objs: Vec<Vec<u8>> = vec![b"<< /Type /Catalog /Pages 2 0 R >>".to_vec()];
let kids: Vec<String> = (0..n).map(|i| format!("{} 0 R", 3 + i)).collect();
objs.push(format!("<< /Type /Pages /Kids [{}] /Count {n} /MediaBox [0 0 200 300] >>", kids.join(" ")).into_bytes());
for _ in 0..n {
objs.push(b"<< /Type /Page /Parent 2 0 R >>".to_vec());
}
let mut out = b"%PDF-1.7\n".to_vec();
let mut offsets = Vec::new();
for (i, o) in objs.iter().enumerate() {
offsets.push(out.len());
out.extend_from_slice(format!("{} 0 obj\n", i + 1).as_bytes());
out.extend_from_slice(o);
out.extend_from_slice(b"\nendobj\n");
}
let xref = out.len();
out.extend_from_slice(format!("xref\n0 {}\n0000000000 65535 f \n", objs.len() + 1).as_bytes());
for o in offsets {
out.extend_from_slice(format!("{o:010} 00000 n \n").as_bytes());
}
out.extend_from_slice(format!("trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n", objs.len() + 1).as_bytes());
out
}
/// A fresh directory holding `report.pdf` (3 pages).
fn workdir(test: &str) -> PathBuf {
let d = std::env::temp_dir().join(format!("pdfcraft-cli-split-{test}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&d);
std::fs::create_dir_all(&d).unwrap();
std::fs::write(d.join("report.pdf"), fixture(3)).unwrap();
d
}
fn split(dir: &std::path::Path, out_dir: &std::path::Path) -> Output {
Command::new(BIN)
.args(["split", dir.join("report.pdf").to_str().unwrap(), "--every", "1", "--out-dir", out_dir.to_str().unwrap()])
.output()
.unwrap()
}
#[test]
fn a_missing_out_dir_is_created() {
let dir = workdir("create");
let parts = dir.join("parts").join("nested");
let out = split(&dir, &parts);
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
let stdout = String::from_utf8_lossy(&out.stdout);
for n in 1..=3 {
let part = parts.join(format!("report-p{n}.pdf"));
assert!(part.is_file(), "{} missing; stdout: {stdout}", part.display());
assert!(stdout.contains(&format!("report-p{n}.pdf")), "{stdout}");
}
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn an_out_dir_that_cannot_be_created_is_reported_as_such() {
let dir = workdir("file");
let not_a_dir = dir.join("parts");
std::fs::write(&not_a_dir, b"in the way").unwrap();
let out = split(&dir, &not_a_dir);
assert!(!out.status.success());
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stderr.contains("--out-dir") && stderr.contains("parts"), "{stderr}");
assert!(!stderr.contains("report-p1.pdf"), "the folder is the problem, not the first part: {stderr}");
assert_eq!(std::fs::read(&not_a_dir).unwrap(), b"in the way");
let _ = std::fs::remove_dir_all(&dir);
}