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
//! Very simple helper functions to make building linux scripts in rust easier
// https://doc.rust-lang.org/stable/std/process/index.html
#![feature(range_contains)]

use std::{
    collections::HashMap, io, process::{Command, Output, Stdio},
};

/// Runs a `command` using `shell` (sh, bash, zsh, etc), `name` is used to make error messages
/// useful, e.g. `command("sh", "sudo pacman -Syyu", "update")` would panic on `Failed to execute
/// update` or `Failed to wait on update`. Returns output as `Output`
pub fn command(shell: &str, command: &str, name: &str) -> Output {
    Command::new(shell)
        .arg("-c")
        .arg(&format!("{}", command))
        .stdout(Stdio::piped())
        .spawn()
        .expect(&format!("Failed to execute {}", name))
        .wait_with_output()
        .expect(&format!("Failed to wait on {}", name))
}

/// Asks `question` (inserting a newline at the end) and given `options` (a vector of tuples
/// (option, function)) will show all `options` and numbers which have to be typed in in order to
/// choose them. If user types in 0, the function
/// returns
pub fn dialog(question: &str, mut options: Vec<(String, fn())>) {
    for i in 0..options.len() {
        options[i].0.insert_str(0, "[ ] ");
    }

    loop {
        print!("{}[2J", 27 as char); // clear screen

        println!("{}\n", question);

        for (i, (option, _)) in options.iter().enumerate() {
            println!("{}: {}", i + 1, option);
        }

        println!("\n0: Exit\n");

        let mut choice = String::new();

        io::stdin()
            .read_line(&mut choice)
            .expect("Failed to read line");

        let choice = choice.trim().parse::<usize>().unwrap_or(100);

        if choice == 0 {
            return;
        }

        if (1..options.len() + 1).contains(&choice) {
            options[choice - 1].1();
            options[choice - 1].0 = options[choice - 1].0.replace("[ ]", "[X]");
        }
    }
}

/// Installs a package using `repo`'s package manager, e.g. `aur` would use yay, `arch` would
/// use pacman, dnf is default. Can give additional arguments. Uses command
pub fn install(repo: &str, package: &str, args: &str) -> Output {
    let package_managers: HashMap<&str, &str> = [("aur", "yay -S --noconfirm --needed"),
        ("arch", "sudo pacman -S --noconfirm --needed")]
        .iter()
        .cloned()
        .collect();

    let mut package_manager = "sudo ";

    let repos = ["arch", "aur"];

    if repos.contains(&repo.to_lowercase().as_str()) {
        package_manager = package_managers[&repo];
    }

    command(
        "sh",
        &format!("{} {} {}", package_manager, package, args),
        &format!("install {}", package),
    )
}

#[cfg(test)]
mod tests {
    use command;

    fn test() {
        println!("echo");
    }

    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }

    #[test]
    fn command_with_output_works() {
        assert_eq!(
            command("sh", "echo hello", "echo")
                .stdout
                .into_iter()
                .map(|u| u as char)
                .collect::<String>(),
            "hello\n"
        );
    }

    #[test]
    fn dialog_format_works() {
        let mut options: Vec<(String, fn())> = vec![
            ("Update System".to_string(), test),
            ("Remove Packages".to_string(), test),
            ("Create Folders".to_string(), test),
            ("Install Software".to_string(), test),
            ("Setup".to_string(), test),
        ];

        for i in 0..options.len() {
            options[i].0.insert_str(0, "[ ] ");
        }

        assert_eq!(options[0].0, "[ ] Update System");
    }
}