From 5083bb16d8a1a28ddbbaf5c548ca45dd32a5d1ce Mon Sep 17 00:00:00 2001 From: Nitestack <74626967+Nitestack@users.noreply.github.com> Date: Sat, 23 Mar 2024 16:50:00 +0100 Subject: [PATCH] feat: ready for 0.1.0 --- Cargo.lock | 29 +++++++++++++++++ Cargo.toml | 4 +++ README.md | 29 +++++++++++++++++ src/cli.rs | 21 ++++++------ src/cmds/add.rs | 65 +++++++++++++++++++++++-------------- src/cmds/list.rs | 18 +++++------ src/cmds/remove.rs | 74 +++++++++++++++++++++++------------------- src/cmds/select.rs | 64 ++++++++++++++++++------------------- src/main.rs | 17 ++++------ src/store.rs | 69 ++++++++++++++++++++++++--------------- src/utils/logger.rs | 24 +++++++------- src/utils/mod.rs | 78 ++++++++++++++++++++++++++++++++------------- 12 files changed, 311 insertions(+), 181 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74201ca..f4c9628 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "clap" version = "4.5.3" @@ -186,6 +192,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctrlc" +version = "3.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" +dependencies = [ + "nix", + "windows-sys 0.52.0", +] + [[package]] name = "dirs" version = "5.0.1" @@ -353,6 +369,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "num-traits" version = "0.2.18" @@ -376,6 +404,7 @@ dependencies = [ "cliclack", "colored", "console", + "ctrlc", "dirs", "merge", "prettytable-rs", diff --git a/Cargo.toml b/Cargo.toml index b042824..07c3c2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,10 @@ description = "A CLI to easily switch between multiple Neovim configuration envi readme = "README.md" keywords = ["neovim", "nvim", "switcher", "cli"] categories = ["command-line-utilities"] +homepage = "https://github.com/Nitestack/nvim-switcher" repository = "https://github.com/Nitestack/nvim-switcher" +license = "Apache-2.0" +authors = ["Nitestack"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -15,6 +18,7 @@ clap = { version = "4.5.3", features = ["derive"] } cliclack = "0.1.13" colored = "2.1.0" console = "0.15.8" +ctrlc = "3.4.4" dirs = "5.0.1" merge = "0.1.0" prettytable-rs = "0.10.0" diff --git a/README.md b/README.md index 2375b71..0207b44 100644 --- a/README.md +++ b/README.md @@ -1 +1,30 @@ # Neovim Configuration Switcher +Neovim Configuration Switcher (short `nvims`) is a CLI to easily switch between multiple Neovim configuration environments. + +## 🧭 Features +- Use fuzzy search to switch between Neovim configurations +- Add and remove Neovim configurations +## 📦 Requirements +- [rustup](https://rustup.rs) +- [git](https://git-scm.com/downloads) +- [Neovim](https://github.com/neovim/neovim/blob/master/INSTALL.md) +- [fzf](https://github.com/junegunn/fzf) + +## 🔧 Installation + +### crates.io +```sh +cargo install nvim-switcher +``` + +### GitHub +```sh +cargo install --git https://github.com/Nitestack/nvim-switcher +``` + +## 🚀 Usage + +Use `nvims --help` to see all available options. + +## 📖 License +This project is licensed under the Apache-2.0 license. diff --git a/src/cli.rs b/src/cli.rs index 37ab866..ada0ce9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,4 @@ -use crate::{ - cmds::*, - utils::{is_neovim_installed, print_outro_cancel}, -}; +use crate::{cmds, utils}; use clap::{Parser, Subcommand}; #[derive(Parser, Debug)] @@ -24,18 +21,20 @@ enum Commands { } pub fn run_cli() { + utils::print_intro(None); let cli = Cli::parse(); - if !is_neovim_installed() { - print_outro_cancel("Neovim not installed! Please install Neovim and try again."); - return; + if !utils::is_neovim_installed() { + utils::print_outro_cancel(Some( + "Neovim not installed! Please install Neovim and try again.", + )); } match &cli.command { - Some(Commands::Add {}) => add::add_config(), - Some(Commands::Remove {}) => remove::remove_config(), + Some(Commands::Add {}) => cmds::add::add_config(), + Some(Commands::Remove {}) => cmds::remove::remove_config(), Some(Commands::Config {}) => {} - Some(Commands::List {}) => list::list_configs(), - None => select::select_config(), + Some(Commands::List {}) => cmds::list::list_configs(), + None => cmds::select::select_config(), } } diff --git a/src/cmds/add.rs b/src/cmds/add.rs index b936031..d18741b 100644 --- a/src/cmds/add.rs +++ b/src/cmds/add.rs @@ -1,18 +1,15 @@ -use cliclack::{input, spinner}; +use cliclack; use std::process::Command; -use crate::store::{get_config, set_config, Config, NeovimConfig}; -use crate::utils::{get_nvim_config_dir, is_valid_github_url, print_intro, print_outro}; +use crate::{store, utils}; pub fn add_config() { - let configs_dir = get_nvim_config_dir(None); + let configs_dir = utils::get_nvim_config_dir(None); - print_intro(None); - - let name: String = input("Name") + let name: String = match cliclack::input("Name") .placeholder("Configuration Name") .validate(|input: &String| { - if get_config() + if store::get_config() .configs .iter() .any(|nvim_config| nvim_config.name.to_lowercase() == *input.to_lowercase()) @@ -23,54 +20,71 @@ pub fn add_config() { } }) .interact() - .unwrap(); - let repo_url: String = input("GitHub Repository URL") + { + Ok(name) => name, + Err(_) => utils::print_outro_cancel(Some("Failed to get configuration name")), + }; + let repo_url: String = match cliclack::input("GitHub Repository URL") .placeholder("https://github.com/user/repo") .validate(|input: &String| { - if !is_valid_github_url(input) { + if !utils::is_valid_github_url(input) { Err("URL must be a valid GitHub repository URL") } else { Ok(()) } }) .interact() - .unwrap(); - let nvim_dir_name: String = input(format!( + { + Ok(repo_url) => repo_url, + Err(_) => utils::print_outro_cancel(Some("Failed to get GitHub Repository URL")), + }; + let nvim_dir_name: String = match cliclack::input(format!( "{} ({})", "Neovim Directory Name", - configs_dir.join("").to_str().unwrap() + match configs_dir.join("").to_str() { + Some(s) => s, + None => utils::print_outro_cancel(None), + } )) .placeholder("configuration-name") .validate(|input: &String| { - if get_config() + if store::get_config() .configs .iter() .any(|nvim_config| nvim_config.nvim_dir_name == *input) { Err("Directory name already exists") + } else if !utils::is_valid_dir_name(input) { + Err("Directory name is not valid") } else { Ok(()) } }) .interact() - .unwrap(); + { + Ok(nvim_dir_name) => nvim_dir_name, + Err(_) => utils::print_outro_cancel(Some("Failed to get Neovim Directory Name")), + }; - let mut spinner = spinner(); + let mut spinner = cliclack::spinner(); spinner.start(format!("Cloning {} from '{}'...", name, repo_url).as_str()); - Command::new("git") + if Command::new("git") .arg("clone") .arg(&repo_url) .arg(configs_dir.join(&nvim_dir_name)) .arg("--depth") .arg("1") .output() - .expect("Failed to clone repository"); + .is_err() + { + utils::print_outro_cancel(Some("Failed to clone repository")) + } spinner.stop(format!("Cloned {} from '{}'", name, repo_url).as_str()); spinner.start("Editing user config..."); - set_config( - Config { - configs: vec![NeovimConfig { + store::set_config( + store::Config { + configs: vec![store::NeovimConfig { name: name.clone(), repo_url: repo_url.clone(), nvim_dir_name: nvim_dir_name.clone(), @@ -80,12 +94,15 @@ pub fn add_config() { ); spinner.stop("User config saved"); - print_outro(Some( + utils::print_outro(Some( format!( "Added {} ({}) to '{}'", name, repo_url, - configs_dir.join(nvim_dir_name).to_str().unwrap() + match configs_dir.join(nvim_dir_name).to_str() { + Some(s) => s, + None => utils::print_outro_cancel(None), + } ) .as_str(), )); diff --git a/src/cmds/list.rs b/src/cmds/list.rs index 5ee9535..20e8732 100644 --- a/src/cmds/list.rs +++ b/src/cmds/list.rs @@ -1,10 +1,12 @@ use prettytable::{color, Attr, Cell, Row, Table}; -use crate::{store::get_config, utils::get_nvim_config_dir}; +use crate::{store, utils}; pub fn list_configs() { - let config = get_config(); - let config_dir = get_nvim_config_dir(None); + utils::ensure_non_empty_config(); + + let config = store::get_config(); + let config_dir = utils::get_nvim_config_dir(None); let mut table = Table::new(); table.add_row(Row::new(vec![ @@ -18,12 +20,10 @@ pub fn list_configs() { for nvim_config in config.configs.iter() { table.add_row(Row::new(vec![ Cell::new(&nvim_config.name), - Cell::new( - config_dir - .join(&nvim_config.nvim_dir_name) - .to_str() - .unwrap(), - ), + Cell::new(match config_dir.join(&nvim_config.nvim_dir_name).to_str() { + Some(s) => s, + None => utils::print_outro_cancel(None), + }), Cell::new(&nvim_config.repo_url), ])); } diff --git a/src/cmds/remove.rs b/src/cmds/remove.rs index 5adea91..44bc5e8 100644 --- a/src/cmds/remove.rs +++ b/src/cmds/remove.rs @@ -1,21 +1,15 @@ -use cliclack::{select, spinner}; +use cliclack; -use crate::{ - store::{get_config, set_config}, - utils::{get_nvim_config_dir, print_empty_configurations, print_intro, print_outro}, -}; +use crate::{store, utils}; use std::{env, fs, ops::Add}; pub fn remove_config() { - if print_empty_configurations() { - return; - } - print_intro(None); + utils::ensure_non_empty_config(); - let config = get_config(); + let config = store::get_config(); - let selected_key = select("Select Neovim Distribution") + let selected_key = match cliclack::select("Select Neovim Configuration") .items( &config .configs @@ -24,15 +18,21 @@ pub fn remove_config() { .collect::>(), ) .interact() - .unwrap(); + { + Ok(selected_key) => selected_key, + Err(_) => utils::print_outro_cancel(Some("Failed to get selected configuration")), + }; - let selected_item = &config + let selected_item = match config .configs .iter() .find(|s| s.nvim_dir_name == selected_key) - .expect("Failed to find selected item"); + { + Some(selected_item) => selected_item, + None => utils::print_outro_cancel(None), + }; - let mut spinner = spinner(); + let mut spinner = cliclack::spinner(); spinner.start( format!( "Removing config, data, cache and state for {}...", @@ -46,27 +46,32 @@ pub fn remove_config() { selected_item.nvim_dir_name.clone() }; - fs::remove_dir_all(get_nvim_config_dir(Some(&selected_item.nvim_dir_name))).ok(); - fs::remove_dir_all( - dirs::data_local_dir() - .expect("Failed to get data directory") - .join(&dir_name), - ) + fs::remove_dir_all(utils::get_nvim_config_dir(Some( + &selected_item.nvim_dir_name, + ))) + .ok(); + fs::remove_dir_all(match dirs::data_local_dir() { + Some(data_local_dir) => data_local_dir.join(&dir_name), + None => utils::print_outro_cancel(None), + }) .ok(); fs::remove_dir_all({ - let cache_dir = dirs::cache_dir().expect("Failed to get cache directory"); if env::consts::OS == "windows" { - dirs::data_local_dir() - .expect("Failed to get temp directory") - .join("Temp") - .join(&dir_name) + match dirs::data_local_dir() { + Some(data_local_dir) => data_local_dir.join("Temp").join(dir_name), + None => utils::print_outro_cancel(None), + } } else { - cache_dir.join(&dir_name) + let cache_dir = match dirs::cache_dir() { + Some(cache_dir) => cache_dir, + None => utils::print_outro_cancel(None), + }; + cache_dir.join(dir_name) } }) .ok(); - //TODO: As long as `state_dir` returns nothing on Windows, this should work. + // INFO: As long as `state_dir` returns nothing on Windows, this should work. if let Some(state_dir) = dirs::state_dir() { fs::remove_dir_all(state_dir.join(&selected_item.nvim_dir_name)).ok(); } @@ -75,19 +80,22 @@ pub fn remove_config() { spinner.start("Editing user config..."); let mut new_config = config.clone(); new_config.configs.swap_remove( - config + match config .configs .iter() .position(|s| s.nvim_dir_name == selected_item.nvim_dir_name) - .expect("Failed to find selected item"), + { + Some(position) => position, + None => utils::print_outro_cancel(None), + }, ); - set_config(new_config, true); + store::set_config(new_config, true); spinner.stop("User config saved"); - print_outro(Some( + utils::print_outro(Some( format!( "Removed {} ({})", - &selected_item.name, &selected_item.repo_url, + selected_item.name, selected_item.repo_url, ) .as_str(), )); diff --git a/src/cmds/select.rs b/src/cmds/select.rs index c835484..3fe640a 100644 --- a/src/cmds/select.rs +++ b/src/cmds/select.rs @@ -1,11 +1,4 @@ -// use cliclack::select; - -use crate::{ - store::get_config, - utils::{ - is_fzf_installed, print_empty_configurations, print_intro, print_outro, print_outro_cancel, - }, -}; +use crate::{store, utils}; use std::{ io::{Read, Write}, @@ -13,17 +6,13 @@ use std::{ }; pub fn select_config() { - if !is_fzf_installed() { - print_intro(None); - print_outro_cancel("fzf not installed! Please install fzf and try again."); - return; + if !utils::is_fzf_installed() { + utils::print_outro_cancel(Some("fzf not installed! Please install fzf and try again.")); } - if print_empty_configurations() { - return; - } + utils::ensure_non_empty_config(); - let config = get_config(); + let config = store::get_config(); let items = config .configs @@ -32,8 +21,8 @@ pub fn select_config() { .collect::>() .join("\n"); - let mut child = Command::new("fzf") - .arg("--prompt= Select Neovim Distribution  ") + let mut child = match Command::new("fzf") + .arg("--prompt= Select Neovim Configuration  ") .arg("--height=~50%") .arg("--layout=reverse") .arg("--border") @@ -41,32 +30,41 @@ pub fn select_config() { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() - .expect("Failed to spawn fzf process"); + { + Ok(child) => child, + Err(_) => utils::print_outro_cancel(Some("Failed to start fzf")), + }; if let Some(mut stdin) = child.stdin.take() { - stdin.write_all(items.as_bytes()).unwrap(); + match stdin.write_all(items.as_bytes()) { + Ok(_) => {} + Err(_) => utils::print_outro_cancel(None), + }; } let mut output = String::new(); if let Some(mut stdout) = child.stdout.take() { - stdout.read_to_string(&mut output).unwrap(); + match stdout.read_to_string(&mut output) { + Ok(_) => {} + Err(_) => utils::print_outro_cancel(None), + }; } - Command::new("nvim") + match Command::new("nvim") .env( "NVIM_APPNAME", - config - .configs - .iter() - .find(|s| s.name == output.trim()) - .expect("Failed to find selected item") - .nvim_dir_name - .clone(), + match config.configs.iter().find(|s| s.name == output.trim()) { + Some(selected_item) => selected_item.nvim_dir_name.clone(), + None => utils::print_outro_cancel(None), + }, ) .spawn() - .unwrap() - .wait() - .ok(); + { + Ok(mut command) => { + command.wait().ok(); + } + Err(_) => utils::print_outro_cancel(Some("Failed to start Neovim")), + }; - print_outro(None); + utils::print_outro(None); } diff --git a/src/main.rs b/src/main.rs index f2aaf1b..c0fcbea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,15 +3,10 @@ mod cmds; mod store; mod utils; -extern crate core; - -use crate::{ - cli::run_cli, - utils::{disable_emojis, enable_emojis}, -}; - -fn main() { - enable_emojis(); - run_cli(); - disable_emojis(); +fn main() -> std::io::Result<()> { + utils::setup_ctrl_c_handler(); + utils::enable_emojis(); + cli::run_cli(); + utils::disable_emojis(); + Ok(()) } diff --git a/src/store.rs b/src/store.rs index b6ef6fa..3564a05 100644 --- a/src/store.rs +++ b/src/store.rs @@ -5,6 +5,8 @@ use std::fs::File; use std::io::Write; use std::path::PathBuf; +use crate::utils; + #[derive(Serialize, Deserialize, Debug, Clone, Merge)] pub struct Config { #[merge(strategy = merge::vec::append)] @@ -25,14 +27,14 @@ fn get_default_config() -> Config { fn get_config_dir() -> PathBuf { match dirs::config_dir() { Some(dir) => dir.join("nvims"), - None => panic!("Could not get config dir"), + None => utils::print_outro_cancel(None), } } fn get_config_path() -> String { match get_config_dir().join("config.json").to_str() { Some(path) => path.to_string(), - None => panic!("Could not get config dir"), + None => utils::print_outro_cancel(None), } } @@ -42,26 +44,34 @@ pub fn get_config() -> Config { let config_dir = get_config_dir(); - if !config_dir.exists() { - fs::create_dir_all(&config_dir).expect("Could not create config dir"); + if !config_dir.exists() && fs::create_dir_all(&config_dir).is_err() { + utils::print_outro_cancel(Some("Failed to create config directory")) } match fs::metadata(&config_path) { Ok(_) => { - let config = fs::read_to_string(&config_path).expect("Could not read config file"); - serde_json::from_str(&config).unwrap_or(default_config) - } - Err(_) => { - File::create(&config_path) - .expect("Could not create config file") - .write_all( - serde_json::to_string(&default_config) - .expect("Could not default serialize config") - .as_bytes(), - ) - .expect("Could not write config file"); - default_config + let config = match fs::read_to_string(&config_path) { + Ok(config) => config, + Err(_) => utils::print_outro_cancel(Some("Failed to read config file")), + }; + match serde_json::from_str(&config) { + Ok(config) => config, + Err(_) => utils::print_outro_cancel(Some("Failed to parse config file")), + } } + Err(_) => match File::create(&config_path) { + Ok(mut file) => match file.write_all( + match serde_json::to_string(&default_config) { + Ok(config) => config, + Err(_) => utils::print_outro_cancel(Some("Failed to serialize config")), + } + .as_bytes(), + ) { + Ok(_) => default_config, + Err(_) => utils::print_outro_cancel(Some("Failed to write config file")), + }, + Err(_) => utils::print_outro_cancel(Some("Failed to create config file")), + }, } } @@ -75,12 +85,21 @@ pub fn set_config(config: Config, override_config: bool) { current_config.merge(config); } - File::create(config_path) - .expect("Could not create config file") - .write_all( - serde_json::to_string(¤t_config) - .expect("Could not serialize config") - .as_bytes(), - ) - .expect("Could not write config file"); + match File::create(config_path) { + Ok(mut file) => { + if file + .write_all( + match serde_json::to_string(¤t_config) { + Ok(config) => config, + Err(_) => utils::print_outro_cancel(Some("Failed to serialize config")), + } + .as_bytes(), + ) + .is_err() + { + utils::print_outro_cancel(Some("Failed to write config file")) + } + } + Err(_) => utils::print_outro_cancel(Some("Failed to create config file")), + } } diff --git a/src/utils/logger.rs b/src/utils/logger.rs index d94aedf..ca7f910 100644 --- a/src/utils/logger.rs +++ b/src/utils/logger.rs @@ -1,6 +1,6 @@ use colored::*; -use cliclack::log; +// use cliclack::log; pub fn get_error_string(err: &str, with_prefix: bool) -> String { format!( @@ -35,14 +35,14 @@ pub fn get_info_string(msg: &str, with_prefix: bool) -> String { ) } -pub fn error(err: &str, with_prefix: bool) { - log::error(get_error_string(err, with_prefix)).unwrap(); -} - -pub fn success(msg: &str, with_prefix: bool) { - log::success(get_success_string(msg, with_prefix)).unwrap(); -} - -pub fn info(msg: &str, with_prefix: bool) { - log::info(get_info_string(msg, with_prefix)).unwrap(); -} +// pub fn error(err: &str, with_prefix: bool) { +// log::error(get_error_string(err, with_prefix)).unwrap(); +// } +// +// pub fn success(msg: &str, with_prefix: bool) { +// log::success(get_success_string(msg, with_prefix)).unwrap(); +// } +// +// pub fn info(msg: &str, with_prefix: bool) { +// log::info(get_info_string(msg, with_prefix)).unwrap(); +// } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 3779188..c84d82d 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,10 +1,9 @@ mod logger; -use cliclack::{intro, outro, outro_cancel}; use regex::Regex; use std::{ env, - path::PathBuf, + path::{Component, Path, PathBuf}, process::{self, Command}, }; @@ -19,31 +18,35 @@ pub fn is_fzf_installed() -> bool { } pub fn print_intro(msg: Option<&str>) { - intro( + cliclack::intro( msg.unwrap_or("Neovim Configuration Switcher") .to_uppercase(), ) .unwrap(); } -pub fn print_outro(msg: Option<&str>) { - outro(logger::get_success_string( +pub fn print_outro(msg: Option<&str>) -> ! { + cliclack::outro(logger::get_success_string( msg.unwrap_or("Thanks for using Neovim Configuration Switcher!"), false, )) .unwrap(); + process::exit(0); } -pub fn print_outro_cancel(msg: &str) { - outro_cancel(logger::get_error_string(msg, true)).unwrap(); +pub fn print_outro_cancel(msg: Option<&str>) -> ! { + cliclack::outro_cancel(logger::get_error_string( + msg.unwrap_or("An unexpected error occurred. Please try again!"), + true, + )) + .unwrap(); process::exit(0); } /// Workaround to enable [console::Emoji]. Disable it with [disable_emojis] pub fn enable_emojis() { - match env::var("WT_SESSION") { - Ok(_) => {} - Err(_) => env::set_var("WT_SESSION", "nvims-wt-session-hack"), + if env::var("WT_SESSION").is_err() { + env::set_var("WT_SESSION", "nvims-wt-session-hack") } } @@ -55,27 +58,56 @@ pub fn disable_emojis() { } pub fn is_valid_github_url(url: &str) -> bool { - let re = Regex::new(r"^(https?://github\.com/|git@github\.com:)[^/\s]+/[^/\s]+(\.git)?$") - .expect("Failed to validate GitHub URL"); + let re = match Regex::new(r"^(https?://github\.com/|git@github\.com:)[^/\s]+/[^/\s]+(\.git)?$") + { + Ok(re) => re, + Err(_) => print_outro_cancel(None), + }; re.is_match(url) } +pub fn is_valid_dir_name(name: &str) -> bool { + let path = Path::new(name); + // Check if the path has a single component and that component is not ".." + path.components().count() == 1 + && path + .components() + .all(|component| matches!(component, Component::Normal(_))) +} + pub fn get_nvim_config_dir(path: Option<&str>) -> PathBuf { - let config_dir = dirs::config_local_dir().expect("Could not get config dir"); - if let Some(path) = path { - config_dir.join(path) - } else { - config_dir + match dirs::config_local_dir() { + Some(dir) => { + if let Some(path) = path { + dir.join(path) + } else { + dir + } + } + None => print_outro_cancel(None), } } -pub fn print_empty_configurations() -> bool { +pub fn ensure_non_empty_config() { let config = get_config(); if config.configs.is_empty() { - print_intro(None); - print_outro_cancel("No configurations found! Add a configuration and try again."); - true - } else { - false + print_outro_cancel(Some( + "No configurations found! Add a configuration and try again.", + )); + } +} + +pub fn setup_ctrl_c_handler() { + let ctrlc = ctrlc::set_handler(move || { + println!("{}", logger::get_info_string("Exiting...", true)); + process::exit(0); + }); + + if ctrlc.is_err() { + println!( + "{}", + logger::get_error_string("An unexpected error occurred. Please try again!", true) + ); + process::exit(0); } }