conjure_oxide/
find_conjure.rs1use anyhow::{anyhow, bail, Result};
2use versions::Versioning;
3
4const CONJURE_MIN_VERSION: &str = "2.5.1";
5const CORRECT_FIRST_LINE: &str = "Conjure: The Automated Constraint Modelling Tool";
6
7pub fn conjure_executable() -> Result<()> {
10 let mut cmd = std::process::Command::new("conjure");
11 let output = cmd.arg("--version").output()?;
12 let stdout = String::from_utf8(output.stdout)?;
13 let stderr = String::from_utf8(output.stderr)?;
14
15 if !stderr.is_empty() {
16 bail!("'conjure' results in error: ".to_string() + &stderr);
17 }
18 let first = stdout
19 .lines()
20 .next()
21 .ok_or(anyhow!("Could not read stdout"))?;
22 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 }
35 let version_line = stdout
36 .lines()
37 .nth(1)
38 .ok_or(anyhow!("Could not read Conjure's stdout"))?;
39
40 let version = match version_line.strip_prefix("Release version ") {
41 Some(v) => Ok(v),
42 None => match version_line.strip_prefix("Conjure v") {
43 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 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 }
62 Ok(())
63}