flams_stex/
latex.rs

1use std::{ffi::OsStr, path::Path};
2
3pub fn clean(path: &Path) {
4    const EXTENSIONS: &[&str] = &[
5        "aux",
6        "log",
7        "bbl",
8        "toc",
9        "upa",
10        "upb",
11        "blg",
12        "out",
13        "idx",
14        "ilg",
15        "ind",
16        "mw",
17        "nav",
18        "snm",
19        "vrb",
20        "sms",
21        "sms2",
22        "hd",
23        "glo",
24        "bcf",
25        "blg",
26        "fdb_latexmk",
27        "fls",
28        //"sref",
29        "run.xml",
30        "synctex.gz",
31    ];
32    for ext in EXTENSIONS {
33        let p = path.with_extension(ext);
34        if p.exists() {
35            let _ = std::fs::remove_file(p);
36        }
37    }
38    // remove "x-blx.bib"
39    let Some(stem) = path
40        .file_stem()
41        .and_then(|s| s.to_str())
42        .map(|s| s.to_string() + "-blx.bib")
43    else {
44        return;
45    };
46    let p = path.with_file_name(stem);
47    if p.exists() {
48        let _ = std::fs::remove_file(p);
49    }
50}
51
52pub fn pdflatex_and_bib<S: AsRef<std::ffi::OsStr>, I: IntoIterator<Item = (S, S)>>(
53    path: &Path,
54    envs: I,
55) -> Result<(), ()> {
56    pdflatex(path, envs)?;
57    let Some(stem) = path.file_stem().and_then(|s| s.to_str()) else {
58        return Err(());
59    };
60    let Some(parent) = path.parent() else {
61        return Err(());
62    };
63    let bib = path.with_extension("bcf");
64    if bib.exists() {
65        let _ = run_command(
66            "biber",
67            std::iter::once(stem),
68            parent,
69            std::iter::empty::<(String, String)>(),
70        );
71    } else {
72        let _ = run_command(
73            "bibtex",
74            std::iter::once(stem),
75            parent,
76            std::iter::empty::<(String, String)>(),
77        );
78    }
79    Ok(())
80}
81
82pub fn pdflatex<S: AsRef<std::ffi::OsStr>, I: IntoIterator<Item = (S, S)>>(
83    path: &Path,
84    envs: I,
85) -> Result<(), ()> {
86    let Some(parent) = path.parent() else {
87        return Err(());
88    };
89    let Some(stem) = path.file_stem().and_then(|s| s.to_str()) else {
90        return Err(());
91    };
92    //tracing::info!("Running: pdflatex {stem} in {}", parent.display());
93    let out = run_command(
94        "pdflatex",
95        ["-interaction", "nonstopmode", "-halt-on-error", stem],
96        parent,
97        envs,
98    )?;
99    if !out.status.success() {
100        //let out = String::from_utf8_lossy(out.stdout.as_slice());
101        //let err = out
102        //    .find("Fatal error")
103        //    .map_or("unknown error",|i| &out[i..]);
104        //tracing::error!("pdflatex failed: {} ({})", err,path.display());
105        return Err(());
106    }
107    Ok(())
108}
109
110fn run_command<
111    A: AsRef<OsStr>,
112    S: AsRef<OsStr>,
113    Env: IntoIterator<Item = (S, S)>,
114    Args: IntoIterator<Item = A>,
115>(
116    cmd: &str,
117    args: Args,
118    in_path: &Path,
119    with_envs: Env,
120) -> Result<std::process::Output, ()> {
121    let mut proc = std::process::Command::new(cmd);
122    let mut process = proc
123        .args(args)
124        .current_dir(in_path)
125        .env("FLAMS_ADMIN_PWD", "NOPE");
126    for (k, v) in with_envs {
127        process = process.env(k, v);
128    }
129    match process
130        .stdout(std::process::Stdio::piped())
131        .stderr(std::process::Stdio::piped())
132        .spawn()
133    {
134        Ok(c) => c.wait_with_output().map_err(|_e| {
135            //tracing::error!("Error executing command {cmd}: {e}");
136        }),
137        Err(_e) => {
138            //tracing::error!("Error executing command {cmd}: {e}");
139            Err(())
140        }
141    }
142}