env_substitute/
lib.rs

1//// -- env-substitute
2//// 
3//// This crate provides the substitute function.
4use std::{
5    ffi::OsString,
6    path::{Component, PathBuf},
7};
8fn check_and_swap(component: impl ToString) -> Option<impl ToString> {
9    let component = component.to_string();
10    if component.starts_with('$') {
11        let trimmed = component.strip_prefix('$')?;
12        std::env::var(trimmed).ok()
13    } else {
14        Some(component)
15    }
16}
17fn handle_components<'a>(c: Component<'a>) -> Option<OsString> {
18    match c {
19        Component::Normal(normal) => {
20            let comp_str = normal.to_str()?;
21            let swapped = check_and_swap(comp_str)?.to_string();
22            let os_str = OsString::from(swapped);
23            Some(os_str)
24        }
25        _ => None,
26    }
27}
28
29/// Takes a path, finds any components that begin with a `$` and then attempts to 
30/// substitute the remainder of the component with an environment variable named
31/// with that remainder.
32///
33/// An attempt to duplicate the functionality of shells.
34pub fn substitute(path: PathBuf) -> PathBuf {
35    let mut output = PathBuf::new();
36    for c in path.components() {
37        match handle_components(c) {
38            Some(s) => output.push(s),
39            None => output.push(c),
40        };
41    }
42    output
43}
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    /// Demonstrating that $TEST in a path is replaced with the value of the
49    /// environment variable named TEST
50    #[test]
51    fn basic() {
52        std::env::set_var("TEST", "TEST_VAL");
53        let path = PathBuf::from("$TEST/file.txt");
54        let substituted = substitute(path);
55        assert_eq!(substituted, PathBuf::from("TEST_VAL/file.txt"));
56    }
57
58    /// Demonstrating that if the environment variable is undefined or the
59    /// substitution fails for some reason the path is unchanged
60    #[test]
61    fn no_env() {
62        let path = PathBuf::from("$FAKE/file.txt");
63        let substituted = substitute(path.clone());
64        assert_eq!(substituted, path);
65    }
66}