1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Miscellaneous convenience functions
//!
//! These functions make some menial tasks a little easier to do,
//! like getting the executable's name, or running a command

use std::{
    env, io,
    fmt::Display,
    path::{Path, PathBuf},
    ffi::OsStr,
    process::{
        Child,
        Command,
        Stdio
    }
};

/// Data returned by utils::run_command()
///
/// This has the command's output, status code, and whether it was successful or not
pub struct CommandOutput {
    /// Text output from the executed command
    pub output: String,

    /// The exit code of the executed command
    ///
    /// 0 means it was successfully executed
    pub code: u32
}

impl CommandOutput {
    /// Was this command successfully executed?
    pub fn success(&self) -> bool {
        self.code == 0
    }
}

/// Run a command and return the output, status code, and whether the command succeeded or not
#[track_caller]
pub fn run_command<A: IntoIterator<Item = S> + Clone, S: AsRef<OsStr>>(prog: &str, superuser: bool, args: A) -> CommandOutput {
    let mut cmd = if superuser {
        let mut cmd_t = Command::new("sudo");
        cmd_t.arg(prog);
        cmd_t
    }
    else { Command::new(prog) };

    cmd.args(args);

    let output = match cmd.output() {
        Ok(o) => o,
        Err(why) => {
            return CommandOutput {
                output: why.to_string(),
                code: 666
            };
        }
    };

    let code = output.status.code().unwrap_or(1) as u32;

    let mut o_fmt = if code == 0 {
        String::from_utf8(output.stdout).unwrap()
    }
    else {
        String::from_utf8(output.stderr).unwrap()
    };

    o_fmt.pop();

    CommandOutput {
        output: o_fmt,
        code: output.status.code().unwrap_or(1) as u32
    }
}

/// Spawn a detached process
#[track_caller]
pub fn spawn_process<A: IntoIterator<Item = S> + Clone, S: AsRef<OsStr>>(prog: &str, superuser: bool, should_output: bool, args: A) -> Child {
    let mut cmd = if superuser {
        let mut cmd_t = Command::new("sudo");
        cmd_t.arg(prog);
        cmd_t
    }
    else {
        Command::new(prog)
    };

    if !should_output {
        cmd.stdout(Stdio::null());
        cmd.stderr(Stdio::null());
    }

    cmd.args(args);
    cmd.spawn().unwrap()
}

/// Alphabetizes a vector of strings
pub fn alphabetize<L: Display>(list: &[L]) -> Vec<String> {
    // extract the strings from the generic value
    let mut string_list: Vec<String> = list.iter().map(L::to_string).collect();
    string_list.sort_unstable_by_key(|a| a.to_lowercase());
    string_list
}

/// Get the name of the file/directory from a file path
///
/// Returns an empty string if there is no file name
pub fn get_filename<D: Into<PathBuf>>(dir: D) -> String {
    let path = dir.into();
    if let Some(file_name) = path.file_name() {
        file_name.to_str().unwrap().to_string()
    }
    else { String::new() }
}

/// Gets the parent directory from a file path
///
/// Returns the current path if there is no parent
pub fn get_parent<D: Into<PathBuf>>(dir: D) -> String {
    let path = dir.into();
    if let Some(parent) = path.parent() {
        parent.display().to_string()
    }
    else {
        path.display().to_string()
    }
}

/// Capitalize the first letter in a string
pub fn capitalize<W: Display>(word: W) -> String {
    let s1 = word.to_string();
    let mut v: Vec<char> = s1.chars().collect();

    for item in &mut v {
        if item.is_ascii_alphanumeric() {
            *item = item.to_ascii_uppercase();
            break;
        }
    }

    let s2: String = v.into_iter().collect();
    s2
}

/// Get the name of the executable
#[track_caller]
pub fn get_execname() -> String {
    env::args().next()
        .as_ref()
        .map(Path::new)
        .and_then(Path::file_name)
        .and_then(OsStr::to_str)
        .map(String::from)
        .unwrap()
}

/// Show a message and
/// get the user's input from the standard input
#[track_caller]
pub fn get_input<M: Display>(msg: M) -> String {
    println!("{msg}");
    let mut buf_string = String::new();
    io::stdin().read_line(&mut buf_string).unwrap();
    buf_string.retain(|c| c != '\n');
    buf_string
}