use anyhow::Result;
use gflow::core::job::Job;
use gflow::utils::{parse_job_ids, substitute_parameters};
use gflow::{print_field, print_optional_field};
use std::path::PathBuf;
use std::time::SystemTime;
pub async fn handle_show(config_path: &Option<PathBuf>, job_ids_str: String) -> Result<()> {
let client = gflow::create_client(config_path)?;
let job_ids = parse_job_ids(&job_ids_str)?;
for (index, &job_id) in job_ids.iter().enumerate() {
if index > 0 {
println!("\n{}", "=".repeat(80));
println!();
}
let Some(job) = gflow::client::get_job_or_warn(&client, job_id).await? else {
continue;
};
print_job_details(&job);
}
Ok(())
}
fn print_job_details(job: &Job) {
println!("Job Details:");
print_field!("ID", "{}", job.id);
print_field!("State", "{} ({})", job.state, job.state.short_form());
print_field!("Priority", "{}", job.priority);
print_field!("SubmittedBy", "{}", job.submitted_by);
if job.max_retries > 0 {
print_field!("MaxRetries", "{}", job.max_retries);
}
print_optional_field!("GroupID", job.group_id);
print_optional_field!("Script", job.script, |s| s.display());
if let Some(ref command) = job.command {
let has_params = command.contains('{') && !job.parameters.is_empty();
if has_params {
print_field!("Command(template)", "{}", command);
match substitute_parameters(command, &job.parameters) {
Ok(substituted) => print_field!("Command(actual)", "{}", substituted),
Err(e) => print_field!("Command(actual)", "Error: {}", e),
}
} else {
print_field!("Command", "{}", command);
}
}
if !job.parameters.is_empty() {
println!("\nParameters:");
let mut params: Vec<_> = job.parameters.iter().collect();
params.sort_by_key(|(k, _)| *k);
for (key, value) in params {
print_field!(key, "{}", value);
}
}
println!("\nResources:");
print_field!("GPUs", "{}", job.gpus);
print_field!(
"GPUSharing",
"{}",
format_gpu_sharing_mode(job.gpu_sharing_mode)
);
if let Some(gpu_memory_mb) = job.gpu_memory_limit_mb {
print_field!(
"GPUMemoryLimit",
"{}",
gflow::utils::format_memory(gpu_memory_mb)
);
}
print_optional_field!("GPUIDs", job.gpu_ids, |ids| format_ids(ids));
if let Some(memory_mb) = job.memory_limit_mb {
print_field!("MemoryLimit", "{}", gflow::utils::format_memory(memory_mb));
}
print_optional_field!("CondaEnv", job.conda_env);
println!("\nExecution:");
print_field!("WorkingDir", "{}", job.run_dir.display());
print_optional_field!("TmuxSession", job.run_name);
if !job.notifications.is_empty() {
print_field!(
"NotifyEmail",
"{}",
job.notifications
.emails
.iter()
.map(|email| email.as_str())
.collect::<Vec<_>>()
.join(",")
);
if !job.notifications.events.is_empty() {
print_field!(
"NotifyOn",
"{}",
job.notifications
.events
.iter()
.map(|event| event.as_str())
.collect::<Vec<_>>()
.join(",")
);
}
}
let all_deps = job.all_dependency_ids();
if !all_deps.is_empty() || job.task_id.is_some() {
println!("\nDependencies:");
if !all_deps.is_empty() {
print_field!("DependsOn", "{}", format_ids(&all_deps));
if let Some(mode) = job.dependency_mode {
print_field!("Mode", "{:?}", mode);
}
if job.auto_cancel_on_dependency_failure {
print_field!("AutoCancel", "enabled");
}
}
if let Some(task_id) = job.task_id {
print_field!("TaskID", "{}", task_id);
}
}
println!("\nTiming:");
if let Some(time_limit) = job.time_limit {
print_field!("TimeLimit", "{}", gflow::utils::format_duration(time_limit));
}
if let Some(submitted_at) = job.submitted_at {
if let Some(wait_time) = job.wait_time() {
print_field!(
"Submitted",
"{} (waited {})",
format_time(submitted_at),
gflow::utils::format_duration(wait_time)
);
} else {
print_field!("Submitted", "{}", format_time(submitted_at));
}
}
if let Some(started_at) = job.started_at {
if let Some(finished_at) = job.finished_at {
let runtime = finished_at.duration_since(started_at).ok();
print_field!("Started", "{}", format_time(started_at));
if let Some(rt) = runtime {
print_field!(
"Finished",
"{} (runtime {})",
format_time(finished_at),
gflow::utils::format_duration(rt)
);
} else {
print_field!("Finished", "{}", format_time(finished_at));
}
} else if let Ok(elapsed) = SystemTime::now().duration_since(started_at) {
print_field!(
"Started",
"{} (running {} so far)",
format_time(started_at),
gflow::utils::format_duration(elapsed)
);
} else {
print_field!("Started", "{}", format_time(started_at));
}
}
}
fn format_ids(ids: &[u32]) -> String {
ids.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(",")
}
fn format_gpu_sharing_mode(mode: gflow::core::job::GpuSharingMode) -> &'static str {
match mode {
gflow::core::job::GpuSharingMode::Exclusive => "exclusive",
gflow::core::job::GpuSharingMode::Shared => "shared",
}
}
fn format_time(time: SystemTime) -> String {
use chrono::{DateTime, Local};
let datetime: DateTime<Local> = time.into();
datetime.format("%m/%d-%H:%M:%S").to_string()
}