Skip to main content

nsg_cli/commands/
submit.rs

1use crate::client::NsgClient;
2use crate::config::Credentials;
3use anyhow::{Context, Result};
4use clap::Args;
5use colored::Colorize;
6use std::path::PathBuf;
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!(
42            "Size:     {} bytes",
43            format_size(std::fs::metadata(&self.zip_file)?.len())
44        );
45        println!();
46
47        println!("{} Submitting job to NSG...", "→".yellow().bold());
48
49        let status = client
50            .submit_job(&self.zip_file, &self.tool)
51            .context("Failed to submit job")?;
52
53        println!();
54        println!("{}", "=".repeat(80).green());
55        println!("{} Job submitted successfully!", "✓".green().bold());
56        println!("{}", "=".repeat(80).green());
57        println!();
58        println!("Job ID:   {}", status.job_id.cyan().bold());
59        println!("Stage:    {}", status.job_stage.bold());
60        println!("URL:      {}", status.self_uri.dimmed());
61
62        if let Some(date) = &status.date_submitted {
63            println!("Submitted: {}", date);
64        }
65
66        println!();
67        println!("{}", "Next Steps:".bold());
68        println!("  1. Check job status:");
69        println!("     {}", format!("nsg status {}", status.job_id).cyan());
70        println!();
71        println!("  2. When completed, download results:");
72        println!("     {}", format!("nsg download {}", status.job_id).cyan());
73        println!();
74        println!("  3. View all jobs:");
75        println!("     {}", "nsg list".cyan());
76        println!();
77        println!("{}", "NSG Portal:".bold());
78        println!("  {}", "https://www.nsgportal.org/".cyan());
79        println!();
80
81        Ok(())
82    }
83}
84
85fn format_size(bytes: u64) -> String {
86    const KB: u64 = 1024;
87    const MB: u64 = KB * 1024;
88    const GB: u64 = MB * 1024;
89
90    if bytes >= GB {
91        format!("{:.2} GB", bytes as f64 / GB as f64)
92    } else if bytes >= MB {
93        format!("{:.2} MB", bytes as f64 / MB as f64)
94    } else if bytes >= KB {
95        format!("{:.2} KB", bytes as f64 / KB as f64)
96    } else {
97        format!("{} B", bytes)
98    }
99}