navi-core 0.2.7

Local agentic engine and terminal-first coding agent.
Documentation
use anyhow::Result;
use serde_json::json;
use std::path::Path;

use super::finders;
use super::{PackageCheckOutput, PackageEntry, helpers};
use crate::tool::ToolResult;

pub(super) async fn cmd_check(
    project_root: &Path,
    invocation_id: &str,
    manager: &str,
    packages: &[String],
) -> Result<ToolResult> {
    match manager {
        "dart" => check_dart(project_root, invocation_id, packages).await,
        "npm" | "bun" => check_npm(project_root, invocation_id, manager, packages).await,
        "cargo" => check_cargo(project_root, invocation_id, packages).await,
        "go" => check_go(project_root, invocation_id, packages).await,
        _ => Ok(ToolResult {
            invocation_id: invocation_id.to_string(),
            ok: false,
            output: helpers::tool_error(
                "unsupported_package_manager",
                format!("check not supported for {manager}"),
                true,
                None,
                None,
            ),
        }),
    }
}

pub(super) async fn check_dart(
    project_root: &Path,
    invocation_id: &str,
    packages: &[String],
) -> Result<ToolResult> {
    let manifest = std::fs::read_to_string(project_root.join("pubspec.yaml")).unwrap_or_default();
    let mut installed: Vec<PackageEntry> = Vec::new();
    let mut not_found: Vec<String> = Vec::new();

    for pkg in packages {
        if finders::find_dart_package(&manifest, pkg) {
            installed.push(PackageEntry {
                name: pkg.clone(),
                status: "found",
                section: Some("dependencies".to_string()),
            });
        } else {
            not_found.push(pkg.clone());
        }
    }

    if packages.is_empty() {
        let has_lock = project_root.join("pubspec.lock").exists();
        return Ok(helpers::ok(
            invocation_id.to_string(),
            json!({
                "schema_version": helpers::SPECIALIZED_SCHEMA_VERSION,
                "status": if has_lock { "installed" } else { "not_installed" },
                "manager": "dart",
            }),
        ));
    }

    Ok(helpers::ok(
        invocation_id.to_string(),
        helpers::versioned(PackageCheckOutput {
            status: if not_found.is_empty() {
                "success"
            } else {
                "not_found"
            },
            manager: "dart".to_string(),
            installed,
            not_found,
        }),
    ))
}

pub(super) async fn check_npm(
    project_root: &Path,
    invocation_id: &str,
    manager: &str,
    packages: &[String],
) -> Result<ToolResult> {
    let manifest = std::fs::read_to_string(project_root.join("package.json")).unwrap_or_default();
    let mut installed: Vec<PackageEntry> = Vec::new();
    let mut not_found: Vec<String> = Vec::new();

    for pkg in packages {
        if let Some(section) = finders::find_npm_package(&manifest, pkg) {
            installed.push(PackageEntry {
                name: pkg.clone(),
                status: "found",
                section: Some(section),
            });
        } else {
            not_found.push(pkg.clone());
        }
    }

    if packages.is_empty() {
        let has_modules = project_root.join("node_modules").exists();
        return Ok(helpers::ok(
            invocation_id.to_string(),
            json!({
                "schema_version": helpers::SPECIALIZED_SCHEMA_VERSION,
                "status": if has_modules { "installed" } else { "not_installed" },
                "manager": manager,
            }),
        ));
    }

    Ok(helpers::ok(
        invocation_id.to_string(),
        helpers::versioned(PackageCheckOutput {
            status: if not_found.is_empty() {
                "success"
            } else {
                "not_found"
            },
            manager: manager.to_string(),
            installed,
            not_found,
        }),
    ))
}

pub(super) async fn check_cargo(
    project_root: &Path,
    invocation_id: &str,
    packages: &[String],
) -> Result<ToolResult> {
    let manifest = std::fs::read_to_string(project_root.join("Cargo.toml")).unwrap_or_default();
    let mut installed: Vec<PackageEntry> = Vec::new();
    let mut not_found: Vec<String> = Vec::new();

    for pkg in packages {
        if let Some(section) = finders::find_cargo_package(&manifest, pkg) {
            installed.push(PackageEntry {
                name: pkg.clone(),
                status: "found",
                section: Some(section),
            });
        } else {
            not_found.push(pkg.clone());
        }
    }

    if packages.is_empty() {
        let has_lock = project_root.join("Cargo.lock").exists();
        return Ok(helpers::ok(
            invocation_id.to_string(),
            json!({
                "schema_version": helpers::SPECIALIZED_SCHEMA_VERSION,
                "status": if has_lock { "installed" } else { "not_installed" },
                "manager": "cargo",
            }),
        ));
    }

    Ok(helpers::ok(
        invocation_id.to_string(),
        helpers::versioned(PackageCheckOutput {
            status: if not_found.is_empty() {
                "success"
            } else {
                "not_found"
            },
            manager: "cargo".to_string(),
            installed,
            not_found,
        }),
    ))
}

pub(super) async fn check_go(
    project_root: &Path,
    invocation_id: &str,
    packages: &[String],
) -> Result<ToolResult> {
    let manifest = std::fs::read_to_string(project_root.join("go.mod")).unwrap_or_default();
    let mut installed: Vec<PackageEntry> = Vec::new();
    let mut not_found: Vec<String> = Vec::new();

    for pkg in packages {
        if finders::find_go_package(&manifest, pkg) {
            installed.push(PackageEntry {
                name: pkg.clone(),
                status: "found",
                section: Some("require".to_string()),
            });
        } else {
            not_found.push(pkg.clone());
        }
    }

    if packages.is_empty() {
        let has_sum = project_root.join("go.sum").exists();
        return Ok(helpers::ok(
            invocation_id.to_string(),
            json!({
                "schema_version": helpers::SPECIALIZED_SCHEMA_VERSION,
                "status": if has_sum { "installed" } else { "not_installed" },
                "manager": "go",
            }),
        ));
    }

    Ok(helpers::ok(
        invocation_id.to_string(),
        helpers::versioned(PackageCheckOutput {
            status: if not_found.is_empty() {
                "success"
            } else {
                "not_found"
            },
            manager: "go".to_string(),
            installed,
            not_found,
        }),
    ))
}