summaryrefslogtreecommitdiff
path: root/support/texlab/crates/tex/src/lib.rs
blob: 5075ec0efb415936c1bd95ec92610be1d465b453 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
mod compile;
mod language;
mod miktex;
mod tectonic;
mod texlive;

pub use self::compile::*;
pub use self::language::Language;

use self::miktex::Miktex;
use self::tectonic::Tectonic;
use self::texlive::Texlive;
use futures_boxed::boxed;
use tokio_net::process::Command;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum DistributionKind {
    Texlive,
    Miktex,
    Tectonic,
    Unknown,
}

impl DistributionKind {
    pub async fn detect() -> Self {
        if Command::new("tectonic")
            .arg("--version")
            .status()
            .await
            .is_ok()
        {
            return Self::Tectonic;
        }

        match Command::new("latex").arg("--version").output().await {
            Ok(output) => {
                let stdout = String::from_utf8_lossy(&output.stdout);
                if stdout.contains("TeX Live") {
                    Self::Texlive
                } else if stdout.contains("MiKTeX") {
                    Self::Miktex
                } else {
                    Self::Unknown
                }
            }
            Err(_) => Self::Unknown,
        }
    }
}

pub trait Distribution: Send + Sync {
    fn kind(&self) -> DistributionKind;

    fn supports_format(&self, format: Format) -> bool;

    fn output_kind(&self, format: Format) -> OutputKind {
        match format {
            Format::Latex => OutputKind::Dvi,
            Format::Pdflatex | Format::Xelatex | Format::Lualatex => OutputKind::Pdf,
        }
    }

    #[boxed]
    async fn compile<'a>(
        &'a self,
        params: CompileParams<'a>,
    ) -> Result<CompileResult, CompileError> {
        let executable = params.format.executable();
        let args = &["--interaction=batchmode", "-shell-escape", params.file_name];
        compile(executable, args, params).await
    }
}

impl dyn Distribution {
    pub async fn detect() -> Box<Self> {
        let kind = DistributionKind::detect().await;
        let distro: Box<Self> = match kind {
            DistributionKind::Texlive => Box::new(Texlive),
            DistributionKind::Miktex => Box::new(Miktex),
            DistributionKind::Tectonic => Box::new(Tectonic),
            DistributionKind::Unknown => Box::new(Unknown),
        };
        distro
    }
}

#[derive(Debug, Default)]
pub struct Unknown;

impl Distribution for Unknown {
    fn kind(&self) -> DistributionKind {
        DistributionKind::Unknown
    }

    fn supports_format(&self, _format: Format) -> bool {
        false
    }

    #[boxed]
    async fn compile<'a>(
        &'a self,
        _params: CompileParams<'a>,
    ) -> Result<CompileResult, CompileError> {
        Err(CompileError::NotInstalled)
    }
}