use clap::Args;
use clap_complete::ArgValueCandidates;
use console::Style;
use indicatif::HumanCount;
use log::{debug, trace, warn};
use std::error::Error;
use std::io::Write;
use std::path::PathBuf;
use wildmatch::WildMatch;
use crate::MultiProgressContainer;
use crate::cli::{self, GlobalOptions, autocomplete};
use crate::project::{Project, Status};
use crate::ui::{Alignment, Item, Row, Table};
use crate::workflow::ResourceCost;
#[derive(Args, Debug)]
pub struct Arguments {
#[arg(short, long, value_name = "pattern", default_value_t=String::from("*"), display_order=0,
add=ArgValueCandidates::new(autocomplete::get_action_candidates))]
action: String,
#[arg(long, display_order = 0)]
no_header: bool,
#[arg(add=ArgValueCandidates::new(autocomplete::get_directory_candidates))]
directories: Vec<PathBuf>,
#[arg(long, display_order = 0, conflicts_with = "all")]
completed: bool,
#[arg(long, display_order = 0, conflicts_with = "all")]
submitted: bool,
#[arg(long, display_order = 0, conflicts_with = "all")]
eligible: bool,
#[arg(long, display_order = 0, conflicts_with = "all")]
waiting: bool,
#[arg(long, display_order = 0)]
all: bool,
}
fn make_row(action_name: &str, status: &Status, cost: &ResourceCost) -> Vec<Item> {
let mut result = Vec::with_capacity(6);
result.push(Item::new(action_name.to_string(), Style::new().bold()));
result.push(
Item::new(
HumanCount(status.completed.len() as u64).to_string(),
Style::new().green().bold(),
)
.with_alignment(Alignment::Right),
);
result.push(
Item::new(
HumanCount(status.submitted.len() as u64).to_string(),
Style::new().yellow().bold(),
)
.with_alignment(Alignment::Right),
);
result.push(
Item::new(
HumanCount(status.eligible.len() as u64).to_string(),
Style::new().blue(),
)
.with_alignment(Alignment::Right),
);
result.push(
Item::new(
HumanCount(status.waiting.len() as u64).to_string(),
Style::new().cyan().dim(),
)
.with_alignment(Alignment::Right),
);
if !cost.is_zero() {
result.push(
Item::new(format!("{cost}"), Style::new().italic().dim())
.with_alignment(Alignment::Right),
);
}
result
}
pub fn status<W: Write>(
options: &GlobalOptions,
args: Arguments,
multi_progress: &mut MultiProgressContainer,
output: &mut W,
) -> Result<(), Box<dyn Error>> {
debug!("Showing the workflow's status.");
let mut show_completed = args.completed;
let mut show_submitted = args.submitted;
let mut show_eligible = args.eligible;
let mut show_waiting = args.waiting;
if !show_completed && !show_submitted && !show_eligible && !show_waiting {
show_completed = true;
show_submitted = true;
show_eligible = true;
show_waiting = true;
}
let action_matcher = WildMatch::new(&args.action);
let mut project = Project::open(options.io_threads, &options.cluster, multi_progress)?;
let query_directories =
cli::parse_directories(args.directories, || Ok(project.state().list_directories()))?;
let mut table = Table::new().with_hide_header(args.no_header);
let underlined = Style::new().underlined();
table.header = vec![
Item::new("Action".to_string(), underlined.clone()),
Item::new("Completed".to_string(), underlined.clone()).with_alignment(Alignment::Right),
Item::new("Submitted".to_string(), underlined.clone()).with_alignment(Alignment::Right),
Item::new("Eligible".to_string(), underlined.clone()).with_alignment(Alignment::Right),
Item::new("Waiting".to_string(), underlined.clone()).with_alignment(Alignment::Right),
Item::new("Remaining cost".to_string(), underlined.clone())
.with_alignment(Alignment::Right),
];
let mut matching_action_count = 0;
for action in &project.workflow().action {
if !action_matcher.matches(action.name()) {
trace!(
"Skipping action '{}'. It does not match the pattern '{}'.",
action.name(),
args.action
);
continue;
}
matching_action_count += 1;
let matching_directories =
project.find_matching_directories(action, query_directories.clone())?;
let status = project.separate_by_status(action, matching_directories)?;
let mut combined_directories = Vec::with_capacity(
status.submitted.len() + status.eligible.len() + status.waiting.len(),
);
combined_directories.extend(status.submitted.clone());
combined_directories.extend(status.eligible.clone());
combined_directories.extend(status.waiting.clone());
let groups = project.separate_into_groups(action, combined_directories.clone())?;
let mut cost = ResourceCost::new();
for group in groups {
cost = cost + action.resources.cost(group.len());
}
if args.all
|| (!status.completed.is_empty() && show_completed)
|| (!status.submitted.is_empty() && show_submitted)
|| (!status.eligible.is_empty() && show_eligible)
|| (!status.waiting.is_empty() && show_waiting)
{
table
.rows
.push(Row::Items(make_row(action.name(), &status, &cost)));
}
}
if matching_action_count == 0 {
warn!("No actions match '{}'.", args.action);
} else {
table.write(output)?;
output.flush()?;
}
project.close(multi_progress)?;
Ok(())
}