1use crate::errors::{bail, Result, ResultExt};
4use log::trace;
5use serde_derive::{Deserialize, Serialize};
6use std::collections::hash_map::{Entry, HashMap};
7use std::ffi::OsString;
8use std::fmt::{Debug, Display};
9use std::hash::{BuildHasher, Hash};
10use std::io::{stderr, stdout, Write};
11use std::path::PathBuf;
12use std::process::Command;
13use std::sync::{Arc, Mutex};
14use std::{env, iter, process};
15
16#[cfg(windows)]
17pub fn exe_suffix() -> &'static str {
20 ".exe"
21}
22
23#[cfg(not(windows))]
24pub fn exe_suffix() -> &'static str {
27 ""
28}
29
30pub fn add_to_multihash<K, T, V, S>(hash: &mut HashMap<K, V, S>, key: K, value: T)
33where
34 K: Eq + Hash + Clone,
35 V: Default + Extend<T>,
36 S: BuildHasher,
37{
38 match hash.entry(key) {
39 Entry::Occupied(mut entry) => entry.get_mut().extend(iter::once(value)),
40 Entry::Vacant(entry) => {
41 let mut r = V::default();
42 r.extend(iter::once(value));
43 entry.insert(r);
44 }
45 }
46}
47
48pub fn run_command(command: &mut Command) -> Result<()> {
50 trace!("Executing command: {:?}", command);
51 let status = command
52 .status()
53 .with_context(|_| format!("failed to run command: {:?}", command))?;
54 if status.success() {
55 Ok(())
56 } else {
57 bail!("command failed with {}: {:?}", status, command);
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub struct CommandOutput {
63 pub status: i32,
64 pub stdout: String,
65 pub stderr: String,
66}
67
68impl CommandOutput {
69 pub fn is_success(&self) -> bool {
70 self.status == 0
71 }
72}
73
74pub fn run_command_and_capture_output(command: &mut Command) -> Result<CommandOutput> {
77 trace!("Executing command: {:?}", command);
78 command.stdout(process::Stdio::piped());
79 command.stderr(process::Stdio::piped());
80 let output = command
81 .output()
82 .with_context(|_| format!("failed to run command: {:?}", command))?;
83 Ok(CommandOutput {
84 stdout: String::from_utf8_lossy(&output.stdout).to_string(),
85 stderr: String::from_utf8_lossy(&output.stderr).to_string(),
86 status: output.status.code().unwrap_or(-1),
87 })
88}
89
90pub fn get_command_output(command: &mut Command) -> Result<String> {
92 trace!("Executing command: {:?}", command);
93 command.stdout(process::Stdio::piped());
94 command.stderr(process::Stdio::piped());
95 let output = command
96 .output()
97 .with_context(|_| format!("failed to run command: {:?}", command))?;
98 if output.status.success() {
99 Ok(String::from_utf8(output.stdout)
100 .with_context(|_| "comand output is not valid unicode")?)
101 } else {
102 let mut stderr = stderr();
103 writeln!(stderr, "Stdout:")?;
104 stderr
105 .write_all(&output.stdout)
106 .with_context(|_| "output failed")?;
107 writeln!(stderr, "Stderr:")?;
108 stderr
109 .write_all(&output.stderr)
110 .with_context(|_| "output failed")?;
111 bail!("command failed with {}: {:?}", output.status, command);
112 }
113}
114
115pub trait MapIfOk<A> {
117 fn map_if_ok<B, E, F: FnMut(A) -> std::result::Result<B, E>>(
121 self,
122 f: F,
123 ) -> std::result::Result<Vec<B>, E>;
124}
125
126impl<A, T: IntoIterator<Item = A>> MapIfOk<A> for T {
127 fn map_if_ok<B, E, F>(self, f: F) -> std::result::Result<Vec<B>, E>
128 where
129 F: FnMut(A) -> std::result::Result<B, E>,
130 {
131 self.into_iter().map(f).collect()
132 }
133}
134
135pub fn add_env_path_item(env_var_name: &str, mut new_paths: Vec<PathBuf>) -> Result<OsString> {
139 for path in env::split_paths(&env::var(env_var_name).unwrap_or_default()) {
140 if new_paths.iter().find(|&x| x == &path).is_none() {
141 new_paths.push(path);
142 }
143 }
144 Ok(env::join_paths(new_paths).with_context(|_| "env::join_paths failed")?)
145}
146
147pub trait Inspect {
148 fn inspect(self, text: impl Display) -> Self;
149}
150
151impl<T: Debug> Inspect for T {
152 fn inspect(self, text: impl Display) -> Self {
153 println!("{} {:?}", text, self);
154 self
155 }
156}
157
158#[derive(Debug)]
159struct ProgressBarInner {
160 message: String,
161 count: u64,
162 pos: u64,
163 last_line_len: usize,
164}
165
166#[derive(Clone, Debug)]
167pub struct ProgressBar(Arc<Mutex<ProgressBarInner>>);
168
169impl ProgressBar {
170 pub fn new(count: u64, message: impl Into<String>) -> Self {
171 let mut progress_bar = ProgressBarInner {
172 count,
173 message: message.into(),
174 pos: 0,
175 last_line_len: 0,
176 };
177 progress_bar.print();
178 ProgressBar(Arc::new(Mutex::new(progress_bar)))
179 }
180
181 pub fn add(&self, n: u64) {
182 self.0.lock().unwrap().inc(n);
183 }
184}
185
186impl ProgressBarInner {
187 fn clear_line(&self) {
188 print!("\r");
189 for _ in 0..self.last_line_len {
190 print!(" ");
191 }
192 print!("\r");
193 }
194 fn print(&mut self) {
195 self.clear_line();
196 let message = format!("{}: {} / {}", self.message, self.pos, self.count);
197 self.last_line_len = message.len();
198 print!("{}\r", message);
199 stdout().flush().unwrap();
200 }
201
202 fn inc(&mut self, n: u64) {
203 self.pos += n;
204 self.print();
205 }
206}