use crate::{
db::Database,
error::{WHError, WHResult},
path::FilePath,
};
use clap::Parser;
use std::{env::current_exe, fmt::Display, str::FromStr};
#[derive(Parser, Debug)]
pub struct Init {
shell: Shell,
#[clap(long, default_value = "wh")]
worm_hole: String,
#[clap(long, default_value = "whcd")]
cd: String,
#[clap(long)]
add: Option<String>,
#[clap(long)]
remove: Option<String>,
#[clap(long)]
list: Option<String>,
#[clap(long)]
search: Option<String>,
#[clap(long)]
query: Option<String>,
#[clap(long)]
edit: Option<String>,
#[clap(long)]
rename: Option<String>,
}
impl Init {
pub fn run(&self, database: &Database, db_path: &str) -> WHResult<()> {
database.init();
print!("{}", self.shell.get_init_code(self, db_path)?);
Ok(())
}
}
#[derive(clap::ValueEnum, Debug, Clone)]
enum Shell {
Bash,
Fish,
Zsh,
Nu,
}
impl Shell {
fn new_alias(&self, alias: &str, command: &str) -> WHResult<String> {
Ok(match self {
Shell::Bash | Shell::Zsh | Shell::Fish => {
if alias.contains(' ') {
return Err(WHError::Err(format!("Aliases cannot contain spaces in {self:?}"), 1));
}
format!("alias {}='{}'", alias, command)
}
Shell::Nu => format!("export alias '{}' = {}", alias, command),
})
}
fn comment(&self, text: impl Display) -> String {
format!("# {}", text)
}
pub fn get_init_code(&self, aliases: &Init, db_path: &str) -> WHResult<String> {
let mut builder = vec![self.comment(format!("This was generated using worm hole {}\n", env!("CARGO_PKG_VERSION")))];
let wh = &aliases.worm_hole;
builder.push(self.new_alias(
&aliases.worm_hole,
&format!(
"{} --db-path {}",
current_exe().unwrap().into_os_string().into_string().unwrap(),
FilePath::from_str(db_path).unwrap().str()
),
)?);
builder.push(self.get_cd_function(aliases));
if let Some(add) = &aliases.add {
builder.push(match self {
Shell::Nu => format!("#Add an alias-path pair to worm hole\nexport def '{add}' [alias: string path?: string # The real path to the location\n]: nothing -> nothing {{ {wh} 'add' ...([$alias $path] | where $it != null) }}"),
_ => self.new_alias(add, &format!("{wh} add"))?,
});
}
if let Some(remove) = &aliases.remove {
builder.push(match self {
Shell::Nu => format!("#Remove an alias-path pair from worm hole\nexport def '{remove}' [alias: string@__worm_hole_list]: nothing -> nothing {{ {wh} 'rm' $alias }}"),
_ => self.new_alias(remove, &format!("{wh} rm"))?,
});
}
if let Some(list) = &aliases.list {
builder.push(match self {
Shell::Nu => format!("#List all aliases and the corresponding path in worm hole\nexport def '{list}' []: nothing -> table<alias: string, path: string> {{ {wh} 'ls' | parse --regex '(?P<alias>.+?)\\s+-> (?P<path>.+)' }}"),
_ => self.new_alias(list, &format!("{wh} ls"))?
});
}
if let Some(search) = &aliases.search {
builder.push(match self {
Shell::Nu => format!("#Search for worm hole aliases with path matching a pattern\nexport def '{search}' [pattern?: string # The pattern to search for in the aliases's paths, search for aliases in the current directory if not provided\n]: nothing -> table<alias: string, path: string> {{ {wh} 'search' ...([$pattern] | where $it != null) | parse --regex '(?P<alias>.+?)\\s+-> (?P<path>.+)' }}"),
_ => self.new_alias(search, &format!("{wh} search"))?
});
}
if let Some(query) = &aliases.query {
builder.push(match self {
Shell::Nu => format!("#Get the path for the given worm hole alias\nexport def '{query}' [alias: string@__worm_hole_list]: nothing -> nothing {{ {wh} query $alias }}"),
_ => self.new_alias(query, &format!("{wh} query"))?,
});
}
if let Some(edit) = &aliases.edit {
builder.push(match self {
Shell::Nu => format!("#Edit the path of a worm hole alias\nexport def '{edit}' [alias: string@__worm_hole_list path?: string # The new path to the location\n]: nothing -> nothing {{ {wh} edit ...([$alias $path] | where $it != null) }}"),
_ => self.new_alias(edit, &format!("{wh} edit"))?,
});
}
if let Some(rename) = &aliases.rename {
builder.push(match self {
Shell::Nu => format!("#Change an alias in worm hole without changing the path\nexport def '{rename}' [old_alias: string@__worm_hole_list new_alias: string]: nothing -> nothing {{ {wh} rename $old_alias $new_alias }}"),
_ => self.new_alias(rename, &format!("{wh} rename"))?,
});
}
builder.push(match self {
Shell::Bash | Shell::Zsh => format!("export WH_ALIAS=\"{wh}\""),
Shell::Fish => format!("set -gx WH_ALIAS \"{wh}\""),
Shell::Nu => format!("export-env {{ $env.WH_ALIAS = '{wh}' }}"),
});
Ok(builder.join("\n"))
}
#[rustfmt::skip]
fn get_cd_function(&self, aliases: &Init) -> String {
let cd = &aliases.cd;
let wh = &aliases.worm_hole;
match self {
Shell::Bash | Shell::Zsh => vec![
format!("{cd}() {{"),
String::from(" if [ -z \"$1\" ]"),
String::from(" then"),
String::from(" cd $HOME"),
String::from(" else"),
format!(" CD=$({wh} query \"$1\") && \\builtin cd \"$CD\""),
String::from(" fi"),
String::from("}"),
],
Shell::Fish => vec![
format!("function {cd}"),
String::from(" if test -z $argv"),
String::from(" cd $HOME"),
String::from(" else"),
format!(" set -l CD ({wh} query \"$argv\")"),
String::from(" if test -n $CD"),
String::from(" builtin cd \"$CD\""),
String::from(" end"),
String::from(" end"),
String::from("end"),
],
Shell::Nu => vec![
if aliases.list == Some(format!("{wh} ls")) {
format!("def __worm_hole_list [] {{ ({wh} ls | rename -c {{ alias: value path: description }}) }}")
} else {
format!("def __worm_hole_list [] {{ ({wh} ls | parse --regex '(?P<value>.+?)\\s+-> (?P<description>.+)') }}")
},
format!("#Change the current working directory using worm hole's aliases\nexport def --env {cd} [alias?: string@__worm_hole_list] {{"),
String::from(" if $alias == null {"),
String::from(" cd $env.HOME"),
String::from(" } else {"),
format!(" let CD = {wh} query $alias"),
String::from(" cd $CD"),
String::from(" }"),
String::from("}"),
],
}.join("\n")
}
}