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 "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 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 let out = run_command(
94 "pdflatex",
95 ["-interaction", "nonstopmode", "-halt-on-error", stem],
96 parent,
97 envs,
98 )?;
99 if !out.status.success() {
100 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 }),
137 Err(_e) => {
138 Err(())
140 }
141 }
142}