lmrc-cli 0.3.16

CLI tool for scaffolding LMRC Stack infrastructure projects
Documentation
use colored::Colorize;
use lmrc_config_validator::LmrcConfig;
use std::path::PathBuf;

use crate::error::{CliError, Result};
use crate::generator;

/// Add an application to the existing project
pub async fn app(name: String, app_type: Option<String>) -> Result<()> {
    println!("{}", "Adding application to project...".green().bold());

    // Load existing config
    let config_path = PathBuf::from("lmrc.toml");
    if !config_path.exists() {
        return Err(CliError::Config(
            "lmrc.toml not found. Run this command from project root or create project first."
                .to_string(),
        ));
    }

    // Read and parse TOML without validation
    let config_str = std::fs::read_to_string(&config_path)?;
    let mut config: LmrcConfig = toml::from_str(&config_str)
        .map_err(|e| CliError::Config(format!("Failed to parse config: {}", e)))?;

    // Determine app type
    let app_type = if let Some(t) = app_type {
        Some(parse_app_type(&t)?)
    } else {
        None
    };

    // Add app to config
    config.apps.applications.push(
        lmrc_config_validator::ApplicationEntry {
            name: name.clone(),
            app_type: app_type.clone(),
            docker: None,
            deployment: None,
        },
    );

    // Save updated config (serialize directly without validation)
    let config_content = toml::to_string_pretty(&config)
        .map_err(|e| CliError::Config(format!("Failed to serialize config: {}", e)))?;
    std::fs::write(&config_path, config_content)?;

    // Generate the app
    let project_path = std::env::current_dir()?;
    generator::apps::generate_single_app(&project_path, &name, app_type.as_ref())?;

    // Update workspace Cargo.toml to include new app
    generator::workspace::generate_workspace_toml(&project_path, &config)?;

    println!(
        "\n{} Application '{}' added successfully!",
        "".green().bold(),
        name.cyan()
    );
    println!("\nNext steps:");
    println!("  1. Review apps/{}/", name);
    println!("  2. Run: cargo build -p {}", name);

    Ok(())
}

/// Add infrastructure pipeline to the project
pub async fn pipeline() -> Result<()> {
    println!("{}", "Adding infrastructure pipeline...".green().bold());

    let config_path = PathBuf::from("lmrc.toml");
    if !config_path.exists() {
        return Err(CliError::Config(
            "lmrc.toml not found. Run this command from project root.".to_string(),
        ));
    }

    // Read and parse TOML without validation
    let config_str = std::fs::read_to_string(&config_path)?;
    let config: LmrcConfig = toml::from_str(&config_str)
        .map_err(|e| CliError::Config(format!("Failed to parse config: {}", e)))?;
    let project_path = std::env::current_dir()?;

    generator::pipeline::generate_pipeline_app(&project_path, &config)?;

    // Update workspace Cargo.toml
    generator::workspace::generate_workspace_toml(&project_path, &config)?;

    println!(
        "\n{} Infrastructure pipeline added successfully!",
        "".green().bold()
    );
    println!("\nNext steps:");
    println!("  1. Review infra/pipeline/");
    println!("  2. Run: cargo build -p pipeline");

    Ok(())
}

/// Add GitLab CI/CD configuration
pub async fn ci() -> Result<()> {
    println!("{}", "Adding GitLab CI/CD configuration...".green().bold());

    let config_path = PathBuf::from("lmrc.toml");
    if !config_path.exists() {
        return Err(CliError::Config(
            "lmrc.toml not found. Run this command from project root.".to_string(),
        ));
    }

    // Read and parse TOML without validation
    let config_str = std::fs::read_to_string(&config_path)?;
    let config: LmrcConfig = toml::from_str(&config_str)
        .map_err(|e| CliError::Config(format!("Failed to parse config: {}", e)))?;
    let project_path = std::env::current_dir()?;

    generator::gitlab_ci::generate_gitlab_ci(&project_path, &config)?;

    println!(
        "\n{} GitLab CI/CD configuration added successfully!",
        "".green().bold()
    );
    println!("\nNext steps:");
    println!("  1. Review .gitlab-ci.yml");
    println!("  2. Configure CI/CD variables in GitLab (see docs/SECRETS.md)");

    Ok(())
}

/// Add project documentation
pub async fn docs() -> Result<()> {
    println!("{}", "Adding project documentation...".green().bold());

    let config_path = PathBuf::from("lmrc.toml");
    if !config_path.exists() {
        return Err(CliError::Config(
            "lmrc.toml not found. Run this command from project root.".to_string(),
        ));
    }

    // Read and parse TOML without validation
    let config_str = std::fs::read_to_string(&config_path)?;
    let config: LmrcConfig = toml::from_str(&config_str)
        .map_err(|e| CliError::Config(format!("Failed to parse config: {}", e)))?;
    let project_path = std::env::current_dir()?;

    generator::documentation::generate_docs(&project_path, &config)?;

    println!(
        "\n{} Documentation added successfully!",
        "".green().bold()
    );
    println!("\nNext steps:");
    println!("  1. Review docs/");
    println!("  2. Update README.md with project-specific information");

    Ok(())
}

fn parse_app_type(s: &str) -> Result<lmrc_config_validator::AppType> {
    match s.to_lowercase().as_str() {
        "api" => Ok(lmrc_config_validator::AppType::Api),
        "basic" => Ok(lmrc_config_validator::AppType::Basic),
        "gateway" => Ok(lmrc_config_validator::AppType::Gateway),
        "migrator" => Ok(lmrc_config_validator::AppType::Migrator),
        _ => Err(CliError::Config(format!(
            "Invalid app type '{}'. Valid types: api, basic, gateway, migrator",
            s
        ))),
    }
}