cli/installer/
cargo_plugin_installer.rs1#[cfg(test)]
7#[path = "cargo_plugin_installer_test.rs"]
8mod cargo_plugin_installer_test;
9
10use crate::command;
11use crate::error::CargoMakeError;
12use crate::installer::crate_version_check;
13use crate::toolchain::wrap_command;
14use crate::types::ToolchainSpecifier;
15use std::process::Command;
16use strip_ansi_escapes::strip_str;
17
18fn is_crate_in_list_output_legacy(crate_name: &str, output: &str) -> bool {
19 let lines: Vec<&str> = output.split(' ').collect();
20 for mut line in lines {
21 line = line.trim();
22
23 debug!("Checking (legacy): {}", &line);
24
25 if line.contains(crate_name) && crate_name.contains(line) {
26 debug!("Found installed cratei (legacy).");
27
28 return true;
29 }
30 }
31
32 false
33}
34
35fn is_crate_in_list_output(crate_name: &str, output: &str) -> bool {
36 let lines: Vec<&str> = output.split('\n').collect();
37 for mut line in lines {
38 line = line.trim();
39
40 let words: Vec<&str> = line.split(' ').collect();
41 let plugin_name = words[0].trim();
42 let found = crate_name.eq(plugin_name);
43 debug!(
44 "Checking Line: {}\nPlugin: <{}> Expected: <{}> Sizes: {}/{} Found: {}",
45 &line,
46 &plugin_name,
47 &crate_name,
48 plugin_name.len(),
49 crate_name.len(),
50 found
51 );
52
53 if found {
54 debug!("Found installed crate.");
55
56 return true;
57 }
58 }
59
60 false
61}
62
63fn is_crate_installed(
64 toolchain: &Option<ToolchainSpecifier>,
65 crate_name: &str,
66) -> Result<bool, CargoMakeError> {
67 debug!("Getting list of installed cargo commands.");
68
69 let mut command_struct = match toolchain {
70 Some(ref toolchain_string) => {
71 let command_spec = wrap_command(toolchain_string, "cargo", &None);
72 let mut cmd = Command::new(command_spec.command);
73 cmd.args(command_spec.args.unwrap());
74
75 cmd
76 }
77 None => Command::new("cargo"),
78 };
79
80 let result = command_struct.arg("--list").output();
81
82 match result {
83 Ok(output) => {
84 let exit_code = command::get_exit_code(Ok(output.status), false);
85 command::validate_exit_code(exit_code)?;
86
87 let stdout = strip_str(String::from_utf8_lossy(&output.stdout));
88 let crate_name_trimmed = crate_name.trim();
89 Ok(is_crate_in_list_output(&crate_name_trimmed, &stdout)
90 || is_crate_in_list_output_legacy(&crate_name_trimmed, &stdout))
91 }
92 Err(error) => {
93 error!(
94 "Unable to check if crate is installed: {} {:#?}",
95 crate_name, &error
96 );
97 Ok(false)
98 }
99 }
100}
101
102fn should_skip_crate_name(args: &Option<Vec<String>>) -> bool {
103 match *args {
104 Some(ref args_vec) => args_vec.contains(&"--git".to_string()),
105 None => false,
106 }
107}
108
109pub(crate) fn get_install_crate_args(
110 crate_name: &str,
111 force: bool,
112 args: &Option<Vec<String>>,
113 version_option: &Option<String>,
114 install_command: &Option<String>,
115) -> Vec<String> {
116 let install_command_str = match install_command {
117 Some(value) => value.clone(),
118 None => "install".to_string(),
119 };
120 let mut install_args = vec![install_command_str.to_string()];
121
122 if force {
123 install_args.push("--force".to_string());
124 }
125
126 match *args {
127 Some(ref args_vec) => {
128 for arg in args_vec.iter() {
129 install_args.push(arg.to_string());
130 }
131 }
132 None => debug!("No crate installation args defined."),
133 };
134
135 let skip_crate_name = should_skip_crate_name(&args);
136 if !skip_crate_name {
137 if let Some(version) = version_option {
139 if envmnt::is("CARGO_MAKE_CRATE_INSTALLATION_LOCKED") {
140 install_args.push("--locked".to_string());
141 install_args.push("--version".to_string());
142 install_args.push(version.to_string());
143 }
144 }
145
146 install_args.push(crate_name.to_string());
147 }
148
149 install_args
150}
151
152pub(crate) fn install_crate(
153 toolchain: &Option<ToolchainSpecifier>,
154 cargo_command: Option<&str>,
155 crate_name: &str,
156 args: &Option<Vec<String>>,
157 validate: bool,
158 min_version: &Option<String>,
159 install_command: &Option<String>,
160 allow_force: &Option<bool>,
161) -> Result<(), CargoMakeError> {
162 let installed = match cargo_command {
163 Some(cargo_command) => is_crate_installed(&toolchain, cargo_command),
164 None => Ok(false),
165 }?;
166 let mut force = false;
167 let allow_force_value = allow_force.unwrap_or(true);
168 let run_installation = if !installed {
169 true
170 } else if toolchain.is_none() {
171 match *min_version {
172 Some(ref version) => {
173 if crate_version_check::is_min_version_valid(&crate_name, version, None) {
174 false
175 } else {
176 force = allow_force_value;
177 true
178 }
179 }
180 None => false,
181 }
182 } else {
183 false
184 };
185
186 if run_installation {
187 let install_args =
188 get_install_crate_args(crate_name, force, args, &min_version, install_command);
189
190 match toolchain {
191 Some(ref toolchain_string) => {
192 let command_spec = wrap_command(&toolchain_string, "cargo", &Some(install_args));
193 command::run_command(&command_spec.command, &command_spec.args, validate)?
194 }
195 None => command::run_command("cargo", &Some(install_args), validate)?,
196 };
197 }
198 Ok(())
199}