nsg-cli 0.1.2

CLI tool for the Neuroscience Gateway (NSG) BRAIN Initiative API
Documentation
use crate::client::NsgClient;
use crate::config::Credentials;
use anyhow::Result;
use clap::Args;
use colored::Colorize;
use std::io::{self, Write};

#[derive(Debug, Args)]
pub struct LoginCommand {
    #[arg(short, long, help = "NSG username")]
    username: Option<String>,

    #[arg(short, long, help = "NSG password")]
    password: Option<String>,

    #[arg(short, long, help = "NSG application key")]
    app_key: Option<String>,

    #[arg(long, help = "Skip connection test")]
    no_verify: bool,
}

impl LoginCommand {
    pub fn execute(self) -> Result<()> {
        println!("{}", "NSG Login".bold().cyan());
        println!("{}", "=".repeat(60).cyan());
        println!();

        let username = self.get_or_prompt_username()?;
        let password = self.get_or_prompt_password()?;
        let app_key = self.get_or_prompt_app_key()?;

        println!();
        println!("{} Saving credentials...", "".cyan());

        let credentials = Credentials::new(username, password, app_key);

        if !self.no_verify {
            println!("{} Testing connection to NSG...", "".cyan());
            let client = NsgClient::new(credentials.clone())?;

            match client.test_connection() {
                Ok(_) => {
                    println!("{} Connection successful!", "".green().bold());
                }
                Err(e) => {
                    eprintln!();
                    eprintln!("{} {}", "".red().bold(), "Authentication failed!".red());
                    eprintln!();
                    eprintln!("Error: {}", e);
                    eprintln!();
                    eprintln!("Please check your credentials:");
                    eprintln!("  1. Username and password are correct");
                    eprintln!("  2. Application key is valid");
                    eprintln!("  3. Your NSG account is active");
                    eprintln!();
                    eprintln!(
                        "Get credentials at: {}",
                        "https://www.nsgportal.org/".cyan()
                    );
                    anyhow::bail!("Login failed");
                }
            }
        }

        credentials.save()?;

        println!();
        println!("{}", "=".repeat(60).green());
        println!(
            "{} {}",
            "".green().bold(),
            "Login successful!".green().bold()
        );
        println!("{}", "=".repeat(60).green());
        println!();
        println!(
            "Credentials saved to: {}",
            Credentials::credentials_location().cyan()
        );
        println!();
        println!("You can now use:");
        println!("  {} - List your NSG jobs", "nsg list".cyan());
        println!("  {} - Check job status", "nsg status <job_id>".cyan());
        println!(
            "  {} - Submit a new job",
            "nsg submit <zip_file> --tool <tool>".cyan()
        );
        println!(
            "  {} - Download job results",
            "nsg download <job_id>".cyan()
        );
        println!();

        Ok(())
    }

    fn get_or_prompt_username(&self) -> Result<String> {
        if let Some(username) = &self.username {
            return Ok(username.clone());
        }

        print!("NSG Username: ");
        io::stdout().flush()?;

        let mut input = String::new();
        io::stdin().read_line(&mut input)?;
        let username = input.trim().to_string();

        if username.is_empty() {
            anyhow::bail!("Username cannot be empty");
        }

        Ok(username)
    }

    fn get_or_prompt_password(&self) -> Result<String> {
        if let Some(password) = &self.password {
            return Ok(password.clone());
        }

        print!("NSG Password: ");
        io::stdout().flush()?;
        let password = rpassword::read_password()?;

        if password.is_empty() {
            anyhow::bail!("Password cannot be empty");
        }

        Ok(password)
    }

    fn get_or_prompt_app_key(&self) -> Result<String> {
        if let Some(app_key) = &self.app_key {
            return Ok(app_key.clone());
        }

        print!("NSG Application Key: ");
        io::stdout().flush()?;

        let mut input = String::new();
        io::stdin().read_line(&mut input)?;
        let app_key = input.trim().to_string();

        if app_key.is_empty() {
            anyhow::bail!("Application key cannot be empty");
        }

        Ok(app_key)
    }
}