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}