cmd_lib 0.8.2

Common rust commandline macros and utils, to write shell script like tasks easily
Documentation
pub fn parse_cmds(s: &str) -> Vec<String> {
    let is_cmd_ended = |c| c == ';';
    parse_argv(parse_seps(s, is_cmd_ended))
}

pub fn parse_pipes(s: &str) -> Vec<String> {
    let is_pipe_ended = |c| c == '|';
    parse_argv(parse_seps(s, is_pipe_ended))
}

pub fn parse_cmd_args(s: &str) -> Vec<String> {
    let cmd_argv = parse_argv(parse_seps(s, char::is_whitespace));
    let mut ret = Vec::new();
    for arg in cmd_argv {
        ret.push(trim_quotes(&arg));
    }
    ret
}

pub fn trim_quotes(s: &str) -> String {
    let mut iter = s.chars().peekable();
    let mut ret = String::new();
    while let Some(c) = iter.next() {
        if c == '\\' && iter.peek() == Some(&'"') {
            ret.push('\\'); ret.push('"');
            iter.next();
            continue;
        }
        if c == '"' { continue; }
        ret.push(c);
    }
    ret
}

fn parse_seps<F>(s: &str, func: F) -> String
    where F: Fn(char) -> bool {
    let mut in_single_quote = false;
    let mut in_double_quote = false;
    let mut ret = String::new();
    let mut iter = s.chars().peekable();
    while let Some(c) = iter.next() {
        if c == '\\' && iter.peek() == Some(&'"') {
            ret.push('\\'); ret.push('"');
            iter.next();
            continue;
        }
        if c == '\\' && iter.peek() == Some(&'\'') {
            ret.push('\\'); ret.push('\'');
            iter.next();
            continue;
        }

        if c == '"' && !in_single_quote {
            in_double_quote = !in_double_quote;
        } else if c == '\'' && !in_double_quote {
            in_single_quote = !in_single_quote;
        }

        if (func(c)) && !in_single_quote && !in_double_quote {
            ret.push('\n');
        } else {
            ret.push(c);
        }
    }
    ret
}

fn parse_argv(s: String) -> Vec<String> {
    s.split("\n")
        .filter(|s| !s.trim().is_empty())
        .map(|s| s.to_owned())
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_parse_cmds() {
        let cmds_str = "ls -a; echo \"hello\";";
        let cmds_with_lines = parse_cmds(cmds_str);
        assert_eq!(cmds_with_lines, &["ls -a",  " echo \"hello\""]);
    }

    #[test]
    fn test_parse_cmd_args() {
        let cmd_str = "mkdir   /tmp/\"my folder\"";
        let cmd_args = parse_cmd_args(cmd_str);
        assert_eq!(cmd_args, &["mkdir", "/tmp/my folder"]);
    }
}