biovault 0.1.9

A bioinformatics data vault CLI tool
Documentation
use crate::config::{get_biovault_home, is_syftbox_env, Config};
use crate::Result;
use dialoguer::{theme::ColorfulTheme, Confirm, Input};
use std::env;
use std::fs;
use std::io::IsTerminal;
use tracing::info;

pub async fn execute(email: Option<&str>) -> Result<()> {
    // Get the BioVault home directory (respects env vars)
    let biovault_dir = get_biovault_home()?;

    // Determine email with priority:
    // 1. CLI argument (if provided)
    // 2. SYFTBOX_EMAIL env var (confirm on TTY, auto-accept non-interactive)
    // 3. Interactive prompt if on a TTY
    // 4. Error if neither provided and not a TTY
    let email = if let Some(email) = email {
        email.to_string()
    } else if let Ok(syftbox_email) = env::var("SYFTBOX_EMAIL") {
        if std::io::stdin().is_terminal() {
            println!("Detected SyftBox environment email: {}", syftbox_email);
            let use_syftbox = Confirm::with_theme(&ColorfulTheme::default())
                .with_prompt("Use this email for BioVault?")
                .default(true)
                .interact()
                .map_err(|e| anyhow::anyhow!("Prompt error: {}", e))?;

            if use_syftbox {
                syftbox_email
            } else {
                Input::with_theme(&ColorfulTheme::default())
                    .with_prompt("Enter email address")
                    .interact_text()
                    .map_err(|e| anyhow::anyhow!("Prompt error: {}", e))?
            }
        } else {
            // Non-interactive environment: auto-accept SYFTBOX_EMAIL
            syftbox_email
        }
    } else if std::io::stdin().is_terminal() {
        Input::with_theme(&ColorfulTheme::default())
            .with_prompt("Enter email address")
            .interact_text()
            .map_err(|e| anyhow::anyhow!("Prompt error: {}", e))?
    } else {
        return Err(anyhow::anyhow!(
            "email is required. Provide as 'bv init <email>' or set SYFTBOX_EMAIL"
        )
        .into());
    };

    if !biovault_dir.exists() {
        fs::create_dir_all(&biovault_dir)?;
        info!("Created directory: {:?}", biovault_dir);
    }

    let config_file = biovault_dir.join("config.yaml");

    if config_file.exists() {
        println!(
            "Configuration file already exists at: {}",
            config_file.display()
        );
        println!("Skipping initialization.");
    } else {
        // Show detected environment if in SyftBox virtualenv
        if is_syftbox_env() {
            println!("✓ Running in SyftBox virtualenv");
            if let Ok(data_dir) = env::var("SYFTBOX_DATA_DIR") {
                println!("  SyftBox data directory: {}", data_dir);
            }
            println!("  BioVault home: {}", biovault_dir.display());
        }

        // Auto-detect SyftBox config if it exists
        let syftbox_config = if let Ok(config_path) = env::var("SYFTBOX_CONFIG_PATH") {
            if std::path::Path::new(&config_path).exists() {
                println!("✓ Detected SyftBox config at: {}", config_path);
                Some(config_path)
            } else {
                None
            }
        } else {
            let home_dir = dirs::home_dir()
                .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?;
            let default_syftbox = home_dir.join(".syftbox").join("config.json");
            if default_syftbox.exists() {
                Some(default_syftbox.to_string_lossy().to_string())
            } else {
                None
            }
        };

        let config = Config {
            email: email.to_string(),
            syftbox_config: syftbox_config.clone(),
        };

        config.save(&config_file)?;

        println!("✓ BioVault initialized successfully!");
        println!("  Configuration saved to: {}", config_file.display());
        println!("  Email: {}", email);
        if let Some(ref syftbox_cfg) = syftbox_config {
            println!("  SyftBox config: {}", syftbox_cfg);
        }

        // Create env/default directory and copy templates
        let env_dir = biovault_dir.join("env").join("default");
        if !env_dir.exists() {
            fs::create_dir_all(&env_dir)?;
            info!("Created environment directory: {:?}", env_dir);
        }

        // Copy template.nf
        let template_nf_content = include_str!("../../templates/template.nf");
        let template_nf_path = env_dir.join("template.nf");
        fs::write(&template_nf_path, template_nf_content)?;
        info!("Created template.nf at: {:?}", template_nf_path);

        // Copy nextflow.config
        let nextflow_config_content = include_str!("../../templates/nextflow.config");
        let nextflow_config_path = env_dir.join("nextflow.config");
        fs::write(&nextflow_config_path, nextflow_config_content)?;
        info!("Created nextflow.config at: {:?}", nextflow_config_path);

        println!("✓ Nextflow templates installed to: {}", env_dir.display());
    }

    Ok(())
}