polyhorn_cli/core/tasks/
install_dependencies.rs

1use ansi_term::Color;
2use dialoguer::Confirm;
3use std::io::Error;
4use std::process::{Command, Stdio};
5
6use crate::core::{Manager, Task};
7
8/// Check that will be used to determine if a dependency is installed.
9pub struct DependencyCheck {
10    /// Command that will be run to check if a dependency is installed.
11    pub command: Vec<String>,
12}
13
14/// Dependency that must be installed.
15pub struct Dependency {
16    /// Name of this dependency that will be shown to the user.
17    pub name: String,
18
19    /// Check that will be used to determine if this dependency is installed.
20    pub check: DependencyCheck,
21
22    /// Command that will be executed to install this dependency if necessary.
23    pub install_command: Vec<String>,
24}
25
26impl Dependency {
27    /// Returns a new dependency on a CLI with the given commands to check its
28    /// existence and install it if necessary, respectively.
29    pub fn cli(name: &str, check_command: &[&str], install_command: &[&str]) -> Dependency {
30        Dependency {
31            name: name.to_owned(),
32            check: DependencyCheck {
33                command: check_command.iter().map(|&arg| arg.to_owned()).collect(),
34            },
35            install_command: install_command.iter().map(|&arg| arg.to_owned()).collect(),
36        }
37    }
38}
39
40/// This task checks if all dependencies exist and if necessary, installs the
41/// dependencies that weren't found.
42pub struct InstallDependencies {
43    /// Dependencies that need to be installed.
44    pub dependencies: Vec<Dependency>,
45}
46
47impl InstallDependencies {
48    fn check(&self) -> Vec<&Dependency> {
49        self.dependencies
50            .iter()
51            .filter_map(|dependency| {
52                let mut command = Command::new(&dependency.check.command[0]);
53                command.args(&dependency.check.command[1..]);
54                let output = command.output().ok();
55
56                match output.map(|output| output.status.success()) {
57                    Some(true) => None,
58                    _ => Some(dependency),
59                }
60            })
61            .collect()
62    }
63}
64
65impl Task for InstallDependencies {
66    type Context = ();
67    type Error = Error;
68
69    fn verb(&self) -> &str {
70        "Checking"
71    }
72
73    fn message(&self) -> &str {
74        "dependencies"
75    }
76
77    fn detail(&self) -> &str {
78        ""
79    }
80
81    fn run(
82        &self,
83        context: Self::Context,
84        _manager: &mut Manager,
85    ) -> Result<Self::Context, Self::Error> {
86        let missing = self.check();
87
88        if missing.is_empty() {
89            return Ok(context);
90        }
91
92        eprintln!("\n\nThe following dependencies haven't been found and need to be installed:\n",);
93
94        for dependency in missing.iter() {
95            eprintln!(
96                "    {} {}",
97                Color::Cyan.bold().paint(&dependency.name),
98                Color::Fixed(8).paint(format!("({})", dependency.install_command.join(" ")))
99            );
100        }
101
102        eprintln!("");
103
104        if Confirm::new()
105            .with_prompt("Do you want Polyhorn to install these dependencies automatically?")
106            .interact()?
107        {
108            for dependency in missing {
109                eprintln!(
110                    "\nInstalling {} ...\n",
111                    Color::Cyan.bold().paint(&dependency.name),
112                );
113                let mut command = Command::new(&dependency.install_command[0]);
114                command.args(&dependency.install_command[1..]);
115                command.stdout(Stdio::inherit());
116                command.stderr(Stdio::inherit());
117
118                match command.status() {
119                    Ok(status) if status.success() => {}
120                    Ok(status) => match status.code() {
121                        Some(code) => std::process::exit(code),
122                        None => std::process::exit(1),
123                    },
124                    Err(_) => {}
125                }
126
127                eprint!("");
128            }
129        } else {
130            std::process::exit(1);
131        }
132
133        eprintln!("");
134
135        Ok(context)
136    }
137}