main program

This commit is contained in:
2025-03-02 05:53:27 +05:30
parent 796d378bbb
commit f99e5991f8

548
src/main.rs Normal file
View File

@@ -0,0 +1,548 @@
use std::{fs, env, vec, error::Error, process::Command, path::Path, collections::HashMap, io::{self, Write}};
use serde::Deserialize;
use rust_embed::RustEmbed;
use tempfile::NamedTempFile;
#[derive(Deserialize)]
struct Flatpak {
map: HashMap<String, String>,
}
#[derive(RustEmbed)]
#[folder = "data/"]
struct Asset;
fn main() -> io::Result<()> {
clear_terminal();
let binding = detect_distro();
let distro: &str = binding.as_str();
let file_content = match distro {
"arch" => Asset::get("prompts/arch.txt").ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "File not found"))?,
"Debian" => Asset::get("prompts/debian.txt").ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "File not found"))?,
"fedora" => Asset::get("prompts/fedora.txt").ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "File not found"))?,
_ => return Err(io::Error::new(io::ErrorKind::NotFound, "Unsupported distro")),
};
let buffer = std::str::from_utf8(&file_content.data)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let first_choice: Vec<String> = buffer.lines().map(|line| line.to_string()).collect();
for(index, element) in first_choice.iter().enumerate() {
eprint!("{}. {}\n", index+1, element)
}
eprint!("\nEnter 0 to exit\n");
let _ = match distro {
"arch" => arch_choices(),
"Debian" => debian_choices(),
"fedora" => fedora_choices(),
_ => return Err(io::Error::new(io::ErrorKind::NotFound, "Unsupported distro")),
};
Ok(())
}
fn arch_choices() -> io::Result<()> {
let input: i32 = handle_int_input();
let _ = match input {
1 => install_programs(),
2 => install_flatpaks(),
3 => update_system(),
4 => aur(),
5 => drivers(),
_ => return Err(io::Error::new(io::ErrorKind::NotFound, "Invalid input")),
};
Ok(())
}
fn debian_choices() -> io::Result<()> {
let input: i32 = handle_int_input();
let _ = match input {
1 => install_programs(),
2 => install_flatpaks(),
3 => update_system(),
4 => third_party(),
5 => drivers(),
_ => return Err(io::Error::new(io::ErrorKind::NotFound, "Invalid input")),
};
Ok(())
}
fn fedora_choices() -> io::Result<()> {
let input: i32 = handle_int_input();
let _ = match input {
1 => install_programs(),
2 => install_flatpaks(),
3 => update_system(),
4 => third_party(),
5 => Ok(rpm()),
6 => drivers(),
_ => return Err(io::Error::new(io::ErrorKind::NotFound, "Invalid input")),
};
Ok(())
}
fn install_programs() -> Result<(), Box<dyn std::error::Error>> {
clear_terminal();
let file = Asset::get("prompts/programs.txt").ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "File not found"))?;
let buffer = std::str::from_utf8(&file.data)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let choice: Vec<String> = buffer.lines().map(|line| line.to_string()).collect();
for(index, element) in choice.iter().enumerate() {
eprint!("{}. {}\n", index+1, element)
}
eprint!("\nEnter 0 to go back\n");
let input = handle_vec_input();
let mut chosen: Vec<String> = vec![];
for &n in input.iter() {
if input.contains(&0) {
let _ = main();
} else {
chosen.push(choice[(n-1) as usize].clone());
}
}
if which("apt").is_some() {
Command::new("sudo").arg("apt").arg("update");
}
for n in chosen.iter() {
if which("yay").is_some() {
Command::new("yay").arg("-S").arg(n).status()?;
} else if which("pacman").is_some() {
Command::new("sudo").arg("pacman").arg("-S").arg(n).status()?;
} else if which("dnf").is_some() {
Command::new("sudo").arg("dnf").arg("install").arg(n).status()?;
} else if which("apt").is_some() {
Command::new("sudo").arg("apt").arg("install").arg(n).status()?;
}
};
let _ = main();
Ok(())
}
fn flatpak_file() -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
let file_content = Asset::get("prompts/flatpaks.json")
.ok_or("Embedded file not found")?;
let contents = std::str::from_utf8(file_content.data.as_ref())?;
let data: Flatpak = serde_json::from_str(contents)?;
Ok(data.map)
}
fn install_flatpaks() -> Result<(), Box<dyn std::error::Error>> {
clear_terminal();
let binding = detect_distro();
let distro = binding.as_str();
if which("flatpak").is_none() {
eprint!("\nInstall Flatpak first? Y/N (1/0)\n");
let input = handle_int_input();
if input == 0 {
let _ = main();
} else {
match distro {
"arch" => {
Command::new("sudo").arg("pacman").arg("-S").arg("flatpak").status()?;
}
"Debian" => {
Command::new("sudo").arg("apt").arg("update").status()?;
Command::new("sudo").arg("apt").arg("install").arg("flatpak").status()?;
}
"fedora" => {
Command::new("sudo").arg("dnf").arg("install").arg("flatpak").status()?;
}
_ => eprint!("Unsupported distro")
};
}
}
match flatpak_file() {
Ok(hashmap) => {
for (i, (_key, value)) in hashmap.iter().enumerate() {
eprint!("{}. {}\n",i+1, value);
}
eprint!("\nEnter 0 to go back\n");
let input = handle_vec_input();
for &n in input.iter() {
if input.contains(&0) {
let _ = main();
} else if let Some((key, _)) = hashmap.iter().nth((n-1) as usize) {
let status = Command::new("flatpak").arg("install").arg("-y").arg(key).status();
match status {
Ok(s) if s.success() => eprint!("Successfully installed {}", key),
Ok(_) => eprintln!("Failed to install Flatpak: {}", key),
Err(e) => eprintln!("Error executing command: {}", e),
}
} else {
eprint!("Invalid selection");
}
}
}
Err(e) => {
eprint!("Error reading file: {}", e);
}
}
let _ = main();
Ok(())
}
fn update_system() -> Result<(), Box<dyn std::error::Error>> {
clear_terminal();
let binding = detect_distro();
let distro = binding.as_str();
match distro {
"arch" => {
if which("yay").is_some() {
Command::new("yay").status()?;
} else {
Command::new("sudo").arg("pacman").arg("-Syu").status()?;
}
}
"Debian" => {
Command::new("sudo").arg("apt").arg("update").status()?;
Command::new("sudo").arg("apt").arg("upgrade").status()?;
}
"fedora" => {
Command::new("sudo").arg("dnf").arg("up").status()?;
}
_ => eprint!("Unsupported distro")
};
let _ = main();
Ok(())
}
fn aur() -> Result<(), Box<dyn std::error::Error>> {
clear_terminal();
let file = Asset::get("prompts/aur.txt").ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "File not found"))?;
let buffer = std::str::from_utf8(&file.data)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let choice: Vec<String> = buffer.lines().map(|line| line.to_string()).collect();
if which("yay").is_none() {
eprint!("AUR helper not found. Install yay Y/N? (1/0)");
let input = handle_int_input();
if input == 1 {
Command::new("sudo").arg("pacman").arg("-S").arg("git").arg("base-devel").status()?;
Command::new("git").arg("clone").arg("https://aur.archlinux.org/yay.git").status()?;
std::env::set_current_dir("yay")?;
Command::new("makepkg").arg("-si").status()?;
} else {
let _ = main();
}
}
for (index, element) in choice.iter().enumerate() {
eprint!("{}. {}\n", index+1, element)
}
eprint!("\nEnter 0 to go back\n");
let input = handle_vec_input();
let mut chosen: Vec<String> = vec![];
for &n in input.iter() {
if input.contains(&0) {
let _ = main();
} else {
chosen.push(choice[(n-1) as usize].clone());
}
}
for i in chosen.iter() {
Command::new("yay").arg("-S").arg(i).status()?;
}
let _ = main();
Ok(())
}
fn third_party() -> Result<(), Box<dyn std::error::Error>> {
clear_terminal();
let file = Asset::get("prompts/third-party.txt")
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "File not found"))?;
let buffer = std::str::from_utf8(&file.data)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
let choices: Vec<String> = buffer.lines().map(|line| line.to_string()).collect();
for (index, element) in choices.iter().enumerate() {
eprintln!("{}. {}", index + 1, element);
}
eprintln!("\nEnter 0 to go back");
let mut chosen: Vec<String> = vec![];
let input = handle_vec_input();
for &n in input.iter() {
if input.contains(&0) {
let _ = main();
} else {
chosen.push(choices[(n-1) as usize].clone());
}
}
for j in chosen.iter() {
let script_name = format!("scripts/{}.sh", j);
if let Some(script) = Asset::get(&script_name) {
let mut temp_file = NamedTempFile::new()?;
temp_file.write_all(&script.data)?;
let temp_file_path = temp_file.path();
let status = Command::new("bash")
.arg(temp_file_path)
.status()?;
if !status.success() {
eprintln!("Script failed: {}", script_name);
}
} else {
eprintln!("Script not found: {}", script_name);
}
}
let _ = main();
Ok(())
}
fn drivers() -> Result<(), Box<dyn Error>> {
clear_terminal();
let distro = detect_distro();
let choices = vec!["Intel", "AMD", "Nvidia"];
for (index, element) in choices.iter().enumerate() {
eprintln!("{}. {}", index + 1, element);
}
eprintln!("\nEnter 0 to go back");
let input: i32 = handle_int_input();
if input == 0 {
let _ = main();
}
match input {
1 => handle_intel_driver(&distro)?,
2 => handle_amd_driver(&distro)?,
3 => handle_nvidia_driver(&distro)?,
_ => eprintln!("Invalid choice"),
}
Ok(())
}
fn handle_intel_driver(distro: &str) -> Result<(), Box<dyn Error>> {
clear_terminal();
match distro {
"arch" => {
let intel_models = vec!["intel-media-driver", "libva-intel-driver"];
for (index, element) in intel_models.iter().enumerate() {
eprintln!("{}. {}", index + 1, element);
}
eprintln!("\nEnter 0 to go back");
let input_2 = handle_int_input();
if input_2 == 0 {
return drivers();
}
match input_2 {
1 => Command::new("sudo").arg("pacman").arg("-S").arg("intel-media-driver").status()?,
2 => Command::new("sudo").arg("pacman").arg("-S").arg("libva-intel-driver").status()?,
_ => {
eprintln!("Invalid choice");
return Ok(());
},
}
},
"Debian" => {
Command::new("sudo").arg("apt").arg("update").status()?;
Command::new("sudo").arg("apt").arg("install").arg("intel-media-va-driver").status()
}?,
"fedora" => {
Command::new("sudo").arg("dnf").arg("install").arg("intel-media-driver").status()?
},
_ => {
eprintln!("Invalid choice");
return Ok(());
},
};
let _ = drivers();
Ok(())
}
fn handle_amd_driver(distro: &str) -> Result<(), Box<dyn Error>> {
clear_terminal();
match distro {
"arch" => Command::new("sudo").arg("pacman").arg("-S").arg("mesa").status()?,
"Debian" => {
Command::new("sudo").arg("apt").arg("update").status()?;
Command::new("sudo").arg("apt").arg("install").arg("mesa-va-drivers").status()
}?,
"fedora" => Command::new("sudo").arg("dnf").arg("install").arg("mesa-va-drivers").status()?,
_ => {
eprintln!("Unsupported distro");
return Ok(())
},
};
let _ = drivers();
Ok(())
}
fn handle_nvidia_driver(distro: &str) -> Result<(), Box<dyn Error>> {
clear_terminal();
match distro {
"arch" => Command::new("sudo").arg("pacman").arg("-S").arg("nvidia").status()?,
"Debian" => {
Command::new("sudo").arg("apt").arg("update").status()?;
Command::new("sudo").arg("apt").arg("install").arg("nvidia-driver").status()
}?,
"fedora" => Command::new("sudo").arg("dnf").arg("install").arg("akmod-nvidia").status()?,
_ => {
eprintln!("Unsupported distro");
return Ok(())
},
};
let _ = drivers();
Ok(())
}
fn rpm() {
clear_terminal();
let version = Command::new("rpm")
.arg("-E")
.arg("%fedora")
.output()
.expect("Failed to lookup fedora version");
if version.status.success() {
let stdout = String::from_utf8_lossy(&version.stdout);
let version_str = stdout.trim();
println!("Fedora version: {}", version_str);
let url1 = format!("https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-{}.noarch.rpm", version_str);
let url2 = format!("https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-{}.noarch.rpm", version_str);
let status1 = Command::new("sudo").arg("dnf").arg("install").arg("-y").arg(&url1).status();
let status2 = Command::new("sudo").arg("dnf").arg("install").arg("-y").arg(&url2).status();
if status1.is_ok() && status1.unwrap().success() {
println!("Successfully installed RPM Fusion free repository.");
} else {
eprintln!("Failed to install RPM Fusion free repository.");
}
if status2.is_ok() && status2.unwrap().success() {
println!("Successfully installed RPM Fusion nonfree repository.");
} else {
eprintln!("Failed to install RPM Fusion nonfree repository.");
}
} else {
let stderr = String::from_utf8_lossy(&version.stderr);
eprintln!("Failed to get Fedora version with status: {}", version.status);
eprintln!("Error output:\n{}", stderr);
}
let _ = main();
}
fn handle_vec_input() -> Vec<i32> {
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Error in reading.");
input
.trim()
.split_whitespace()
.filter_map(|x| x.parse().ok())
.collect()
}
fn handle_int_input() -> i32 {
loop {
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read input");
match input.trim().parse::<i32>() {
Ok(num) => return num,
Err(_) => println!("Please enter a valid integer."),
}
}
}
fn detect_distro() -> String {
let detected_distro = fs::read_to_string("/etc/os-release")
.expect("Could not find os-release file on system");
if detected_distro.contains("arch") {
"arch".to_string()
} else if detected_distro.contains("Debian") || detected_distro.contains("ubuntu") {
"Debian".to_string()
} else if detected_distro.contains("fedora") {
"fedora".to_string()
} else {
eprintln!("Distro Not Supported");
"unsupported".to_string()
}
}
fn which(program: &str) -> Option<String> {
if let Ok(paths) = env::var("PATH") {
for path in paths.split(':') {
let full_path = Path::new(path).join(program);
if full_path.exists() && full_path.is_file() {
return Some(full_path.to_string_lossy().to_string());
}
}
}
None
}
fn clear_terminal() {
Command::new("clear")
.status()
.expect("Failed to clear the terminal");
}