1use 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
29pub 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 #[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 #[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}