1
use anyhow::{anyhow, bail, Result};
2
use versions::Versioning;
3

            
4
const CONJURE_MIN_VERSION: &str = "2.5.1";
5
const CORRECT_FIRST_LINE: &str = "Conjure: The Automated Constraint Modelling Tool";
6

            
7
/// Checks if the conjure executable is present in PATH and if it is the correct version.
8
/// Returns () on success and an error on failure.
9
5
pub fn conjure_executable() -> Result<()> {
10
5
    let mut cmd = std::process::Command::new("conjure");
11
5
    let output = cmd.arg("--version").output()?;
12
5
    let stdout = String::from_utf8(output.stdout)?;
13
5
    let stderr = String::from_utf8(output.stderr)?;
14

            
15
5
    if !stderr.is_empty() {
16
        bail!("'conjure' results in error: ".to_string() + &stderr);
17
5
    }
18
5
    let first = stdout
19
5
        .lines()
20
5
        .next()
21
5
        .ok_or(anyhow!("Could not read stdout"))?;
22
5
    if first != CORRECT_FIRST_LINE {
23
        let path = std::env::var("PATH")?;
24
        let paths = std::env::split_paths(&path);
25
        let num_conjures = paths.filter(|path| path.join("conjure").exists()).count();
26
        if num_conjures > 1 {
27
            bail!(
28
                "Conjure may be present in PATH after a conflicting name. \
29
            Make sure to prepend the correct path to Conjure to PATH."
30
            )
31
        } else {
32
            bail!("The correct Conjure executable is not present in PATH.")
33
        }
34
5
    }
35
5
    let version_line = stdout
36
5
        .lines()
37
5
        .nth(1)
38
5
        .ok_or(anyhow!("Could not read Conjure's stdout"))?;
39

            
40
5
    let version = match version_line.strip_prefix("Release version ") {
41
5
        Some(v) => Ok(v),
42
        None => match version_line.strip_prefix("Conjure v") {
43
            // New format: Conjure v2.5.1 (Repository version ...)
44
            Some(v) => v.split_whitespace().next().ok_or(anyhow!(
45
                "Could not read Conjure's version from: {}",
46
                version_line
47
            )),
48
            None => Err(anyhow!(
49
                "Could not read Conjure's version from: {}",
50
                version_line
51
            )),
52
        },
53
    }?;
54

            
55
5
    if Versioning::new(version) < Versioning::new(CONJURE_MIN_VERSION) {
56
        bail!(
57
            "Conjure version is too old (< {}): {}",
58
            CONJURE_MIN_VERSION,
59
            version
60
        );
61
5
    }
62
5
    Ok(())
63
5
}