cargo_parcel/
util.rs

1//! Miscellaneous utilities
2
3use std::{
4    ffi::{OsStr, OsString},
5    fmt,
6    path::PathBuf,
7    process::{Command, ExitStatus},
8};
9
10use anyhow::Error;
11use pico_args::Arguments;
12
13#[derive(Debug)]
14struct ExecError {
15    program: OsString,
16    args: Vec<OsString>,
17    status: ExitStatus,
18}
19
20impl<'a> fmt::Display for ExecError {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        write!(
23            f,
24            "subprocess {} failed with status {}",
25            self.program.to_string_lossy(),
26            self.status
27        )
28    }
29}
30
31impl std::error::Error for ExecError {}
32
33/// Display a list of command-line arguments.
34#[derive(Debug)]
35pub struct DisplayArgs<T>(pub T);
36
37impl<T> fmt::Display for DisplayArgs<T>
38where
39    T: Iterator + Clone,
40    T::Item: AsRef<OsStr>,
41{
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        let mut args = self.0.clone();
44        let mut next = args.next();
45        while let Some(arg) = next {
46            // FIXME: quoting
47            let s = arg.as_ref().to_string_lossy();
48            write!(f, "{}", s)?;
49            next = args.next();
50            if next.is_some() {
51                write!(f, " ")?;
52            }
53        }
54        Ok(())
55    }
56}
57
58/// Construct an empty set of environment variables.
59pub fn empty_env() -> Vec<(&'static str, &'static OsStr)> {
60    Vec::new()
61}
62
63/// Run a program in a subprocess.
64///
65/// Blockingly runs the given executable with the supplied arguments and
66/// environment.
67pub fn cmd<P, E, A, V>(program: P, args: A, env: E, verbose: bool) -> Result<(), Error>
68where
69    P: AsRef<OsStr>,
70    A: IntoIterator,
71    A::IntoIter: Clone,
72    A::Item: AsRef<OsStr>,
73    E: IntoIterator<Item = (&'static str, V)>,
74    V: AsRef<OsStr>,
75{
76    let program = program.as_ref();
77    let args = args.into_iter();
78    let env = env.into_iter();
79    if verbose {
80        println!(
81            "* {} {}",
82            program.to_string_lossy(),
83            DisplayArgs(args.clone())
84        );
85    }
86    let status = Command::new(program)
87        .args(args.clone())
88        .envs(env)
89        .status()?;
90    if !status.success() {
91        return Err(ExecError {
92            program: program.to_owned(),
93            args: args.map(|arg| arg.as_ref().to_owned()).collect(),
94            status,
95        }
96        .into());
97    }
98    Ok(())
99}
100
101/// Get the path prefix to use.
102///
103/// If present in the supplied arguments, return the value given for
104/// `--prefix`. Otherwise, it will return a default, currently `~/.local` for
105/// all platforms.
106pub fn get_prefix_opt(args: &mut Arguments) -> anyhow::Result<PathBuf> {
107    match args.opt_value_from_str("--prefix")? {
108        Some(prefix) => Ok(prefix),
109        None => {
110            let mut home = PathBuf::from(std::env::var("HOME")?);
111            home.push(".local");
112            Ok(home)
113        }
114    }
115}