shell-rs 0.1.2

Rust reimplementation of common coreutils APIs
Documentation
extern crate regex;

use regex::Regex;

/// Expand environment variables in string.
/// ```
/// extern crate shell_rs as shell;
///
/// use shell::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);
///
/// let s = expand_env("server.conf");
/// let s3 = format!("{}/server.conf", pwd.to_str().unwrap());
/// assert_eq!(s, s3);
///
/// ```
pub fn expand_env<T: AsRef<str>>(p: T) -> String {
    // TODO(Shaohua): Tuning string operations
    let mut p = String::from(p.as_ref());
    let mut result = String::new();
    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());
    }

    // TODO(Shaohua): Define path separator as constant value
    let first_char = result.chars().nth(0).unwrap();
    if first_char != '$' && first_char != '/' {
        let pwd = String::from_utf8(nc::getcwd().unwrap()).unwrap();
        result = format!("{}/{}", pwd, result);
    }

    return result;
}