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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::env::{self, var};
use anyhow::{ensure, Context, Result};
use std::{
    env::{join_paths, split_paths},
    ffi::{OsStr, OsString},
    path::Path,
    process::{Command as StdCommand, Output, Stdio},
};

pub struct Command {
    envs: Vec<(OsString, OsString)>,
    command: StdCommand,
}

impl Command {
    pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
        Self {
            envs: vec![],
            command: StdCommand::new(program),
        }
    }

    pub fn args<I, S>(&mut self, args: I) -> &mut Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        self.command.args(args);
        self
    }

    pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
    where
        I: IntoIterator<Item = (K, V)>,
        K: AsRef<OsStr>,
        V: AsRef<OsStr>,
    {
        self.envs = vars
            .into_iter()
            .map(|(k, v)| (k.as_ref().to_os_string(), v.as_ref().to_os_string()))
            .collect();
        self.command.envs(self.envs.clone().into_iter());
        self
    }

    pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Self {
        self.command.env_remove(key);
        self
    }

    pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
        self.command.current_dir(dir);
        self
    }

    pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
        self.command.stdout(cfg);
        self
    }

    pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
        self.command.stderr(cfg);
        self
    }

    pub fn output(&mut self) -> Result<Output> {
        log::debug!("{:?}", self.envs);
        log::debug!("{:?}", self.command);

        let output = self
            .command
            .output()
            .with_context(|| format!("Could not get output of `{:?}`", self.command))?;

        ensure!(
            output.status.success(),
            "command failed: {:?}\nstdout: {:?}\nstderr: {:?}",
            self.command,
            std::str::from_utf8(&output.stdout).unwrap_or_default(),
            std::str::from_utf8(&output.stderr).unwrap_or_default()
        );

        Ok(output)
    }

    // smoelius: Why not get the status by calling `self.output()`? Because we don't want stdout and
    // stderr to be captured.
    pub fn success(&mut self) -> Result<()> {
        log::debug!("{:?}", self.envs);
        log::debug!("{:?}", self.command);

        let status = self
            .command
            .status()
            .with_context(|| format!("Could not get status of `{:?}`", self.command))?;

        ensure!(status.success(), "command failed: {:?}", self.command);

        Ok(())
    }
}

pub fn driver(toolchain: &str, driver: &Path) -> Result<Command> {
    let path = var(env::PATH)?;

    let path = {
        if cfg!(target_os = "windows") {
            // MinerSebas: To succesfully determine the dylint driver Version on Windows,
            // it is neccesary to add some Libraries to the Path.
            let rustup_home = var(env::RUSTUP_HOME)?;

            join_paths(
                std::iter::once(
                    Path::new(&rustup_home)
                        .join("toolchains")
                        .join(toolchain)
                        .join("bin"),
                )
                .chain(split_paths(&path)),
            )?
        } else {
            OsString::from(path)
        }
    };

    let mut command = Command::new(driver);
    command.envs(vec![(env::PATH, path)]);
    Ok(command)
}