1mod kpsewhich;
2mod miktex;
3mod texlive;
4
5use std::process::{Command, Stdio};
6
7use anyhow::Result;
8use derive_more::Display;
9use log::warn;
10
11pub use kpsewhich::Resolver;
12
13#[derive(Debug, PartialEq, Eq, Clone, Copy, Display)]
14pub enum DistributionKind {
15 #[display(fmt = "TeXLive")]
16 Texlive,
17 #[display(fmt = "MikTeX")]
18 Miktex,
19 #[display(fmt = "Tectonic")]
20 Tectonic,
21 #[display(fmt = "Unknown")]
22 Unknown,
23}
24
25#[derive(Debug, Clone)]
26pub struct Distribution {
27 pub kind: DistributionKind,
28 pub resolver: Resolver,
29}
30
31impl Distribution {
32 #[must_use]
33 pub fn detect() -> Self {
34 let kind = match Command::new("latex").arg("--version").output() {
35 Ok(output) => {
36 let stdout = String::from_utf8_lossy(&output.stdout);
37 if stdout.contains("TeX Live") {
38 DistributionKind::Texlive
39 } else if stdout.contains("MiKTeX") {
40 DistributionKind::Miktex
41 } else {
42 DistributionKind::Unknown
43 }
44 }
45 Err(_) => {
46 if Command::new("tectonic")
47 .arg("--version")
48 .stdout(Stdio::null())
49 .stderr(Stdio::null())
50 .status()
51 .is_ok()
52 {
53 DistributionKind::Tectonic
54 } else {
55 DistributionKind::Unknown
56 }
57 }
58 };
59
60 let resolver = match kind {
61 DistributionKind::Texlive => Self::load_resolver(texlive::load_resolver),
62 DistributionKind::Miktex => Self::load_resolver(miktex::load_resolver),
63 DistributionKind::Tectonic | DistributionKind::Unknown => Resolver::default(),
64 };
65 Self { kind, resolver }
66 }
67
68 fn load_resolver(loader: impl FnOnce() -> Result<Resolver>) -> Resolver {
69 match loader() {
70 Ok(resolver) => return resolver,
71 Err(why) => warn!("Failed to load resolver: {}", why),
72 };
73 Resolver::default()
74 }
75}