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
use std::{
    error, fmt, fs, io,
    path::{Path, PathBuf},
    process::Command,
};

#[derive(Debug)]
pub enum Error {
    AlreadyDownloaded,
    MissingEnv,
    InvalidFlutterRoot(&'static str),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;

        f.write_str(self.description())
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::AlreadyDownloaded => "Flutter engine already downloaded",
            Error::MissingEnv => "Cannot find flutter engine version. flutter cli not in PATH. You may need to set either FLUTTER_ROOT or FLUTTER_ENGINE_VERSION",
            Error::InvalidFlutterRoot(_) => "Cannot read from FLUTTER_ROOT",
        }
    }
}

fn guess_sdk_path() -> Result<PathBuf, &'static str> {
    let output = if cfg!(target_os = "windows") {
        Command::new("cmd")
            .args(&["/C", "where.exe flutter"])
            .output()
    } else {
        Command::new("sh").arg("-c").arg("which flutter").output()
    }
    .map_err(|_| "cannot find flutter executable")?;
    let s = std::str::from_utf8(output.stdout.as_slice())
        .map_err(|_| "parse result of `which flutter`")?;
    let line = s.trim().lines().next().ok_or("output empty")?;
    let p = Path::new(line).canonicalize().map_err(|_| "follow link")?;
    let p = p
        .parent()
        .ok_or("parent of flutter")?
        .parent()
        .ok_or("parent of parent of flutter")?;
    Ok(p.to_owned())
}

fn read_ver_from_sdk(p: &Path) -> io::Result<String> {
    let p = p.join("bin").join("internal").join("engine.version");
    fs::read_to_string(p).map(|v| v.trim().to_owned())
}

pub fn get_flutter_version() -> Result<String, Error> {
    if let Ok(v) = std::env::var("FLUTTER_ENGINE_VERSION") {
        Ok(v)
    } else if let Ok(v) = std::env::var("FLUTTER_ROOT") {
        let p = Path::new(&v);
        read_ver_from_sdk(p)
            .map_err(|_| Error::InvalidFlutterRoot("read engine version from FLUTTER_ROOT failed"))
    } else {
        match guess_sdk_path() {
            Ok(p) => read_ver_from_sdk(&p).map_err(|_| {
                Error::InvalidFlutterRoot("read engine version from flutter executable failed")
            }),
            Err("cannot find flutter executable") => Err(Error::MissingEnv),
            Err(reason) => Err(Error::InvalidFlutterRoot(reason)),
        }
    }
}