use {
super::{util, ShellInstall},
crate::{
conf,
errors::ProgramError,
},
directories::UserDirs,
lazy_regex::regex,
minimad::*,
regex::Captures,
std::{env, fs::OpenOptions, io::Write, path::PathBuf},
termimad::mad_print_inline,
};
const NAME: &str = "bash";
const SOURCING_FILES: &[&str] = &[".bashrc", ".bash_profile", ".zshrc", "$ZDOTDIR/.zshrc"];
const VERSION: &str = "1";
const BASH_FUNC: &str = r#"
# This script was automatically generated by the broot program
# More information can be found in https://github.com/Canop/broot
# This function starts broot and executes the command
# it produces, if any.
# It's needed because some shell commands, like `cd`,
# have no useful effect if executed in a subshell.
function br {
local cmd cmd_file code
cmd_file=$(mktemp)
if broot --outcmd "$cmd_file" "$@"; then
cmd=$(<"$cmd_file")
rm -f "$cmd_file"
eval "$cmd"
else
code=$?
rm -f "$cmd_file"
return "$code"
fi
}
"#;
const MD_NO_SOURCING: &str = r#"
I found no sourcing file for the bash/zsh family.
If you're using bash or zsh, then installation isn't complete:
the br function initialization script won't be sourced unless you source it yourself.
"#;
pub fn get_script() -> &'static str {
BASH_FUNC
}
fn get_link_path() -> PathBuf {
conf::dir().join("launcher").join(NAME).join("br")
}
fn get_script_path() -> PathBuf {
conf::app_dirs()
.data_dir()
.join("launcher")
.join(NAME)
.join(VERSION)
}
fn get_sourcing_paths() -> Vec<PathBuf> {
let homedir_path = UserDirs::new()
.expect("no home directory!")
.home_dir()
.to_path_buf();
SOURCING_FILES
.iter()
.map(|name| {
regex!(r#"\$(\w+)"#)
.replace(name, |c: &Captures<'_>| {
env::var(&c[1]).unwrap_or_else(|_| (*name).to_string())
})
.to_string()
})
.map(PathBuf::from)
.map(|path| {
if path.is_absolute() {
path
} else {
homedir_path.join(path)
}
})
.filter(|path| {
debug!("considering path: {:?}", &path);
path.exists()
})
.collect()
}
pub fn install(si: &mut ShellInstall) -> Result<(), ProgramError> {
let script_path = get_script_path();
si.write_script(&script_path, BASH_FUNC)?;
let link_path = get_link_path();
si.create_link(&link_path, &script_path)?;
let sourcing_paths = get_sourcing_paths();
if sourcing_paths.is_empty() {
warn!("no sourcing path for bash/zsh!");
si.skin.print_text(MD_NO_SOURCING);
return Ok(());
}
let source_line = format!("source {}", &link_path.to_string_lossy());
let sourced_template = TextTemplate::from(
r#"
${path} successfully patched, you can make the function immediately available with
source ${path}
"#,
);
for sourcing_path in &sourcing_paths {
let sourcing_path_str = sourcing_path.to_string_lossy();
if util::file_contains_line(sourcing_path, &source_line)? {
mad_print_inline!(
&si.skin,
"`$0` already patched, no change made.\n",
&sourcing_path_str
);
} else {
let mut shellrc = OpenOptions::new()
.write(true)
.append(true)
.open(sourcing_path)?;
shellrc.write_all(b"\n")?;
shellrc.write_all(source_line.as_bytes())?;
shellrc.write_all(b"\n")?;
let mut expander = sourced_template.expander();
expander.set("path", &sourcing_path_str);
si.skin.print_expander(expander);
}
}
si.done = true;
Ok(())
}