1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use std::{
    env::{split_paths, var_os},
    ffi::OsString,
    io::BufRead,
    ops::Range,
    path::PathBuf,
    process::Command,
};

use anyhow::{bail, Result};

pub struct Context {
    pub(crate) builtins: Vec<OsString>,
    pub(crate) patches: Vec<(Range<usize>, PathBuf)>,
    pub(crate) paths: Vec<PathBuf>,
    pub(crate) src: Vec<u8>,
    pub(crate) store_dir: PathBuf,
}

impl Context {
    pub fn load(
        bash: OsString,
        path: Option<OsString>,
        src: Vec<u8>,
        store_dir: PathBuf,
    ) -> Result<Self> {
        Ok(Context {
            builtins: load_builtins(bash)?,
            patches: Vec::new(),
            paths: load_paths(path),
            src,
            store_dir,
        })
    }
}

fn load_builtins(bash: OsString) -> Result<Vec<OsString>> {
    let output = Command::new(&bash).arg("-c").arg("enable").output()?;

    if !output.status.success() {
        bail!(
            "command `{} -c enable` failed: {}\n\nstdout: {}\nstderr: {}",
            bash.to_string_lossy(),
            output.status,
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr),
        );
    }

    Ok(output
        .stdout
        .lines()
        .filter_map(|line| line.ok()?.strip_prefix("enable ").map(Into::into))
        .collect())
}

fn load_paths(path: Option<OsString>) -> Vec<PathBuf> {
    path.or_else(|| var_os("PATH"))
        .map_or_else(Vec::new, |path| split_paths(&path).collect())
}