use crate::{db::Database, error::WHResult, path::FilePath};
use clap::Parser;
use std::str::FromStr;
use string_builder::Builder as StringBuilder;
#[derive(Parser, Debug)]
pub struct Init {
/// The shell to generate the init code for
shell: Shell,
// All these flags will be used to set the commands to write in the console to call worm_hole
// Only the worm_hole and cd command has a default value
/// The command to use to call worm_hole
#[clap(long, default_value = "wh")]
worm_hole: String,
/// The command to use to change directory with worm_hole
#[clap(long, default_value = "whcd")]
cd: String,
/// The command to use to add a new path to worm_hole
#[clap(long)]
add: Option<String>,
/// The command to use to remove a path from worm_hole
#[clap(long)]
remove: Option<String>,
/// The command to use to list all the paths in worm_hole
#[clap(long)]
list: Option<String>,
/// The command to use to search for all paths containing a given pattern in worm_hole
#[clap(long)]
search: Option<String>,
/// The command to use to query a path in worm_hole
#[clap(long)]
query: Option<String>,
/// The command to use to edit a path in worm_hole
#[clap(long)]
edit: Option<String>,
/// The command to use to rename an alias in worm_hole
#[clap(long)]
rename: Option<String>,
}
impl Init {
pub fn run(&self, database: &Database, db_path: &str) -> WHResult<()> {
database.init();
println!("{}", self.shell.get_init_code(self, db_path));
Ok(())
}
}
#[derive(clap::ValueEnum, Debug, Clone)]
enum Shell {
Bash,
Fish,
Zsh,
}
impl Shell {
/// Get the init code for the shell
/// The init code can be eval to create aliases and functions required to use worm_hole
/// Works for bash, zsh and fish
pub fn get_init_code(&self, aliases: &Init, db_path: &str) -> String {
let mut builder = StringBuilder::default();
builder.append(format!(
"alias {}='worm_hole --db-path {}'\n",
aliases.worm_hole,
FilePath::from_str(db_path).unwrap().str()
));
builder.append(self.get_cd_function(aliases.worm_hole.as_str()));
builder.append(format!("alias {}=__worm_hole_cd\n", aliases.cd));
if let Some(add) = &aliases.add {
builder.append(format!("alias {}='{} add'\n", add, aliases.worm_hole));
}
if let Some(remove) = &aliases.remove {
builder.append(format!("alias {}='{} remove'\n", remove, aliases.worm_hole));
}
if let Some(list) = &aliases.list {
builder.append(format!("alias {}='{} list'\n", list, aliases.worm_hole));
}
if let Some(search) = &aliases.search {
builder.append(format!("alias {}='{} search'\n", search, aliases.worm_hole));
}
if let Some(query) = &aliases.query {
builder.append(format!("alias {}='{} query'\n", query, aliases.worm_hole));
}
if let Some(edit) = &aliases.edit {
builder.append(format!("alias {}='{} edit'\n", edit, aliases.worm_hole));
}
if let Some(rename) = &aliases.rename {
builder.append(format!("alias {}='{} rename'\n", rename, aliases.worm_hole));
}
builder.string().unwrap()
}
/// Get the function to change directory with worm_hole
/// The function will call worm_hole query to get the path of the alias and cd to it
/// Works for bash, zsh and fish
/// The function is named __worm_hole_cd
fn get_cd_function(&self, worm_hole_command: &str) -> String {
let mut builder = StringBuilder::default();
match self {
Shell::Bash | Shell::Zsh => {
builder.append("__worm_hole_cd() {\n");
builder.append(" if [ -z \"$1\" ]\n");
builder.append(" then\n");
builder.append(" cd $HOME\n");
builder.append(" else\n");
builder.append(format!(
" CD=$({} query \"$1\") && \\builtin cd \"$CD\"\n",
worm_hole_command
));
builder.append(" fi\n");
builder.append("}\n");
builder.string().unwrap()
}
Shell::Fish => {
builder.append("function __worm_hole_cd\n");
builder.append(" if test -z $argv\n");
builder.append(" cd $HOME\n");
builder.append(" else\n");
builder.append(format!(" set -l CD ({} query \"$argv\")\n", worm_hole_command));
builder.append(" if test -n $CD\n");
builder.append(" builtin cd \"$CD\"\n");
builder.append(" end\n");
builder.append(" end\n");
builder.append("end\n");
builder.string().unwrap()
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
fn has_bin_index(bin: usize, index: usize) -> bool {
(bin >> index) & 1 == 1
}
#[rustfmt::skip]
fn get_init_struct(shell: Shell, data: usize) -> Init {
Init {
shell,
worm_hole: if has_bin_index(data, 0) { "custom_wh".to_string() } else { "wh".to_string() },
cd: if has_bin_index(data, 1) { "custom_whcd".to_string() } else { "whcd".to_string() },
add: if has_bin_index(data, 2) { Some("custom_add".to_string()) } else { None },
remove: if has_bin_index(data, 3) { Some("custom_remove".to_string()) } else { None },
list: if has_bin_index(data, 4) { Some("custom_list".to_string()) } else { None },
search: if has_bin_index(data, 5) { Some("custom_search".to_string()) } else { None },
query: if has_bin_index(data, 6) { Some("custom_query".to_string()) } else { None },
edit: if has_bin_index(data, 7) { Some("custom_edit".to_string()) } else { None },
rename: if has_bin_index(data, 8) { Some("custom_rename".to_string()) } else { None },
}
}
const DB_PATH: &str = "/usr/bin/bash";
#[test]
fn bash() {
const SHELL: Shell = Shell::Bash;
let normal = "alias wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\n__worm_hole_cd() {\n if [ -z \"$1\" ]\n then\n cd $HOME\n else\n CD=$(wh query \"$1\") && \\builtin cd \"$CD\"\n fi\n}\nalias whcd=__worm_hole_cd\n";
vec![
(0b000000000, normal.clone()),
(0b000000001, "alias custom_wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\n__worm_hole_cd() {\n if [ -z \"$1\" ]\n then\n cd $HOME\n else\n CD=$(custom_wh query \"$1\") && \\builtin cd \"$CD\"\n fi\n}\nalias whcd=__worm_hole_cd\n"),
(0b000000010, "alias wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\n__worm_hole_cd() {\n if [ -z \"$1\" ]\n then\n cd $HOME\n else\n CD=$(wh query \"$1\") && \\builtin cd \"$CD\"\n fi\n}\nalias custom_whcd=__worm_hole_cd\n"),
(0b000000100, normal.clone() + "alias custom_add='wh add'\n"),
(0b000001000, normal.clone() + "alias custom_remove='wh remove'\n"),
(0b000010000, normal.clone() + "alias custom_list='wh list'\n"),
(0b000100000, normal.clone() + "alias custom_search='wh search'\n"),
(0b001000000, normal.clone() + "alias custom_query='wh query'\n"),
(0b010000000, normal.clone() + "alias custom_edit='wh edit'\n"),
(0b100000000, normal.clone() + "alias custom_rename='wh rename'\n"),
(0b111111111, "alias custom_wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\n__worm_hole_cd() {\n if [ -z \"$1\" ]\n then\n cd $HOME\n else\n CD=$(custom_wh query \"$1\") && \\builtin cd \"$CD\"\n fi\n}\nalias custom_whcd=__worm_hole_cd\nalias custom_add='custom_wh add'\nalias custom_remove='custom_wh remove'\nalias custom_list='custom_wh list'\nalias custom_search='custom_wh search'\nalias custom_query='custom_wh query'\nalias custom_edit='custom_wh edit'\nalias custom_rename='custom_wh rename'\n"),
].into_iter().for_each(|(data, expected)| {
assert_eq!(SHELL.get_init_code(&get_init_struct(SHELL, data), DB_PATH), expected);
});
}
#[test]
fn zsh() {
const SHELL: Shell = Shell::Zsh;
let normal = "alias wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\n__worm_hole_cd() {\n if [ -z \"$1\" ]\n then\n cd $HOME\n else\n CD=$(wh query \"$1\") && \\builtin cd \"$CD\"\n fi\n}\nalias whcd=__worm_hole_cd\n";
vec![
(0b000000000, normal.clone()),
(0b000000001, "alias custom_wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\n__worm_hole_cd() {\n if [ -z \"$1\" ]\n then\n cd $HOME\n else\n CD=$(custom_wh query \"$1\") && \\builtin cd \"$CD\"\n fi\n}\nalias whcd=__worm_hole_cd\n"),
(0b000000010, "alias wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\n__worm_hole_cd() {\n if [ -z \"$1\" ]\n then\n cd $HOME\n else\n CD=$(wh query \"$1\") && \\builtin cd \"$CD\"\n fi\n}\nalias custom_whcd=__worm_hole_cd\n"),
(0b000000100, normal.clone() + "alias custom_add='wh add'\n"),
(0b000001000, normal.clone() + "alias custom_remove='wh remove'\n"),
(0b000010000, normal.clone() + "alias custom_list='wh list'\n"),
(0b000100000, normal.clone() + "alias custom_search='wh search'\n"),
(0b001000000, normal.clone() + "alias custom_query='wh query'\n"),
(0b010000000, normal.clone() + "alias custom_edit='wh edit'\n"),
(0b100000000, normal.clone() + "alias custom_rename='wh rename'\n"),
(0b111111111, "alias custom_wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\n__worm_hole_cd() {\n if [ -z \"$1\" ]\n then\n cd $HOME\n else\n CD=$(custom_wh query \"$1\") && \\builtin cd \"$CD\"\n fi\n}\nalias custom_whcd=__worm_hole_cd\nalias custom_add='custom_wh add'\nalias custom_remove='custom_wh remove'\nalias custom_list='custom_wh list'\nalias custom_search='custom_wh search'\nalias custom_query='custom_wh query'\nalias custom_edit='custom_wh edit'\nalias custom_rename='custom_wh rename'\n"),
].into_iter().for_each(|(data, expected)| {
assert_eq!(SHELL.get_init_code(&get_init_struct(SHELL, data), DB_PATH), expected);
});
}
#[test]
fn fish() {
const SHELL: Shell = Shell::Fish;
let normal = "alias wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\nfunction __worm_hole_cd\n if test -z $argv\n cd $HOME\n else\n set -l CD (wh query \"$argv\")\n if test -n $CD\n builtin cd \"$CD\"\n end\n end\nend\nalias whcd=__worm_hole_cd\n";
vec![
(0b000000000, normal.clone()),
(0b000000001, "alias custom_wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\nfunction __worm_hole_cd\n if test -z $argv\n cd $HOME\n else\n set -l CD (custom_wh query \"$argv\")\n if test -n $CD\n builtin cd \"$CD\"\n end\n end\nend\nalias whcd=__worm_hole_cd\n"),
(0b000000010, "alias wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\nfunction __worm_hole_cd\n if test -z $argv\n cd $HOME\n else\n set -l CD (wh query \"$argv\")\n if test -n $CD\n builtin cd \"$CD\"\n end\n end\nend\nalias custom_whcd=__worm_hole_cd\n"),
(0b000000100, normal.clone() + "alias custom_add='wh add'\n"),
(0b000001000, normal.clone() + "alias custom_remove='wh remove'\n"),
(0b000010000, normal.clone() + "alias custom_list='wh list'\n"),
(0b000100000, normal.clone() + "alias custom_search='wh search'\n"),
(0b001000000, normal.clone() + "alias custom_query='wh query'\n"),
(0b010000000, normal.clone() + "alias custom_edit='wh edit'\n"),
(0b100000000, normal.clone() + "alias custom_rename='wh rename'\n"),
(0b111111111, "alias custom_wh='worm_hole --db-path ".to_owned() + DB_PATH + "'\nfunction __worm_hole_cd\n if test -z $argv\n cd $HOME\n else\n set -l CD (custom_wh query \"$argv\")\n if test -n $CD\n builtin cd \"$CD\"\n end\n end\nend\nalias custom_whcd=__worm_hole_cd\nalias custom_add='custom_wh add'\nalias custom_remove='custom_wh remove'\nalias custom_list='custom_wh list'\nalias custom_search='custom_wh search'\nalias custom_query='custom_wh query'\nalias custom_edit='custom_wh edit'\nalias custom_rename='custom_wh rename'\n"),
].into_iter().for_each(|(data, expected)| {
assert_eq!(SHELL.get_init_code(&get_init_struct(SHELL, data), DB_PATH), expected);
});
}
}