shell_rs/core/
env.rs

1// Copyright (c) 2021 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
2// Use of this source is governed by Apache-2.0 License that can be found
3// in the LICENSE file.
4
5extern crate regex;
6
7use regex::Regex;
8
9/// Expand environment variables in string.
10pub fn expand_env<T: AsRef<str>>(p: T) -> String {
11    // TODO(Shaohua): Tuning string operations
12    let mut p = String::from(p.as_ref());
13    p = p.replace("~", "${HOME}");
14    let start = '{';
15    let end = '}';
16    let re = Regex::new(r"\$(\w+|\{[^}]*\})").unwrap();
17    let mut global_start_pos = 0;
18
19    #[warn(unused_variables, unused_mut)]
20    let mut result = String::new();
21
22    for cap in re.find_iter(&p) {
23        let mut name = cap.as_str();
24        name = &name[1..name.chars().count()];
25        let name_len = name.chars().count();
26        if name.chars().nth(0).unwrap() == start && name.chars().nth(name_len - 1).unwrap() == end {
27            name = &name[1..name_len - 1];
28        }
29
30        let local_start_pos = cap.start();
31        if global_start_pos < local_start_pos {
32            let inner: String = p
33                .chars()
34                .skip(global_start_pos)
35                .take(local_start_pos - global_start_pos)
36                .collect();
37            result.push_str(inner.as_str());
38        }
39
40        for (ref key, ref value) in std::env::vars().into_iter() {
41            if key == name {
42                result.push_str(value);
43                break;
44            }
45        }
46        global_start_pos = cap.end();
47    }
48
49    let end_pos = p.chars().count();
50    if global_start_pos < end_pos {
51        let inner: String = p
52            .chars()
53            .skip(global_start_pos)
54            .take(end_pos - global_start_pos)
55            .collect();
56        result.push_str(inner.as_str());
57    }
58    println!("result: {}", result);
59
60    return result;
61}
62
63#[cfg(test)]
64mod tests {
65    use super::expand_env;
66
67    #[test]
68    fn test_expand_env() {
69        let s = expand_env("${PWD}/a/$HOME/c.txt");
70        let pwd = std::env::current_dir().unwrap();
71        let home = std::env::home_dir().unwrap();
72        let s2 = format!(
73            "{}/a/{}/c.txt",
74            pwd.to_str().unwrap(),
75            home.to_str().unwrap()
76        );
77        assert_eq!(s, s2);
78    }
79}