mecha10-cli 0.1.47

Mecha10 CLI tool
Documentation
//! Lint command handler
//!
//! Orchestrates code linting checks.

use crate::context::CliContext;
use crate::paths;
use anyhow::Result;

/// Arguments for lint command
#[derive(Debug, Clone)]
pub struct LintArgs {
    pub fix: bool,
}

/// Handle lint command
///
/// Runs code linting checks across all project code.
///
/// # Arguments
///
/// * `ctx` - CLI execution context
/// * `args` - Lint command arguments
pub async fn handle_lint(ctx: &mut CliContext, args: &LintArgs) -> Result<()> {
    println!();
    println!("🔍 Code Linting");
    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    println!();

    // Check project initialization
    if !ctx.is_project_initialized() {
        return Err(anyhow::anyhow!(
            "Not in a Mecha10 project directory.\n\
             Run 'mecha10 init' to create a new project."
        ));
    }

    let project = ctx.project()?;
    println!("Project: {}", project.root().display());
    println!();

    let mut all_passed = true;

    // Rust linting (clippy)
    println!("Running Rust linting (clippy)...");
    let clippy_result = if args.fix {
        std::process::Command::new("cargo")
            .args(["clippy", "--fix", "--allow-dirty", "--allow-staged"])
            .current_dir(project.root())
            .status()
    } else {
        std::process::Command::new("cargo")
            .args(["clippy", "--all-targets", "--", "-D", "warnings"])
            .current_dir(project.root())
            .status()
    };

    match clippy_result {
        Ok(status) if status.success() => {
            println!("✅ Rust linting passed");
        }
        Ok(_) => {
            println!("❌ Rust linting failed");
            all_passed = false;
        }
        Err(e) => {
            println!("âš ī¸  Could not run clippy: {}", e);
            println!("   Install with: rustup component add clippy");
        }
    }
    println!();

    // Python linting (ruff)
    println!("Running Python linting (ruff)...");
    let python_dir = project.root().join(paths::framework::TASKRUNNER_DIR);
    if python_dir.exists() {
        let ruff_result = if args.fix {
            std::process::Command::new("ruff")
                .args(["check", "--fix", "."])
                .current_dir(&python_dir)
                .status()
        } else {
            std::process::Command::new("ruff")
                .args(["check", "."])
                .current_dir(&python_dir)
                .status()
        };

        match ruff_result {
            Ok(status) if status.success() => {
                println!("✅ Python linting passed");
            }
            Ok(_) => {
                println!("❌ Python linting failed");
                all_passed = false;
            }
            Err(e) => {
                println!("âš ī¸  Could not run ruff: {}", e);
                println!("   Install with: pip install ruff");
            }
        }
    } else {
        println!("â„šī¸  No Python code found, skipping");
    }
    println!();

    // GDScript linting
    println!("Running GDScript linting...");
    // Determine Godot project path based on context
    let godot_dir = if let Ok(framework_path) = std::env::var("MECHA10_FRAMEWORK_PATH") {
        // Framework dev mode - use monorepo path
        std::path::PathBuf::from(framework_path).join(paths::framework::SIMULATION_GODOT_DIR)
    } else {
        // Generated project mode - use relative path
        project.root().join(paths::project::SIMULATION_GODOT_DIR)
    };

    if godot_dir.exists() {
        println!("â„šī¸  GDScript linting not yet implemented");
        println!("   Consider using gdlint or gdscript-linter");
    } else {
        println!("â„šī¸  No GDScript code found, skipping");
    }
    println!();

    // File naming conventions (ls-lint) - only for monorepo/projects with ls-lint config
    println!("Checking file naming conventions...");
    let ls_lint_config = project.root().join(paths::meta::LS_LINT_CONFIG);
    if ls_lint_config.exists() {
        let ls_lint_result = std::process::Command::new("pnpm")
            .args(["lint:ls"])
            .current_dir(project.root())
            .status();

        match ls_lint_result {
            Ok(status) if status.success() => {
                println!("✅ File naming conventions passed");
            }
            Ok(_) => {
                println!("❌ File naming conventions failed");
                all_passed = false;
            }
            Err(_) => {
                println!("â„šī¸  ls-lint not available, skipping");
            }
        }
    } else {
        println!("â„šī¸  No ls-lint config found, skipping");
    }
    println!();

    // Summary
    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    if all_passed {
        println!("✅ All linting checks passed!");
    } else {
        println!("❌ Some linting checks failed");
        if !args.fix {
            println!();
            println!("Run with --fix to automatically fix issues:");
            println!("  mecha10 lint --fix");
        }
        return Err(anyhow::anyhow!("Linting failed"));
    }
    println!();

    Ok(())
}