.
675 | 
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # Auto-Ricer
 2 | 
 3 | 
 4 | 
 5 | A small auto-ricer for Hyprland on Arch Linux!
 6 | 
 7 | This installer will take all the configuration files from this repository _(https://github.com/3rfaan/dotfiles)_ and copies them into your 📁 **~/.config** directory.
 8 | 
 9 | **⚠️ NOTE: You have to install all the necessary programs from this [Guide](https://github.com/3rfaan/dotfiles/blob/main/README.md), otherwise the ricing won't have an effect on your system!**
10 | 
11 | ## Preview
12 | 
13 | 
14 | 
15 | ## Installation
16 | 
17 | ### [Crates.io](https://crates.io/crates/autoricer)
18 | 
19 | ```
20 | $ cargo install autoricer
21 | ```
22 | 
23 | You can then run the following command:
24 | 
25 | ```
26 | $ autoricer
27 | ```
28 | 
29 | ### [AUR](https://aur.archlinux.org/packages/autoricer-bin)
30 | 
31 | ```
32 | $ yay -S autoricer-bin
33 | ```
34 | 
35 | You can then run the following command:
36 | 
37 | ```
38 | $ autoricer
39 | ```
40 | 
41 | ### Build from Source
42 | 
43 | Execute the following commands in your terminal:
44 | 
45 | ```
46 | $ git clone https://github.com/3rfaan/autoricer.git $HOME/Downloads/autoricer && cd $HOME/Downloads/autoricer
47 | $ cargo run
48 | ```
49 | 
50 | _Note: Rust has to be installed on the system to build from source!_
51 | 
52 | ## Backup
53 | 
54 | A backup of all your old configuration files in 📁 **~/.config** will be created in 📁 **~/Documents/config_backup** before deleting anything.
55 | 
56 | ## Change keyboard layout
57 | 
58 | When prompted you can change the keyboard layout directly in the installer.
59 | 
60 | ## Nvidia Support
61 | 
62 | When prompted you can enable support for Nvidia in Hyprland. The installer will then put the appropriate environment variables inside Hyprland config file.
63 | 
64 | ## After Installation
65 | 
66 | After the installation process you probably have to restart Hyprland using super + shift + e.
67 | 
68 | The wallpaper can be changed inside 📁 **~/.config/hypr/hyprpaper.conf**
69 | 
--------------------------------------------------------------------------------
/hyprforest_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3rfaan/autoricer/8129b6256b8529768b18b0888e21257194267324/hyprforest_logo.png
--------------------------------------------------------------------------------
/src/installer.rs:
--------------------------------------------------------------------------------
  1 | use crate::{
  2 |     clone, error, info, prompt, success, tip,
  3 |     utils::{helper_functions::*, types::*},
  4 |     warning,
  5 | };
  6 | use colored::Colorize;
  7 | use once_cell::sync::Lazy;
  8 | use regex::Regex;
  9 | use std::{
 10 |     fs::{self, File},
 11 |     io::{self, BufRead, BufReader, BufWriter, Write},
 12 |     path::{Path, PathBuf},
 13 |     process::Command,
 14 | };
 15 | 
 16 | pub fn installation_prompt() -> io::Result {
 17 |     let mut input: String;
 18 | 
 19 |     tip!("This installer will copy the config files from this repo: https://github.com/3rfaan/arch-everforest\n\
 20 |           Make sure you've installed those programs to get the best experience.");
 21 |     warning!("==> This installer will modify directories inside your ~/.config directory");
 22 | 
 23 |     loop {
 24 |         prompt!("Do you want to proceed? [y/N]");
 25 | 
 26 |         input = read_input()?;
 27 | 
 28 |         match parse_input(&input) {
 29 |             UserInput::Yes => return Ok(Installation::Proceed),
 30 |             UserInput::No => return Ok(Installation::Exit),
 31 |             UserInput::Other => prompt!("==> Please enter [y]es or [n]o!"),
 32 |         }
 33 |     }
 34 | }
 35 | 
 36 | // Clones Github repo into ~/Downloads/arch-everforest
 37 | pub fn clone_repo(config_path: &Path, repo_path: &Path) -> io::Result {
 38 |     const URL: &str = "https://github.com/3rfaan/dotfiles";
 39 | 
 40 |     info!("Cloning into https://github.com/3rfaan/dotfiles...");
 41 | 
 42 |     if !config_path.exists() {
 43 |         fs::create_dir_all(config_path)?;
 44 |     }
 45 | 
 46 |     if repo_path.exists() {
 47 |         return Ok(DownloadStatus::Existing);
 48 |     }
 49 | 
 50 |     clone!(URL, repo_path)?;
 51 | 
 52 |     Ok(DownloadStatus::Success)
 53 | }
 54 | 
 55 | // Delete directories and files which are not needed to moved to ~/.config directory
 56 | pub fn cleanup_repo(paths: &Paths) -> io::Result<()> {
 57 |     let entries_to_delete: &[&str] = &[
 58 |         "arch_wallpaper.jpg",
 59 |         "preview_1.png",
 60 |         "preview_2.png",
 61 |         "preview_3.png",
 62 |         "preview_4.png",
 63 |         ".git",
 64 |         "README.md",
 65 |         "zsh",
 66 |     ];
 67 | 
 68 |     info!("Removing some directories and files which are not needed to be moved to ~/.config...");
 69 | 
 70 |     cleanup(paths, entries_to_delete)?;
 71 | 
 72 |     Ok(())
 73 | }
 74 | 
 75 | // Creates backup of all files and directories inside ~/.config and puts it inside ~/Documents/config_backup
 76 | pub fn create_backup(config_path: &Path, documents_path: &Path) -> io::Result {
 77 |     let backup_path: PathBuf = documents_path.join("config_backup");
 78 | 
 79 |     info!("Creating backup of your current ~/.config directory...");
 80 | 
 81 |     if backup_path.exists() {
 82 |         return Ok(BackupStatus::Existing);
 83 |     } else {
 84 |         fs::create_dir(&backup_path)?;
 85 |     }
 86 | 
 87 |     if let Err(error) = copy_recursively(config_path, backup_path) {
 88 |         error!(
 89 |             "Could not create backup directory at ~/Documents/backup",
 90 |             error
 91 |         );
 92 | 
 93 |         loop {
 94 |             prompt!("The theme can still be installed. Do you want to continue? [y/N]");
 95 | 
 96 |             let input: String = read_input()?;
 97 | 
 98 |             match parse_input(&input) {
 99 |                 UserInput::Yes => return Ok(BackupStatus::NoBackup),
100 |                 UserInput::No => return Err(error),
101 |                 UserInput::Other => prompt!("==> Please enter [y]es or [n]o!"),
102 |             }
103 |         }
104 |     }
105 | 
106 |     Ok(BackupStatus::Created)
107 | }
108 | 
109 | // Copy directories from ~/Downloads/arch-everforest to ~/.config recursively
110 | pub fn copy_config_dirs_recursively(src: &Path, dest: &Path) -> io::Result<()> {
111 |     fs::create_dir_all(dest)?;
112 | 
113 |     info!("Copying directories from ~/Downloads/arch-everforest to ~/.config...");
114 | 
115 |     copy_recursively(src, dest)?;
116 | 
117 |     Ok(())
118 | }
119 | 
120 | // Prompt for changing settings inside ~/.config/hypr/hyprland.conf
121 | pub fn change_settings(hypr_config: &Path) -> io::Result {
122 |     let mut input: String;
123 | 
124 |     let mut change_kb_layout: bool;
125 |     let mut layout_code: String = String::from("us");
126 | 
127 |     let change_nvidia_env_vars: bool;
128 | 
129 |     loop {
130 |         prompt!("Keyboard layout is currently set to [us]. Would you like to change it? [y/N]");
131 | 
132 |         input = read_input()?;
133 | 
134 |         match parse_input(&input) {
135 |             UserInput::Yes => {
136 |                 change_kb_layout = true;
137 |                 break;
138 |             }
139 |             UserInput::No => {
140 |                 change_kb_layout = false;
141 |                 break;
142 |             }
143 |             UserInput::Other => prompt!("==> Please enter [y]es or [n]o!"),
144 |         }
145 |     }
146 | 
147 |     if change_kb_layout {
148 |         match get_kb_layout_code() {
149 |             Ok(KBLayout::Change(code)) => layout_code = code,
150 |             Ok(KBLayout::Default) => change_kb_layout = false,
151 |             Err(error) => return Err(error),
152 |         }
153 |     }
154 | 
155 |     loop {
156 |         prompt!("Are you using a NVIDIA graphics card? [y/N]");
157 | 
158 |         input = read_input()?;
159 | 
160 |         match parse_input(&input) {
161 |             UserInput::Yes => {
162 |                 change_nvidia_env_vars = true;
163 |                 break;
164 |             }
165 |             UserInput::No => {
166 |                 change_nvidia_env_vars = false;
167 |                 break;
168 |             }
169 |             UserInput::Other => prompt!("==> Please enter [y]es or [n]o!"),
170 |         }
171 |     }
172 | 
173 |     if !change_kb_layout && !change_nvidia_env_vars {
174 |         return Ok(HyprConfig::Default);
175 |     }
176 | 
177 |     update_hypr_config(
178 |         hypr_config,
179 |         change_kb_layout,
180 |         change_nvidia_env_vars,
181 |         &layout_code,
182 |     )?;
183 | 
184 |     Ok(HyprConfig::Modified)
185 | }
186 | 
187 | // Helper function for `change_settings()` to modify Hyprland config file
188 | fn update_hypr_config(
189 |     hypr_config: &Path,
190 |     change_kb_layout: bool,
191 |     change_nvidia_env_vars: bool,
192 |     layout_code: &str,
193 | ) -> io::Result {
194 |     if layout_code == "us" && !change_nvidia_env_vars {
195 |         return Ok(HyprConfig::Default);
196 |     }
197 | 
198 |     // Path to Hyprland config file
199 |     let hypr_config_file: File = File::open(hypr_config)?;
200 |     let hypr_config_reader: BufReader = BufReader::new(hypr_config_file);
201 | 
202 |     let temp_file_path: &Path = Path::new("./hyprland.conf");
203 |     let temp_file: File = File::create(temp_file_path)?;
204 |     let mut temp_file_stream: BufWriter = BufWriter::new(temp_file);
205 | 
206 |     let old_layout: &str = "kb_layout = us";
207 |     let new_layout: String = format!("kb_layout = {}", layout_code);
208 | 
209 |     static NVIDIA_ENV_VARS_RE: Lazy = Lazy::new(|| Regex::new(r"^#(env = .+)$").unwrap());
210 | 
211 |     info!("Modifying Hypr config with your settings...");
212 | 
213 |     for line in hypr_config_reader.lines() {
214 |         let mut line: String = line?;
215 | 
216 |         if change_kb_layout && line.contains(old_layout) {
217 |             line = line.replace(old_layout, &new_layout);
218 | 
219 |             println!(
220 |                 "{} {}",
221 |                 "==> Changed the following line in Hypr config file:".green(),
222 |                 line.trim().green().bold()
223 |             );
224 |         }
225 | 
226 |         if change_nvidia_env_vars && NVIDIA_ENV_VARS_RE.is_match(&line) {
227 |             line = NVIDIA_ENV_VARS_RE.replace(&line, "$1").to_string();
228 |         }
229 | 
230 |         temp_file_stream.write_all(line.as_bytes())?;
231 |         temp_file_stream.write_all(b"\n")?;
232 |     }
233 | 
234 |     temp_file_stream.flush()?;
235 | 
236 |     if temp_file_path.exists() {
237 |         fs::copy(temp_file_path, hypr_config)?;
238 |         fs::remove_file(temp_file_path)?;
239 | 
240 |         success!("==> Copied new Hypr config file to ~/.config/Hypr/hyprland.conf");
241 |         success!("==> Removed temporary file");
242 |     }
243 | 
244 |     Ok(HyprConfig::Modified)
245 | }
246 | 
247 | pub fn install_cli_utilities(home_path: &Path, config_path: &Path) -> io::Result {
248 |     let zsh_path: PathBuf = home_path.join(".zsh");
249 |     let yazi_path: PathBuf = config_path.join("yazi/plugins");
250 | 
251 |     info!("Installing CLI utilies");
252 | 
253 |     if !zsh_path.exists() {
254 |         fs::create_dir_all(&zsh_path)?;
255 |     }
256 | 
257 |     if !zsh_path.join("zsh-autosuggestions").exists() {
258 |         clone!(
259 |             "https://github.com/zsh-users/zsh-autosuggestions",
260 |             &zsh_path.join("zsh-autosuggestions")
261 |         )?;
262 | 
263 |         success!("==> Successfully cloned zsh-autosuggestions");
264 |     }
265 | 
266 |     if !zsh_path.join("zsh-syntax-highlighting").exists() {
267 |         clone!(
268 |             "https://github.com/zsh-users/zsh-syntax-highlighting.git",
269 |             &zsh_path.join("zsh-syntax-highlighting")
270 |         )?;
271 | 
272 |         success!("==> Successfully cloned zsh-syntax-highlighting");
273 |     }
274 | 
275 |     if !yazi_path.exists() {
276 |         fs::create_dir_all(&yazi_path)?;
277 |     }
278 | 
279 |     let yazi_packages: &[&str] = &[
280 |         "dedukun/bookmarks",
281 |         "dedukun/relative-motions",
282 |         "yazi-rs/flavors:catppuccin-macchiato",
283 |         "yazi-rs/plugins:full-border",
284 |     ];
285 | 
286 |     if command_exists("ya") {
287 |         for &package in yazi_packages {
288 |             Command::new("ya")
289 |                 .arg("pack")
290 |                 .arg("-a")
291 |                 .arg(package)
292 |                 .output()?;
293 |         }
294 |     } else {
295 |         warning!("Could not install ya packages for yazi. Make sure ya is installed and try to install them manually");
296 |     }
297 | 
298 |     Ok(DownloadStatus::Success)
299 | }
300 | 
301 | pub fn after_install(repo_path: &Path) -> io::Result<()> {
302 |     info!("Removing ~/Downloads/arch-everforest repo");
303 | 
304 |     if repo_path.exists() {
305 |         fs::remove_dir_all(repo_path)?;
306 |     }
307 | 
308 |     success!("==> Removed repo successfully".green());
309 | 
310 |     info!("Installation succeeded! 🎉");
311 | 
312 |     tip!("Tip: After this installation you have to restart Hyprland by pressing  +  + E");
313 |     tip!("Tip: To change your wallpaper change the path to another picture inside ~/.config/hypr/hyprpaper.conf");
314 | 
315 |     Ok(())
316 | }
317 | 
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
 1 | use crate::utils::{contents::print_installer_info, helper_functions::pause, types::*};
 2 | use colored::Colorize;
 3 | use installer::*;
 4 | use std::io;
 5 | 
 6 | mod installer;
 7 | mod utils;
 8 | 
 9 | fn main() -> io::Result<()> {
10 |     let paths: Paths = Paths::build(); // All paths needed
11 | 
12 |     // Installer
13 |     print_installer_info();
14 | 
15 |     match installation_prompt() {
16 |         Ok(Installation::Proceed) => success!("==> Proceeding with installation..."),
17 |         Ok(Installation::Exit) => {
18 |             info!("==> Exiting...");
19 |             return Ok(());
20 |         }
21 |         Err(error) => return Err(error),
22 |     }
23 | 
24 |     match clone_repo(&paths.config, &paths.repo) {
25 |         Ok(DownloadStatus::Success) => {
26 |             success!("==> Successfully cloned Github repo into ~/Downloads")
27 |         }
28 |         Ok(DownloadStatus::Existing) => {
29 |             success!("==> Repo has already been cloned into ~/Downloads")
30 |         }
31 |         Err(error) => {
32 |             error!(
33 |                 "Could not clone Github repo into ~/Downloads. Exiting...",
34 |                 error
35 |             );
36 |             return Err(error);
37 |         }
38 |     }
39 | 
40 |     match cleanup_repo(&paths) {
41 |         Ok(()) => success!("==> Cleanup was successful"),
42 |         Err(error) => error!(
43 |             "There was an error while cleaning up ~/Downloads/arch-everforest",
44 |             error
45 |         ),
46 |     }
47 | 
48 |     match create_backup(&paths.config, &paths.documents) {
49 |         Ok(BackupStatus::Created) => {
50 |             success!("==> Successfully created backup at ~/Documents/backup")
51 |         }
52 |         Ok(BackupStatus::Existing) => {
53 |             success!("==> There is already a backup in ~/Documents/config_backup")
54 |         }
55 |         Ok(BackupStatus::NoBackup) => success!("==> Continuing installation without backup"),
56 |         Err(error) => {
57 |             error!(
58 |                 "Could not create a backup of your ~/.config directory:",
59 |                 error
60 |             );
61 |             return Err(error);
62 |         }
63 |     }
64 | 
65 |     match copy_config_dirs_recursively(&paths.repo, &paths.config) {
66 |         Ok(()) => {
67 |             success!("==> Successfully copied config files to ~/.config!");
68 |         }
69 |         Err(error) => {
70 |             error!("Could not copy files to ~/.config. Exiting...", error);
71 |             return Err(error);
72 |         }
73 |     }
74 | 
75 |     match change_settings(&paths.hypr_config) {
76 |         Ok(HyprConfig::Modified) => success!("==> Successfully modified Hypr config!"),
77 |         Ok(HyprConfig::Default) => success!("==> Using default Hypr config"),
78 |         Err(error) => error!("Modifying Hypr config failed", error),
79 |     }
80 | 
81 |     match install_cli_utilities(&paths.home, &paths.config) {
82 |         Ok(DownloadStatus::Success) => success!("==> Successfully installed CLI utilities"),
83 |         Ok(_) => {}
84 |         Err(error) => error!("Installing CLI utilities failed", error),
85 |     }
86 | 
87 |     match after_install(&paths.repo) {
88 |         Ok(()) => pause().unwrap(),
89 |         Err(error) => error!("", error),
90 |     }
91 | 
92 |     Ok(())
93 | }
94 | 
--------------------------------------------------------------------------------
/src/utils/contents.rs:
--------------------------------------------------------------------------------
  1 | use colored::Colorize;
  2 | use std::collections::BTreeMap;
  3 | 
  4 | pub fn print_installer_info() {
  5 |     const LOGO: &str = "
  6 | 
  7 | ██╗  ██╗██╗   ██╗██████╗ ██████╗ ███████╗ ██████╗ ██████╗ ███████╗███████╗████████╗
  8 | ██║  ██║╚██╗ ██╔╝██╔══██╗██╔══██╗██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔════╝╚══██╔══╝
  9 | ███████║ ╚████╔╝ ██████╔╝██████╔╝█████╗  ██║   ██║██████╔╝█████╗  ███████╗   ██║   
 10 | ██╔══██║  ╚██╔╝  ██╔═══╝ ██╔══██╗██╔══╝  ██║   ██║██╔══██╗██╔══╝  ╚════██║   ██║   
 11 | ██║  ██║   ██║   ██║     ██║  ██║██║     ╚██████╔╝██║  ██║███████╗███████║   ██║   
 12 | ╚═╝  ╚═╝   ╚═╝   ╚═╝     ╚═╝  ╚═╝╚═╝      ╚═════╝ ╚═╝  ╚═╝╚══════╝╚══════╝   ╚═╝   
 13 | 
 14 | ";
 15 |     const TITLE: &str = "Auto-ricer for Hyprland on Arch Linux";
 16 |     const AUTHOR: &str = "Arfan Zubi";
 17 |     const THEME: &str = "Catppuccin";
 18 |     const LICENSE: &str = "GNU General Public License";
 19 | 
 20 |     println!(
 21 |         "{logo}\n\
 22 |         {title}\n\
 23 |         {} {author}\n\
 24 |         {} {theme}\n\
 25 |         {} {license}\n",
 26 |         "Author:".yellow(),
 27 |         "Theme:".bright_black(),
 28 |         "License:".bright_black(),
 29 |         logo = LOGO.bright_black(),
 30 |         title = TITLE.to_uppercase().green().bold(),
 31 |         author = AUTHOR.yellow(),
 32 |         theme = THEME.bright_black(),
 33 |         license = LICENSE.bright_black()
 34 |     );
 35 | }
 36 | 
 37 | pub fn get_kb_layouts() -> BTreeMap<&'static str, &'static str> {
 38 |     BTreeMap::from([
 39 |         ("al", "Albanian"),
 40 |         ("et", "Amharic"),
 41 |         ("am", "Armenian"),
 42 |         ("ara", "Arabic"),
 43 |         ("eg", "Arabic (Egypt)"),
 44 |         ("iq", "Arabic (Iraq)"),
 45 |         ("ma", "Arabic (Morocco)"),
 46 |         ("sy", "Arabic (Syria)"),
 47 |         ("az", "Azerbaijani"),
 48 |         ("ml", "Bambara"),
 49 |         ("bd", "Bangla"),
 50 |         ("by", "Belarusian"),
 51 |         ("be", "Belgian"),
 52 |         ("dz", "Berber (Algeria, Latin)"),
 53 |         ("ba", "Bosnian"),
 54 |         ("brai", "Braille"),
 55 |         ("bg", "Bulgarian"),
 56 |         ("mm", "Burmese"),
 57 |         ("cn", "Chinese"),
 58 |         ("hr", "Croatian"),
 59 |         ("cz", "Czech"),
 60 |         ("dk", "Danish"),
 61 |         ("af", "Dari"),
 62 |         ("mv", "Dhivehi"),
 63 |         ("nl", "Dutch"),
 64 |         ("bt", "Dzongkha"),
 65 |         ("au", "English (Australia)"),
 66 |         ("cm", "English (Cameroon)"),
 67 |         ("gh", "English (Ghana)"),
 68 |         ("nz", "English (New Zealand)"),
 69 |         ("ng", "English (Nigeria)"),
 70 |         ("za", "English (South Africa)"),
 71 |         ("gb", "English (UK)"),
 72 |         ("us", "English (US)"),
 73 |         ("epo", "Esperanto"),
 74 |         ("ee", "Estonian"),
 75 |         ("fo", "Faroese"),
 76 |         ("ph", "Filipino"),
 77 |         ("fi", "Finnish"),
 78 |         ("fr", "French"),
 79 |         ("ca", "French (Canada)"),
 80 |         ("cd", "French (Democratic Republic of the Congo)"),
 81 |         ("tg", "French (Togo)"),
 82 |         ("ge", "Georgian"),
 83 |         ("de", "German"),
 84 |         ("at", "German (Austria)"),
 85 |         ("ch", "German (Switzerland)"),
 86 |         ("gr", "Greek"),
 87 |         ("il", "Hebrew"),
 88 |         ("hu", "Hungarian"),
 89 |         ("is", "Icelandic"),
 90 |         ("in", "Indian"),
 91 |         ("id", "Indonesian (Latin)"),
 92 |         ("ie", "Irish"),
 93 |         ("it", "Italian"),
 94 |         ("jp", "Japanese"),
 95 |         ("kz", "Kazakh"),
 96 |         ("kh", "Khmer (Cambodia)"),
 97 |         ("kr", "Korean"),
 98 |         ("kg", "Kyrgyz"),
 99 |         ("la", "Lao"),
100 |         ("lv", "Latvian"),
101 |         ("lt", "Lithuanian"),
102 |         ("mk", "Macedonian"),
103 |         ("my", "Malay (Jawi, Arabic Keyboard)"),
104 |         ("mt", "Maltese"),
105 |         ("md", "Moldavian"),
106 |         ("mn", "Mongolian"),
107 |         ("me", "Montenegrin"),
108 |         ("np", "Nepali"),
109 |         ("gn", "N'Ko (AZERTY)"),
110 |         ("no", "Norwegian"),
111 |         ("ir", "Persian"),
112 |         ("pl", "Polish"),
113 |         ("pt", "Portuguese"),
114 |         ("br", "Portuguese (Brazil)"),
115 |         ("ro", "Romanian"),
116 |         ("ru", "Russian"),
117 |         ("rs", "Serbian"),
118 |         ("lk", "Sinhala (phonetic)"),
119 |         ("sk", "Slovak"),
120 |         ("si", "Slovenian"),
121 |         ("es", "Spanish"),
122 |         ("latam", "Spanish (Latin American)"),
123 |         ("ke", "Swahili (Kenya)"),
124 |         ("tz", "Swahili (Tanzania)"),
125 |         ("se", "Swedish"),
126 |         ("tw", "Taiwanese"),
127 |         ("tj", "Tajik"),
128 |         ("th", "Thai"),
129 |         ("bw", "Tswana"),
130 |         ("tm", "Turkmen"),
131 |         ("tr", "Turkish"),
132 |         ("ua", "Ukrainian"),
133 |         ("pk", "Urdu (Pakistan)"),
134 |         ("uz", "Uzbek"),
135 |         ("vn", "Vietnamese"),
136 |         ("sn", "Wolof"),
137 |     ])
138 | }
139 | 
--------------------------------------------------------------------------------
/src/utils/helper_functions.rs:
--------------------------------------------------------------------------------
  1 | use crate::{prompt, success, utils::contents::get_kb_layouts, KBLayout, Paths, UserInput};
  2 | use colored::Colorize;
  3 | use std::{
  4 |     collections::BTreeMap,
  5 |     fs::{self, DirEntry, FileType},
  6 |     io::{self, stdin, stdout, Read, Write},
  7 |     path::Path,
  8 |     process::Command,
  9 | };
 10 | 
 11 | pub fn read_input() -> io::Result {
 12 |     let mut input: String = String::new();
 13 | 
 14 |     io::stdout().flush()?;
 15 |     io::stdin().read_line(&mut input)?;
 16 | 
 17 |     Ok(input.trim().to_lowercase())
 18 | }
 19 | 
 20 | pub fn parse_input(input: &str) -> UserInput {
 21 |     match input {
 22 |         "y" | "yes" => UserInput::Yes,
 23 |         "n" | "no" | "" => UserInput::No,
 24 |         _ => UserInput::Other,
 25 |     }
 26 | }
 27 | 
 28 | pub fn pause() -> io::Result<()> {
 29 |     prompt!("Press Enter to close this installer... ");
 30 | 
 31 |     stdout().flush()?;
 32 |     let _ = stdin().read(&mut [0])?;
 33 | 
 34 |     Ok(())
 35 | }
 36 | 
 37 | pub fn copy_recursively(src: impl AsRef, dest: impl AsRef) -> io::Result<()> {
 38 |     for entry in fs::read_dir(src)? {
 39 |         let entry: DirEntry = entry?;
 40 |         let filetype: FileType = entry.file_type()?;
 41 | 
 42 |         if filetype.is_dir() {
 43 |             fs::create_dir_all(dest.as_ref().join(entry.file_name()))?;
 44 |             copy_recursively(entry.path(), dest.as_ref().join(entry.file_name()))?;
 45 |         } else {
 46 |             fs::copy(entry.path(), dest.as_ref().join(entry.file_name()))?;
 47 |         }
 48 |     }
 49 | 
 50 |     Ok(())
 51 | }
 52 | 
 53 | pub fn cleanup<'a>(paths: &Paths, entries_to_delete: impl AsRef<[&'a str]>) -> io::Result<()> {
 54 |     for entry in fs::read_dir(&paths.repo)? {
 55 |         let entry: DirEntry = entry?;
 56 |         let filetype: FileType = entry.file_type()?;
 57 | 
 58 |         if entry.file_name() == "arch_wallpaper.jpg" {
 59 |             if !paths.wallpapers.exists() {
 60 |                 fs::create_dir_all(&paths.wallpapers)?;
 61 |             }
 62 | 
 63 |             fs::copy(entry.path(), paths.wallpapers.join("arch_wallpaper.jpg"))?;
 64 | 
 65 |             success!("==> Successfully set wallpaper in ~/Documents/wallpapers");
 66 |         }
 67 | 
 68 |         if entry.file_name() == "zsh" {
 69 |             fs::copy(entry.path().join(".zshrc"), paths.home.join(".zshrc"))?;
 70 | 
 71 |             success!("==> Copied .zshrc to ~/.zshrc before removing zsh directory");
 72 |         }
 73 | 
 74 |         if entries_to_delete.as_ref().contains(
 75 |             &entry
 76 |                 .file_name()
 77 |                 .to_str()
 78 |                 .expect("Could not get directory name inside config directories"),
 79 |         ) {
 80 |             if filetype.is_dir() {
 81 |                 fs::remove_dir_all(entry.path())?;
 82 |             } else {
 83 |                 fs::remove_file(entry.path())?;
 84 |             }
 85 | 
 86 |             println!(
 87 |                 "{} {}",
 88 |                 "==> Successfully removed:".green(),
 89 |                 entry
 90 |                     .file_name()
 91 |                     .to_str()
 92 |                     .expect("Could not get file name")
 93 |                     .green()
 94 |                     .bold()
 95 |             );
 96 |         }
 97 |     }
 98 | 
 99 |     Ok(())
100 | }
101 | 
102 | pub fn get_kb_layout_code() -> io::Result {
103 |     let mut input: String;
104 |     let kb_layouts: BTreeMap<&str, &str> = get_kb_layouts();
105 | 
106 |     loop {
107 |         prompt!("Please enter a valid keyboard layout. Press l to see a [l]ist of available options or q to [q]uit:");
108 | 
109 |         input = read_input()?;
110 | 
111 |         match input.as_str() {
112 |             "q" | "quit" => return Ok(KBLayout::Default),
113 |             "l" | "list" => kb_layouts
114 |                 .iter()
115 |                 .for_each(|(code, lang)| println!("{} -> {}", *code, *lang)),
116 |             _ => {
117 |                 if kb_layouts.iter().any(|(&code, _)| *code == input) {
118 |                     break;
119 |                 } else {
120 |                     continue;
121 |                 }
122 |             }
123 |         }
124 |     }
125 | 
126 |     Ok(KBLayout::Change(input))
127 | }
128 | 
129 | pub fn command_exists(command: &str) -> bool {
130 |     Command::new(command).arg("-v").output().is_ok()
131 | }
132 | 
--------------------------------------------------------------------------------
/src/utils/macros.rs:
--------------------------------------------------------------------------------
 1 | #[macro_export]
 2 | macro_rules! clone {
 3 |     ($repo:expr,$dest:expr) => {
 4 |         Command::new("git")
 5 |             .arg("clone")
 6 |             .arg($repo)
 7 |             .arg($dest)
 8 |             .output()
 9 |     };
10 | }
11 | 
12 | #[macro_export]
13 | macro_rules! info {
14 |     ($msg:expr) => {
15 |         println!("\n{}", $msg.green().bold())
16 |     };
17 | }
18 | 
19 | #[macro_export]
20 | macro_rules! prompt {
21 |     ($msg:expr) => {
22 |         print!("\n{} ", $msg.yellow().bold())
23 |     };
24 | }
25 | 
26 | #[macro_export]
27 | macro_rules! warning {
28 |     ($msg:expr) => {
29 |         println!("\n{}", $msg.red().bold())
30 |     };
31 | }
32 | 
33 | #[macro_export]
34 | macro_rules! success {
35 |     ($msg:expr) => {
36 |         println!("{}", $msg.green())
37 |     };
38 | }
39 | 
40 | #[macro_export]
41 | macro_rules! error {
42 |     ($msg:expr,$err:ident) => {
43 |         eprintln!("{} {} {}", $msg.red().bold(), "->".red().bold(), $err)
44 |     };
45 |     ($msg:expr) => {
46 |         eprintln!("{}", $msg.red().bold())
47 |     };
48 | }
49 | 
50 | #[macro_export]
51 | macro_rules! tip {
52 |     ($msg:expr) => {
53 |         println!("{}", $msg.bright_black().bold())
54 |     };
55 | }
56 | 
--------------------------------------------------------------------------------
/src/utils/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod contents;
2 | pub mod helper_functions;
3 | pub mod macros;
4 | pub mod types;
5 | 
--------------------------------------------------------------------------------
/src/utils/types.rs:
--------------------------------------------------------------------------------
 1 | use std::path::PathBuf;
 2 | 
 3 | pub enum UserInput {
 4 |     Yes,
 5 |     No,
 6 |     Other,
 7 | }
 8 | 
 9 | pub enum Installation {
10 |     Proceed,
11 |     Exit,
12 | }
13 | 
14 | pub enum BackupStatus {
15 |     Created,
16 |     Existing,
17 |     NoBackup,
18 | }
19 | 
20 | pub enum DownloadStatus {
21 |     Success,
22 |     Existing,
23 | }
24 | 
25 | pub enum HyprConfig {
26 |     Modified,
27 |     Default,
28 | }
29 | 
30 | pub enum KBLayout {
31 |     Change(String),
32 |     Default,
33 | }
34 | 
35 | pub struct Paths {
36 |     pub home: PathBuf,
37 |     pub config: PathBuf,
38 |     pub documents: PathBuf,
39 |     pub repo: PathBuf,
40 |     pub hypr_config: PathBuf,
41 |     pub wallpapers: PathBuf,
42 | }
43 | 
44 | impl Paths {
45 |     pub fn build() -> Self {
46 |         Self {
47 |             home: dirs::home_dir().expect("Cannot get ~ path"),
48 |             config: dirs::config_dir().expect("Cannot get ~/.config path"),
49 |             documents: dirs::document_dir().expect("Cannot get ~/Documents path"),
50 |             repo: dirs::download_dir()
51 |                 .expect("Cannot get ~/Downloads path")
52 |                 .join("dotfiles"),
53 |             hypr_config: dirs::config_dir()
54 |                 .expect("Cannot get ~/.config path")
55 |                 .join("hypr/hyprland.conf"),
56 |             wallpapers: dirs::document_dir()
57 |                 .expect("Cannot get ~/Documents path")
58 |                 .join("wallpapers"),
59 |         }
60 |     }
61 | }
62 | 
--------------------------------------------------------------------------------