use {
std::{
env,
fs::OpenOptions,
io::Write,
path::PathBuf,
},
directories::UserDirs,
crate::{
conf,
errors::ProgramError,
},
super::{
ShellInstall,
util,
},
minimad::*,
regex::{Captures, Regex},
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 {
f=$(mktemp)
(
set +e
broot --outcmd "$f" "$@"
code=$?
if [ "$code" != 0 ]; then
rm -f "$f"
exit "$code"
fi
)
code=$?
if [ "$code" != 0 ]; then
return "$code"
fi
d=$(<"$f")
rm -f "$f"
eval "$d"
}
"#;
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(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(())
}