nsg_cli/commands/
submit.rs

1use anyhow::{Context, Result};
2use clap::Args;
3use colored::Colorize;
4use std::path::PathBuf;
5use crate::client::NsgClient;
6use crate::config::Credentials;
7
8#[derive(Debug, Args)]
9pub struct SubmitCommand {
10    #[arg(help = "Path to ZIP file containing job data")]
11    zip_file: PathBuf,
12
13    #[arg(short, long, default_value = "PY_EXPANSE", help = "NSG tool to use")]
14    tool: String,
15
16    #[arg(long, help = "Don't wait for job submission confirmation")]
17    no_wait: bool,
18}
19
20impl SubmitCommand {
21    pub fn execute(self) -> Result<()> {
22        if !self.zip_file.exists() {
23            anyhow::bail!("ZIP file not found: {}", self.zip_file.display());
24        }
25
26        if !self.zip_file.extension().map_or(false, |ext| ext == "zip") {
27            eprintln!("{} File does not have .zip extension", "⚠".yellow());
28            eprintln!("   Continuing anyway...");
29            eprintln!();
30        }
31
32        let credentials = Credentials::load()?;
33        let client = NsgClient::new(credentials.clone())?;
34
35        println!("{}", "NSG Job Submission".bold().cyan());
36        println!("{}", "=".repeat(80).cyan());
37        println!();
38        println!("Tool:     {}", self.tool.bold());
39        println!("User:     {}", credentials.username.cyan());
40        println!("File:     {}", self.zip_file.display().to_string().cyan());
41        println!("Size:     {} bytes", format_size(std::fs::metadata(&self.zip_file)?.len()));
42        println!();
43
44        println!("{} Submitting job to NSG...", "→".yellow().bold());
45
46        let status = client
47            .submit_job(&self.zip_file, &self.tool)
48            .context("Failed to submit job")?;
49
50        println!();
51        println!("{}", "=".repeat(80).green());
52        println!("{} Job submitted successfully!", "✓".green().bold());
53        println!("{}", "=".repeat(80).green());
54        println!();
55        println!("Job ID:   {}", status.job_id.cyan().bold());
56        println!("Stage:    {}", status.job_stage.bold());
57        println!("URL:      {}", status.self_uri.dimmed());
58
59        if let Some(date) = &status.date_submitted {
60            println!("Submitted: {}", date);
61        }
62
63        println!();
64        println!("{}", "Next Steps:".bold());
65        println!("  1. Check job status:");
66        println!("     {}", format!("nsg status {}", status.job_id).cyan());
67        println!();
68        println!("  2. When completed, download results:");
69        println!("     {}", format!("nsg download {}", status.job_id).cyan());
70        println!();
71        println!("  3. View all jobs:");
72        println!("     {}", "nsg list".cyan());
73        println!();
74        println!("{}", "NSG Portal:".bold());
75        println!("  {}", "https://www.nsgportal.org/".cyan());
76        println!();
77
78        Ok(())
79    }
80}
81
82fn format_size(bytes: u64) -> String {
83    const KB: u64 = 1024;
84    const MB: u64 = KB * 1024;
85    const GB: u64 = MB * 1024;
86
87    if bytes >= GB {
88        format!("{:.2} GB", bytes as f64 / GB as f64)
89    } else if bytes >= MB {
90        format!("{:.2} MB", bytes as f64 / MB as f64)
91    } else if bytes >= KB {
92        format!("{:.2} KB", bytes as f64 / KB as f64)
93    } else {
94        format!("{} B", bytes)
95    }
96}