env_hooks/shells/
bash.rs

1use std::collections::HashSet;
2
3use bstr::{B, BString, ByteSlice};
4use shell_quote::Bash;
5
6use crate::EnvVarsState;
7
8const BASH_HOOK: &str = r#"
9    _{{.HookPrefix}}_hook() {
10        local previous_exit_status=$?;
11        vars="$({{.ExportCommand}})";
12        trap -- '' SIGINT;
13        eval "$vars";
14        trap - SIGINT;
15        return $previous_exit_status;
16    };
17    if [[ ";${PROMPT_COMMAND[*]:-};" != *";_{{.HookPrefix}}_hook;"* ]]; then
18        if [[ "$(declare -p PROMPT_COMMAND 2>&1)" == "declare -a"* ]]; then
19            PROMPT_COMMAND=(_{{.HookPrefix}}_hook "${PROMPT_COMMAND[@]}")
20        else
21            PROMPT_COMMAND="_{{.HookPrefix}}_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
22        fi
23    fi
24"#;
25
26pub fn hook(hook_prefix: impl AsRef<[u8]>, export_command: impl AsRef<[u8]>) -> BString {
27    BString::from(BASH_HOOK)
28        .replace("{{.HookPrefix}}", hook_prefix)
29        .replace("{{.ExportCommand}}", export_command)
30        .into()
31}
32
33pub fn export(
34    env_vars_state: EnvVarsState,
35    _semicolon_delimited_env_vars: Option<&HashSet<String>>,
36) -> BString {
37    let exports = env_vars_state
38        .iter()
39        .map(|(key, state)| {
40            if let Some(value) = state {
41                export_var(key, value)
42            } else {
43                unset_var(key)
44            }
45        })
46        .collect::<Vec<_>>();
47    bstr::join("\n", exports).into()
48}
49
50fn export_var(key: &str, value: &str) -> BString {
51    let script = bstr::join(" ", [B("export"), &Bash::quote_vec(key)]);
52    let value = Bash::quote_vec(value);
53    bstr::concat([&bstr::join("=", [script, value]), B(";")]).into()
54}
55
56fn unset_var(key: &str) -> BString {
57    bstr::concat([
58        &bstr::join(" ", [B("unset"), &Bash::quote_vec(key)]),
59        B(";"),
60    ])
61    .into()
62}