boltbuild 0.1.0

BoltBuild is a programmable build system.
Documentation
use super::{Task, ENV_RE, SPLIT_RE};
use regex::Captures;
use subprocess::ExitStatus;

impl Task {
    pub(crate) fn run_command(
        &self,
        command: &str,
        extra_args: Vec<String>,
    ) -> (u32, String, String) {
        let mut command_line = Vec::new();

        for argument in SPLIT_RE.split(command) {
            if let Some(captures) = ENV_RE.captures(argument) {
                match self.expand(argument, captures) {
                    Ok(value) => command_line.extend(value),
                    Err(message) => return (1, message, command.to_string()),
                }
            } else {
                command_line.push(argument.to_string());
            }
        }
        command_line.extend(extra_args);

        let command = command_line.join(" ");

        match subprocess::Popen::create(
            &command_line,
            subprocess::PopenConfig {
                stdin: subprocess::Redirection::None,
                stdout: subprocess::Redirection::Pipe,
                stderr: subprocess::Redirection::Merge,
                ..subprocess::PopenConfig::default()
            },
        ) {
            Ok(mut subprocess) => match subprocess.communicate(None) {
                Ok(result) => match subprocess.wait() {
                    Ok(status) => match status {
                        ExitStatus::Exited(return_code) => {
                            (return_code, result.0.unwrap(), command)
                        }
                        _ => (1, result.0.unwrap(), command),
                    },
                    Err(error) => (1, error.to_string(), command),
                },
                Err(error) => (1, error.to_string(), command),
            },
            Err(error) => (1, error.to_string(), command),
        }
    }

    fn expand(&self, argument: &str, capture: Captures) -> Result<Vec<String>, String> {
        let mut env = self.env.lock().unwrap();
        let var = capture.get(2).unwrap();
        let var_name = var.as_str();
        let value = env.get(var_name);
        let start = capture.get(0).unwrap().start();
        let end = capture.get(0).unwrap().end();
        if value.is_none() {
            if start != 0 || end != argument.len() {
                let mut result = argument[0..start].to_string();
                result.push_str(&argument[end..argument.len()]);
                Ok(vec![result])
            } else {
                Ok(Vec::new())
            }
        } else {
            let mut result = value.as_vec();
            if let Some(index) = capture.get(4) {
                let index = index.as_str().parse::<usize>().unwrap();
                if index >= result.len() {
                    return Err(format!(
                        "requesting {}[{}] but the list has {} elements.",
                        var_name,
                        index,
                        result.len()
                    ));
                }
                result = vec![result[index].clone()];
            }

            if !result.is_empty() {
                if let Some(pattern) = capture.get(1) {
                    let var_name = &pattern.as_str()[0..pattern.len() - 1];
                    let pattern = env.get(var_name);
                    if pattern.is_list() {
                        let mut flattened_list = Vec::new();
                        let pattern = pattern.as_vec();
                        for a in result {
                            if start != 0 {
                                flattened_list.push(argument[0..start].to_string());
                            }

                            flattened_list.extend(pattern.clone());
                            flattened_list.push(a);

                            if end != argument.len() {
                                flattened_list.push(argument[0..start].to_string());
                            }
                        }
                        result = flattened_list;
                    } else {
                        let pattern = pattern.as_string();
                        if !pattern.contains("%s") {
                            for a in &mut result {
                                *a = format!(
                                    "{}{}{}{}",
                                    &argument[0..start],
                                    pattern,
                                    a,
                                    &argument[end..argument.len()]
                                );
                            }
                        } else {
                            for a in &mut result {
                                *a = format!(
                                    "{}{}{}",
                                    &argument[0..start],
                                    pattern.replace("%s", a),
                                    &argument[end..argument.len()]
                                );
                            }
                        }
                    }
                } else {
                    for a in &mut result {
                        *a = format!(
                            "{}{}{}",
                            &argument[0..start],
                            a,
                            &argument[end..argument.len()]
                        );
                    }
                }
                Ok(result)
            } else if start != 0 || end != argument.len() {
                let mut result = argument[0..start].to_string();
                result.push_str(&argument[end..argument.len()]);
                Ok(vec![result])
            } else {
                Ok(Vec::new())
            }
        }
    }
}