summaryrefslogtreecommitdiff
path: root/support/texlab/src/distro/kpsewhich.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/distro/kpsewhich.rs')
-rw-r--r--support/texlab/src/distro/kpsewhich.rs74
1 files changed, 74 insertions, 0 deletions
diff --git a/support/texlab/src/distro/kpsewhich.rs b/support/texlab/src/distro/kpsewhich.rs
new file mode 100644
index 0000000000..2dcc98118f
--- /dev/null
+++ b/support/texlab/src/distro/kpsewhich.rs
@@ -0,0 +1,74 @@
+use std::{
+ env,
+ ffi::OsStr,
+ fs,
+ path::{Path, PathBuf},
+ process::Command,
+};
+
+use anyhow::Result;
+use rustc_hash::FxHashMap;
+use smol_str::SmolStr;
+
+use crate::DocumentLanguage;
+
+#[derive(Debug, PartialEq, Eq, Clone, Default)]
+pub struct Resolver {
+ pub files_by_name: FxHashMap<SmolStr, PathBuf>,
+}
+
+impl Resolver {
+ pub fn new(files_by_name: FxHashMap<SmolStr, PathBuf>) -> Self {
+ Self { files_by_name }
+ }
+}
+
+pub fn parse_database(
+ root_directories: &[PathBuf],
+ mut reader: impl FnMut(&Path) -> Result<Vec<PathBuf>>,
+) -> Result<Resolver> {
+ let mut files_by_name = FxHashMap::default();
+ for directory in root_directories {
+ for path in reader(directory)? {
+ if DocumentLanguage::by_path(&path).is_some() {
+ if let Some(path) = make_absolute(root_directories, &path) {
+ if let Some(name) = path.file_name().and_then(OsStr::to_str).map(Into::into) {
+ files_by_name.insert(name, path);
+ }
+ }
+ }
+ }
+ }
+ Ok(Resolver::new(files_by_name))
+}
+
+fn make_absolute(root_directories: &[PathBuf], relative_path: &Path) -> Option<PathBuf> {
+ for dir in root_directories.iter().rev() {
+ if let Ok(path) = fs::canonicalize(dir.join(&relative_path)) {
+ return Some(path);
+ }
+ }
+ None
+}
+
+pub fn root_directories() -> Result<Vec<PathBuf>> {
+ let texmf = run(&["-var-value", "TEXMF"])?;
+ let expand_arg = format!("--expand-braces={}", texmf);
+ let expanded = run(&[&expand_arg])?;
+ let directories = env::split_paths(&expanded.replace("!", ""))
+ .filter(|path| path.exists())
+ .collect();
+ Ok(directories)
+}
+
+fn run(args: impl IntoIterator<Item = impl AsRef<OsStr>>) -> Result<String> {
+ let output = Command::new("kpsewhich").args(args).output()?;
+
+ let result = String::from_utf8(output.stdout)?
+ .lines()
+ .next()
+ .unwrap()
+ .into();
+
+ Ok(result)
+}