shell-rs 0.2.6

Rust reimplementation of common coreutils APIs
Documentation
// Copyright (c) 2021 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
// Use of this source is governed by Apache-2.0 License that can be found
// in the LICENSE file.

extern crate regex;

use regex::Regex;

/// Expand environment variables in string.
pub fn expand_env<T: AsRef<str>>(p: T) -> String {
    // TODO(Shaohua): Tuning string operations
    let mut p = String::from(p.as_ref());
    p = p.replace("~", "${HOME}");
    let start = '{';
    let end = '}';
    let re = Regex::new(r"\$(\w+|\{[^}]*\})").unwrap();
    let mut global_start_pos = 0;

    #[warn(unused_variables, unused_mut)]
    let mut result = String::new();

    for cap in re.find_iter(&p) {
        let mut name = cap.as_str();
        name = &name[1..name.chars().count()];
        let name_len = name.chars().count();
        if name.chars().nth(0).unwrap() == start && name.chars().nth(name_len - 1).unwrap() == end {
            name = &name[1..name_len - 1];
        }

        let local_start_pos = cap.start();
        if global_start_pos < local_start_pos {
            let inner: String = p
                .chars()
                .skip(global_start_pos)
                .take(local_start_pos - global_start_pos)
                .collect();
            result.push_str(inner.as_str());
        }

        for (ref key, ref value) in std::env::vars().into_iter() {
            if key == name {
                result.push_str(value);
                break;
            }
        }
        global_start_pos = cap.end();
    }

    let end_pos = p.chars().count();
    if global_start_pos < end_pos {
        let inner: String = p
            .chars()
            .skip(global_start_pos)
            .take(end_pos - global_start_pos)
            .collect();
        result.push_str(inner.as_str());
    }
    println!("result: {}", result);

    return result;
}

#[cfg(test)]
mod tests {
    use super::expand_env;

    #[test]
    fn test_expand_env() {
        let s = expand_env("${PWD}/a/$HOME/c.txt");
        let pwd = std::env::current_dir().unwrap();
        let home = std::env::home_dir().unwrap();
        let s2 = format!(
            "{}/a/{}/c.txt",
            pwd.to_str().unwrap(),
            home.to_str().unwrap()
        );
        assert_eq!(s, s2);
    }
}