mod cli;
mod execution;
mod file_requirements;
mod model_injection;
pub mod models;
mod output;
mod utils;
mod version_requirements;
use crate::cli::{cli_builder, construct_cli};
use crate::execution::{execute_stages, execute_tasks, PassFail, TaskResult};
use crate::model_injection::{inject_task_metadata, inject_template_values};
use crate::models::Validate;
use std::collections::HashMap;
use std::error::Error;
type RoxResult<T> = Result<T, Box<dyn Error>>;
fn get_filepath() -> String {
let cli = cli_builder();
let cli_matches = cli.clone().arg_required_else_help(false).get_matches();
cli_matches.get_one::<String>("roxfile").unwrap().to_owned()
}
pub fn rox() -> RoxResult<()> {
let start = std::time::Instant::now();
let file_path = get_filepath();
let roxfile = utils::parse_file_contents(utils::load_file(&file_path));
roxfile.validate()?;
utils::print_horizontal_rule();
let tasks = inject_task_metadata(roxfile.tasks, &file_path);
let pipelines = roxfile.pipelines;
let cli = construct_cli(tasks.clone(), pipelines.clone(), &file_path);
let cli_matches = cli.get_matches();
if !cli_matches.get_flag("skip-checks") {
if roxfile.version_requirements.is_some() {
for version_check in roxfile.version_requirements.into_iter().flatten() {
version_requirements::check_version(version_check);
}
}
if roxfile.file_requirements.is_some() {
for requirement in roxfile.file_requirements.into_iter().flatten() {
file_requirements::handle_file_requirement(requirement);
}
}
}
let template_map: HashMap<String, models::Template> = std::collections::HashMap::from_iter(
roxfile
.templates
.into_iter()
.flatten()
.map(|template| (template.name.clone(), template)),
);
let task_map: HashMap<String, models::Task> = std::collections::HashMap::from_iter(
tasks
.into_iter()
.map(|task| match task.uses.clone() {
Some(task_use) => {
inject_template_values(task, template_map.get(&task_use).unwrap())
}
None => task,
})
.map(|task| (task.name.clone(), task)),
);
let pipeline_map: HashMap<String, models::Pipeline> = std::collections::HashMap::from_iter(
pipelines
.into_iter()
.flatten()
.map(|pipeline| (pipeline.name.clone(), pipeline)),
);
let results: Vec<Vec<TaskResult>> = match cli_matches.subcommand_name().unwrap() {
"pl" => {
let (_, args) = cli_matches.subcommand().unwrap();
let pipeline_name = args.subcommand_name().unwrap();
let parallel = args.get_flag("parallel");
execute_stages(
pipeline_map.get(pipeline_name).unwrap().stages.clone(),
&task_map,
parallel,
)
}
"task" => {
let (_, args) = cli_matches.subcommand().unwrap();
let task_name = args.subcommand_name().unwrap().to_owned();
vec![execute_tasks(vec![task_name], &task_map, false)]
}
command => {
println!("'{}' is not a valid subcommand!", command);
std::process::exit(2);
}
};
output::display_execution_results(results.clone());
println!(
"> Total elapsed time: {}s | {}ms",
start.elapsed().as_secs(),
start.elapsed().as_millis(),
);
nonzero_exit_if_failure(results);
Ok(())
}
pub fn nonzero_exit_if_failure(results: Vec<Vec<TaskResult>>) {
for result in results.iter().flatten() {
if result.result == PassFail::Fail {
std::process::exit(2)
}
}
}