neyuki 0.6.2

A small Lua-like scripting language implemented in Rust
//! The `neyuki pkg` commands.

use std::fs;
use std::path::Path;

use super::manifest::PackageManifest;
use super::store::PackageStore;
use super::version::{Version, VersionReq};

pub fn run_pkg_cli(args: &[String]) -> Result<(), String> {
    if args.is_empty() {
        print_pkg_help();
        return Ok(());
    }

    let subcmd = &args[0];
    match subcmd.as_str() {
        "init" => {
            let name = args.get(1).map(|s| s.as_str()).unwrap_or("my_project");
            init_project(name)
        }
        "add" => {
            let Some(pkg_name) = args.get(1) else {
                return Err("Usage: neyuki pkg add <package_name> [version_req]".to_string());
            };
            let req_str = args.get(2).map(|s| s.as_str()).unwrap_or("*");
            add_dependency(pkg_name, req_str)
        }
        "list" => list_packages(),
        "check" => check_project(),
        "install" => install_packages(),
        "help" | "--help" | "-h" => {
            print_pkg_help();
            Ok(())
        }
        other => Err(format!(
            "unknown pkg command '{}'. Run 'neyuki pkg help' for available commands.",
            other
        )),
    }
}

fn print_pkg_help() {
    println!("Neyuki Package Manager (pkg)");
    println!("Usage: neyuki pkg <command> [args]\n");
    println!("Commands:");
    println!("  init [name]              Initialize a new neyuki.toml in current directory");
    println!("  add <package> [version]  Add a dependency to neyuki.toml");
    println!("  install                  Install dependencies to ./neyuki_packages/");
    println!("  list                     List installed packages in ./neyuki_packages/");
    println!("  check                    Validate neyuki.toml manifest format");
    println!("  help                     Show this help message");
}

fn init_project(name: &str) -> Result<(), String> {
    let manifest_path = Path::new("neyuki.toml");
    if manifest_path.exists() {
        return Err("neyuki.toml already exists in current directory".to_string());
    }

    let manifest = PackageManifest::new(name, Version::new(0, 1, 0));
    fs::write(manifest_path, manifest.serialize())
        .map_err(|e| format!("cannot create neyuki.toml: {}", e))?;

    println!("Initialized new Neyuki package '{}' in neyuki.toml", name);
    Ok(())
}

fn add_dependency(pkg_name: &str, req_str: &str) -> Result<(), String> {
    let manifest_path = Path::new("neyuki.toml");
    let content = if manifest_path.exists() {
        fs::read_to_string(manifest_path).map_err(|e| format!("cannot read neyuki.toml: {}", e))?
    } else {
        return Err("neyuki.toml not found. Run 'neyuki pkg init' first.".to_string());
    };

    let mut manifest = PackageManifest::parse(&content)?;
    let req = VersionReq::parse(req_str)?;
    manifest.add_dependency(pkg_name, req);

    fs::write(manifest_path, manifest.serialize())
        .map_err(|e| format!("failed updating neyuki.toml: {}", e))?;

    println!(
        "Added dependency '{} = \"{}\"' to neyuki.toml",
        pkg_name, req_str
    );
    Ok(())
}

fn list_packages() -> Result<(), String> {
    let store = PackageStore::new(".");
    let installed = store.list_installed()?;
    if installed.is_empty() {
        println!("No packages currently installed in ./neyuki_packages/");
    } else {
        println!("Installed packages ({}):", installed.len());
        for pkg in installed {
            println!("  * {}", pkg);
        }
    }
    Ok(())
}

fn check_project() -> Result<(), String> {
    let manifest_path = Path::new("neyuki.toml");
    if !manifest_path.exists() {
        return Err("neyuki.toml not found in current directory".to_string());
    }

    let content =
        fs::read_to_string(manifest_path).map_err(|e| format!("cannot read neyuki.toml: {}", e))?;
    let manifest = PackageManifest::parse(&content)?;

    println!(
        "Package '{}' v{} is valid.",
        manifest.name, manifest.version
    );
    println!("Dependencies ({}):", manifest.dependencies.len());
    for (k, v) in &manifest.dependencies {
        println!("  - {} ({})", k, v);
    }
    Ok(())
}

fn install_packages() -> Result<(), String> {
    let manifest_path = Path::new("neyuki.toml");
    if !manifest_path.exists() {
        return Err("neyuki.toml not found. Nothing to install.".to_string());
    }

    let content =
        fs::read_to_string(manifest_path).map_err(|e| format!("cannot read neyuki.toml: {}", e))?;
    let manifest = PackageManifest::parse(&content)?;
    let store = PackageStore::new(".");
    store.ensure_dir_exists()?;

    println!(
        "Resolving and installing dependencies for '{}'...",
        manifest.name
    );
    let installed = store.list_installed()?;
    println!(
        "Done. {} packages available in ./neyuki_packages/",
        installed.len()
    );
    Ok(())
}