Skip to main content

env_hooks/shells/
fish.rs

1use std::collections::HashSet;
2
3use bstr::{B, BString, ByteSlice};
4use shell_quote::Fish;
5
6use crate::EnvVarsState;
7
8const FISH_HOOK: &str = r#"
9    function __{{.HookPrefix}}_export_eval --on-event fish_prompt;
10        {{.ExportCommand}} | source;
11
12        if test "${{.HookPrefix}}_fish_mode" != "disable_arrow";
13            function __{{.HookPrefix}}_cd_hook --on-variable PWD;
14                if test "${{.HookPrefix}}_fish_mode" = "eval_after_arrow";
15                    set -g __{{.HookPrefix}}_export_again 0;
16                else;
17                    {{.ExportCommand}} | source;
18                end;
19            end;
20        end;
21    end;
22
23    function __{{.HookPrefix}}_export_eval_2 --on-event fish_preexec;
24        if set -q __{{.HookPrefix}}_export_again;
25            set -e __{{.HookPrefix}}_export_again;
26            {{.ExportCommand}} | source;
27            echo;
28        end;
29
30        functions --erase __{{.HookPrefix}}_cd_hook;
31    end;
32"#;
33
34pub fn hook(hook_prefix: impl AsRef<[u8]>, export_command: impl AsRef<[u8]>) -> BString {
35    BString::from(FISH_HOOK)
36        .replace("{{.HookPrefix}}", hook_prefix)
37        .replace("{{.ExportCommand}}", export_command)
38        .into()
39}
40
41pub fn export(
42    env_vars_state: EnvVarsState,
43    semicolon_delimited_env_vars: Option<&HashSet<String>>,
44) -> BString {
45    let exports = env_vars_state
46        .iter()
47        .map(|(key, state)| {
48            if let Some(value) = state {
49                export_var(key, value, semicolon_delimited_env_vars)
50            } else {
51                unset_var(key)
52            }
53        })
54        .collect::<Vec<_>>();
55    bstr::join("\n", exports).into()
56}
57
58fn export_var(
59    key: &str,
60    value: &str,
61    semicolon_delimited_env_vars: Option<&HashSet<String>>,
62) -> BString {
63    let script = bstr::join(" ", [B("set -x -g"), &Fish::quote_vec(key)]);
64    let value = if let Some(sdev) = semicolon_delimited_env_vars
65        && sdev.contains(key)
66    {
67        let value_parts = value.split(':').map(Fish::quote_vec).collect::<Vec<_>>();
68        bstr::join(" ", value_parts)
69    } else {
70        Fish::quote_vec(value)
71    };
72    bstr::concat([&bstr::join(" ", [script, value]), B(";")]).into()
73}
74
75fn unset_var(key: &str) -> BString {
76    bstr::concat([
77        &bstr::join(" ", [B("set -e -g"), &Fish::quote_vec(key)]),
78        B(";"),
79    ])
80    .into()
81}