rust_utils/
utils.rs

1//! Miscellaneous convenience functions
2//!
3//! These functions make some menial tasks a little easier to do,
4//! like getting the executable's name, or running a command
5
6use std::{
7    env, io,
8    fmt::Display,
9    path::{Path, PathBuf},
10    ffi::OsStr,
11    process::{
12        Child,
13        Command,
14        Stdio
15    }
16};
17
18/// Data returned by utils::run_command()
19///
20/// This has the command's output, status code, and whether it was successful or not
21pub struct CommandOutput {
22    /// Text output from the executed command
23    pub output: String,
24
25    /// The exit code of the executed command
26    ///
27    /// 0 means it was successfully executed
28    pub code: u32
29}
30
31impl CommandOutput {
32    /// Was this command successfully executed?
33    pub fn success(&self) -> bool {
34        self.code == 0
35    }
36}
37
38/// Run a command and return the output, status code, and whether the command succeeded or not
39#[track_caller]
40pub fn run_command<A: IntoIterator<Item = S> + Clone, S: AsRef<OsStr>>(prog: &str, superuser: bool, args: A) -> CommandOutput {
41    let mut cmd = if superuser {
42        let mut cmd_t = Command::new("sudo");
43        cmd_t.arg(prog);
44        cmd_t
45    }
46    else { Command::new(prog) };
47
48    cmd.args(args);
49
50    let output = match cmd.output() {
51        Ok(o) => o,
52        Err(why) => {
53            return CommandOutput {
54                output: why.to_string(),
55                code: 666
56            };
57        }
58    };
59
60    let code = output.status.code().unwrap_or(1) as u32;
61
62    let mut o_fmt = if code == 0 {
63        String::from_utf8(output.stdout).unwrap()
64    }
65    else {
66        String::from_utf8(output.stderr).unwrap()
67    };
68
69    o_fmt.pop();
70
71    CommandOutput {
72        output: o_fmt,
73        code: output.status.code().unwrap_or(1) as u32
74    }
75}
76
77/// Spawn a detached process
78#[track_caller]
79pub fn spawn_process<A: IntoIterator<Item = S> + Clone, S: AsRef<OsStr>>(prog: &str, superuser: bool, should_output: bool, args: A) -> Child {
80    let mut cmd = if superuser {
81        let mut cmd_t = Command::new("sudo");
82        cmd_t.arg(prog);
83        cmd_t
84    }
85    else {
86        Command::new(prog)
87    };
88
89    if !should_output {
90        cmd.stdout(Stdio::null());
91        cmd.stderr(Stdio::null());
92    }
93
94    cmd.args(args);
95    cmd.spawn().unwrap()
96}
97
98/// Alphabetizes a vector of strings
99pub fn alphabetize<L: Display>(list: &[L]) -> Vec<String> {
100    // extract the strings from the generic value
101    let mut string_list: Vec<String> = list.iter().map(L::to_string).collect();
102    string_list.sort_unstable_by_key(|a| a.to_lowercase());
103    string_list
104}
105
106/// Get the name of the file/directory from a file path
107///
108/// Returns an empty string if there is no file name
109pub fn get_filename<D: Into<PathBuf>>(dir: D) -> String {
110    let path = dir.into();
111    if let Some(file_name) = path.file_name() {
112        file_name.to_str().unwrap().to_string()
113    }
114    else { String::new() }
115}
116
117/// Gets the parent directory from a file path
118///
119/// Returns the current path if there is no parent
120pub fn get_parent<D: Into<PathBuf>>(dir: D) -> String {
121    let path = dir.into();
122    if let Some(parent) = path.parent() {
123        parent.display().to_string()
124    }
125    else {
126        path.display().to_string()
127    }
128}
129
130/// Capitalize the first letter in a string
131pub fn capitalize<W: Display>(word: W) -> String {
132    let s1 = word.to_string();
133    let mut v: Vec<char> = s1.chars().collect();
134
135    for item in &mut v {
136        if item.is_ascii_alphanumeric() {
137            *item = item.to_ascii_uppercase();
138            break;
139        }
140    }
141
142    let s2: String = v.into_iter().collect();
143    s2
144}
145
146/// Get the name of the executable
147#[track_caller]
148pub fn get_execname() -> String {
149    env::args().next()
150        .as_ref()
151        .map(Path::new)
152        .and_then(Path::file_name)
153        .and_then(OsStr::to_str)
154        .map(String::from)
155        .unwrap()
156}
157
158/// Show a message and
159/// get the user's input from the standard input
160#[track_caller]
161pub fn get_input<M: Display>(msg: M) -> String {
162    println!("{msg}");
163    let mut buf_string = String::new();
164    io::stdin().read_line(&mut buf_string).unwrap();
165    buf_string.retain(|c| c != '\n');
166    buf_string
167}