use std::sync::atomic;
use anyhow::Result;
use clap::{Parser, ValueHint};
use rayon::prelude::*;
use crate::cli::GardenOptions;
use crate::{cmd, constants, errors, model, query};
#[derive(Parser, Clone, Debug)]
#[command(author, about, long_about)]
pub struct ExecOptions {
#[arg(long, short, default_value = "*")]
pub(crate) trees: String,
#[arg(long, short = 'N', short_alias = 'n')]
pub(crate) dry_run: bool,
#[arg(long = "jobs", short = 'j', value_name = "JOBS")]
pub(crate) num_jobs: Option<usize>,
#[arg(short, long)]
pub(crate) quiet: bool,
#[arg(short, long, action = clap::ArgAction::Count)]
pub(crate) verbose: u8,
#[arg(value_hint=ValueHint::Other)]
pub(crate) query: String,
#[arg(allow_hyphen_values = true, trailing_var_arg = true, required = true, value_hint=ValueHint::CommandWithArguments)]
pub(crate) command: Vec<String>,
}
pub fn main(app_context: &model::ApplicationContext, exec_options: &mut ExecOptions) -> Result<()> {
exec_options.verbose += app_context.options.verbose;
if app_context.options.debug_level(constants::DEBUG_LEVEL_EXEC) > 0 {
debug!("query: {}", exec_options.query);
debug!("command: {:?}", exec_options.command);
}
exec_options.quiet |= app_context.options.quiet;
exec(app_context, exec_options)
}
fn exec(app_context: &model::ApplicationContext, exec_options: &ExecOptions) -> Result<()> {
let quiet = exec_options.quiet;
let verbose = exec_options.verbose;
let dry_run = exec_options.dry_run;
let query = &exec_options.query;
let tree_pattern = &exec_options.trees;
let command = &exec_options.command;
cmd::initialize_threads_option(exec_options.num_jobs)?;
let config = app_context.get_root_config_mut();
let contexts = query::resolve_trees(app_context, config, None, query);
let pattern = glob::Pattern::new(tree_pattern).unwrap_or_default();
let exit_status = atomic::AtomicI32::new(errors::EX_OK);
if exec_options.num_jobs.is_some() {
contexts.par_iter().for_each(|context| {
let app_context_clone = app_context.clone();
let app_context = &app_context_clone;
if !model::is_valid_context(app_context, &pattern, context) {
return;
}
if let Err(errors::GardenError::ExitStatus(status)) = cmd::exec_in_context(
app_context,
app_context.get_root_config(),
context,
quiet,
verbose,
dry_run,
command,
) {
exit_status.store(status, atomic::Ordering::Release);
}
});
} else {
for context in &contexts {
if !model::is_valid_context(app_context, &pattern, context) {
continue;
}
if let Err(errors::GardenError::ExitStatus(status)) = cmd::exec_in_context(
app_context,
config,
context,
quiet,
verbose,
dry_run,
command,
) {
exit_status.store(status, atomic::Ordering::Release);
}
}
}
errors::exit_status_into_result(exit_status.load(atomic::Ordering::Acquire))
}