1pub mod assemble;
5pub mod compile;
6pub mod latex;
7pub mod walk;
8
9use crate::zimignore::ZimIgnore;
10use std::error::Error;
11use std::fs;
12use std::path::{Path, PathBuf};
13
14pub struct GenerateOptions {
16 pub root: PathBuf,
17 pub output: PathBuf,
18 pub title: String,
19 pub author: Option<String>,
20 pub keep_tex: bool,
21}
22
23pub struct GenerateStats {
25 pub num_files: usize,
26 pub num_dirs: usize,
27 pub output: PathBuf,
28 pub kept_tex: Option<PathBuf>,
29}
30
31pub fn generate(opts: &GenerateOptions) -> Result<GenerateStats, Box<dyn Error>> {
33 let zimignore = ZimIgnore::load_for_directory(&opts.root);
34 let tree = walk::walk(&opts.root, &zimignore)?;
35 if tree.is_empty() {
36 return Err(format!("no .md files found under {}", opts.root.display()).into());
37 }
38
39 let num_dirs = tree.len();
40 let num_files: usize = tree.iter().map(|d| d.file_count()).sum();
41
42 let latex_source = assemble::build_latex(
43 &tree,
44 &assemble::PdfOptions {
45 title: opts.title.clone(),
46 author: opts.author.clone(),
47 },
48 )?;
49
50 let work_dir = std::env::temp_dir().join(format!("zim-pdf-{}", std::process::id()));
51 let pdf_path = compile::compile(&latex_source, &work_dir)?;
52
53 if let Some(parent) = opts.output.parent()
54 && !parent.as_os_str().is_empty()
55 {
56 fs::create_dir_all(parent)?;
57 }
58 fs::copy(&pdf_path, &opts.output)?;
59
60 let kept_tex = if opts.keep_tex {
61 let target = sibling(&opts.output, "tex");
62 fs::copy(compile::tex_path(&work_dir), &target)?;
63 Some(target)
64 } else {
65 None
66 };
67
68 let _ = fs::remove_dir_all(&work_dir);
69
70 Ok(GenerateStats {
71 num_files,
72 num_dirs,
73 output: opts.output.clone(),
74 kept_tex,
75 })
76}
77
78fn sibling(p: &Path, ext: &str) -> PathBuf {
79 let mut s = p.to_path_buf();
80 s.set_extension(ext);
81 s
82}