use anyhow::{Context, Result};
use std::path::Path;
use std::time::Instant;
use crate::cli::args::ConvertBatchArgs;
use crate::cli::utils::output;
pub async fn convert_batch(args: ConvertBatchArgs) -> Result<()> {
use printwell::{ConversionJob, ConverterPool, Orientation, PageSize, PdfOptions};
let page_size = match args.page_size.to_uppercase().as_str() {
"A3" => PageSize::A3,
"A4" => PageSize::A4,
"A5" => PageSize::A5,
"LETTER" => PageSize::Letter,
"LEGAL" => PageSize::Legal,
"TABLOID" => PageSize::Tabloid,
_ => anyhow::bail!(
"Unknown page size: {}. Use A3, A4, A5, Letter, Legal, or Tabloid",
args.page_size
),
};
std::fs::create_dir_all(&args.output_dir)
.with_context(|| format!("Failed to create output directory: {}", args.output_dir))?;
output::note(format!(
"Starting batch conversion of {} files with {} workers...",
args.inputs.len(),
args.workers
));
let pool = ConverterPool::new(args.workers).context("Failed to create converter pool")?;
let start = Instant::now();
let mut jobs = Vec::new();
for input in &args.inputs {
let html = if input.starts_with("http://") || input.starts_with("https://") {
let resource_options = printwell::ResourceOptions::default();
let resp = printwell::fetch_url(input, &resource_options)
.map_err(|e| anyhow::anyhow!("Failed to fetch URL {input}: {e}"))?;
resp.into_bytes()
} else if input == "-" {
use std::io::Read;
let mut content = String::new();
std::io::stdin()
.read_to_string(&mut content)
.context("Failed to read from stdin")?;
content.into_bytes()
} else {
std::fs::read(input).with_context(|| format!("Failed to read input file: {input}"))?
};
let html_str = String::from_utf8_lossy(&html).to_string();
let pdf_options = PdfOptions::builder()
.page_size(page_size)
.orientation(if args.landscape {
Orientation::Landscape
} else {
Orientation::Portrait
})
.print_background(args.background)
.build();
jobs.push((
input.clone(),
ConversionJob::html(html_str).with_pdf_options(pdf_options),
));
}
let mut success = 0;
let mut failed = 0;
for (input, job) in jobs {
let output_name = if input.starts_with("http") {
input
.split('/')
.next_back()
.unwrap_or("output")
.replace(".html", ".pdf")
} else if input == "-" {
"stdin.pdf".to_string()
} else {
Path::new(&input).file_stem().map_or_else(
|| "output.pdf".to_string(),
|s| format!("{}.pdf", s.to_string_lossy()),
)
};
let output_path = Path::new(&args.output_dir).join(&output_name);
match pool.convert(job).await {
Ok(pdf) => {
if let Err(e) = pdf.write_to_file(&output_path) {
output::batch_fail(&input, format!("Failed to write: {e}"));
failed += 1;
} else {
output::batch_ok(&input, &output_path, pdf.page_count());
success += 1;
}
}
Err(e) => {
output::batch_fail(&input, e);
failed += 1;
}
}
}
let elapsed = start.elapsed();
output::batch_summary(success, failed, elapsed.as_secs_f64());
if failed > 0 {
anyhow::bail!("{failed} file(s) failed to convert");
}
Ok(())
}