mecha10-cli 0.1.47

Mecha10 CLI tool
Documentation
//! Utility functions for package service

use anyhow::Result;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::process::Command;

/// Check if file is executable
pub fn is_executable(path: &Path) -> Result<bool> {
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let metadata = path.metadata()?;
        let permissions = metadata.permissions();
        Ok(permissions.mode() & 0o111 != 0)
    }

    #[cfg(not(unix))]
    {
        if let Some(name) = path.file_name() {
            let name_str = name.to_string_lossy();
            Ok(!name_str.contains('.') || name_str.ends_with(".exe"))
        } else {
            Ok(false)
        }
    }
}

/// Calculate file checksum using BLAKE3
pub fn calculate_checksum(path: &Path) -> Result<String> {
    use std::io::Read;

    let mut file = fs::File::open(path)?;
    let mut hasher = blake3::Hasher::new();
    let mut buffer = vec![0; 8192];

    loop {
        let n = file.read(&mut buffer)?;
        if n == 0 {
            break;
        }
        hasher.update(&buffer[..n]);
    }

    Ok(hasher.finalize().to_hex().to_string())
}

/// Determine config file type
pub fn determine_config_type(path: &Path) -> String {
    let path_str = path.to_string_lossy().to_lowercase();

    if path_str.contains("infrastructure") {
        "infrastructure".to_string()
    } else if path_str.contains("node") {
        "node".to_string()
    } else if path_str.contains("behavior") {
        "behavior".to_string()
    } else {
        "unknown".to_string()
    }
}

/// Determine asset type
pub fn determine_asset_type(path: &Path) -> String {
    let path_str = path.to_string_lossy().to_lowercase();

    if path_str.contains("model") || path_str.ends_with(".onnx") || path_str.ends_with(".pt") {
        "model".to_string()
    } else if path_str.contains("calibration") || path_str.ends_with(".yaml") {
        "calibration".to_string()
    } else {
        "data".to_string()
    }
}

/// Collect project dependencies from Cargo.lock
pub fn collect_dependencies(project_root: &Path) -> Result<HashMap<String, String>> {
    let mut dependencies = HashMap::new();

    let cargo_lock_path = project_root.join("Cargo.lock");
    if cargo_lock_path.exists() {
        let lock_content = fs::read_to_string(&cargo_lock_path)?;

        for line in lock_content.lines() {
            if line.starts_with("name = ") {
                if let Some(name) = line.split('"').nth(1) {
                    dependencies.insert(name.to_string(), "unknown".to_string());
                }
            }
        }
    }

    Ok(dependencies)
}

/// Get git commit hash
pub fn get_git_commit(project_root: &Path) -> Option<String> {
    Command::new("git")
        .args(["rev-parse", "HEAD"])
        .current_dir(project_root)
        .output()
        .ok()
        .and_then(|output| {
            if output.status.success() {
                String::from_utf8(output.stdout).ok().map(|s| s.trim().to_string())
            } else {
                None
            }
        })
}

/// Recursively copy directory
pub fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<()> {
    if !dst.exists() {
        fs::create_dir_all(dst)?;
    }

    for entry in fs::read_dir(src)? {
        let entry = entry?;
        let path = entry.path();
        let dest_path = dst.join(entry.file_name());

        if path.is_dir() {
            copy_dir_recursive(&path, &dest_path)?;
        } else {
            fs::copy(&path, &dest_path)?;
        }
    }

    Ok(())
}