broot/shell_install/
bash.rs1use {
14 super::{
15 ShellInstall,
16 util,
17 },
18 crate::{
19 conf,
20 errors::*,
21 },
22 directories::UserDirs,
23 lazy_regex::regex,
24 regex::Captures,
25 std::{
26 env,
27 path::PathBuf,
28 },
29 termimad::mad_print_inline,
30};
31
32const NAME: &str = "bash";
33const SOURCING_FILES: &[&str] = &[".bashrc", ".bash_profile", ".zshrc", "$ZDOTDIR/.zshrc"];
34const VERSION: &str = "1";
35
36const BASH_FUNC: &str = r#"
40# This script was automatically generated by the broot program
41# More information can be found in https://github.com/Canop/broot
42# This function starts broot and executes the command
43# it produces, if any.
44# It's needed because some shell commands, like `cd`,
45# have no useful effect if executed in a subshell.
46function br {
47 local cmd cmd_file code
48 cmd_file=$(mktemp)
49 if broot --outcmd "$cmd_file" "$@"; then
50 cmd=$(<"$cmd_file")
51 command rm -f "$cmd_file"
52 eval "$cmd"
53 else
54 code=$?
55 command rm -f "$cmd_file"
56 return "$code"
57 fi
58}
59"#;
60
61const MD_NO_SOURCING: &str = r"
62I found no sourcing file for the bash/zsh family.
63If you're using bash or zsh, then installation isn't complete:
64the br function initialization script won't be sourced unless you source it yourself.
65";
66
67pub fn get_script() -> &'static str {
68 BASH_FUNC
69}
70
71fn get_link_path() -> PathBuf {
73 conf::dir().join("launcher").join(NAME).join("br")
74}
75
76fn get_script_path() -> PathBuf {
82 conf::app_dirs()
83 .data_dir()
84 .join("launcher")
85 .join(NAME)
86 .join(VERSION)
87}
88
89fn get_sourcing_paths() -> Vec<PathBuf> {
93 let homedir_path = UserDirs::new()
94 .expect("no home directory!")
95 .home_dir()
96 .to_path_buf();
97 SOURCING_FILES
98 .iter()
99 .map(|name| {
100 regex!(r#"\$(\w+)"#)
101 .replace(name, |c: &Captures<'_>| {
102 env::var(&c[1]).unwrap_or_else(|_| (*name).to_string())
103 })
104 .to_string()
105 })
106 .map(PathBuf::from)
107 .map(|path| {
108 if path.is_absolute() {
109 path
110 } else {
111 homedir_path.join(path)
112 }
113 })
114 .filter(|path| {
115 debug!("considering path: {:?}", &path);
116 path.exists()
117 })
118 .collect()
119}
120
121pub fn install(si: &mut ShellInstall) -> Result<(), ShellInstallError> {
126 let script_path = get_script_path();
127 si.write_script(&script_path, BASH_FUNC)?;
128 let link_path = get_link_path();
129 si.create_link(&link_path, &script_path)?;
130 let sourcing_paths = get_sourcing_paths();
131 if sourcing_paths.is_empty() {
132 warn!("no sourcing path for bash/zsh!");
133 si.skin.print_text(MD_NO_SOURCING);
134 return Ok(());
135 }
136 let escaped_path = link_path.to_string_lossy().replace(' ', "\\ ");
137 let source_line = format!("source {}", &escaped_path);
138 for sourcing_path in &sourcing_paths {
139 let sourcing_path_str = sourcing_path.to_string_lossy();
140 if util::file_contains_line(sourcing_path, &source_line)? {
141 mad_print_inline!(
142 &si.skin,
143 "`$0` already patched, no change made.\n",
144 &sourcing_path_str,
145 );
146 } else {
147 util::append_to_file(sourcing_path, format!("\n{source_line}\n"))?;
148 let is_zsh = sourcing_path_str.contains(".zshrc");
149 if is_zsh {
150 mad_print_inline!(
151 &si.skin,
152 "`$0` successfully patched, you can make the function immediately available with `exec zsh`\n",
153 &sourcing_path_str,
154 );
155 } else {
156 mad_print_inline!(
157 &si.skin,
158 "`$0` successfully patched, you can make the function immediately available with `source $0`\n",
159 &sourcing_path_str,
160 );
161 }
162 }
163 }
164 si.done = true;
165 Ok(())
166}