sal-git 0.1.0

SAL Git - Git repository management and operations
Documentation
//! Rhai wrappers for Git module functions
//!
//! This module provides Rhai wrappers for the functions in the Git module.

use crate::{GitError, GitRepo, GitTree};
use rhai::{Array, Dynamic, Engine, EvalAltResult};

/// Register Git module functions with the Rhai engine
///
/// # Arguments
///
/// * `engine` - The Rhai engine to register the functions with
///
/// # Returns
///
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
pub fn register_git_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
    // Register GitTree constructor
    engine.register_type::<GitTree>();
    engine.register_fn("git_tree_new", git_tree_new);

    // Register GitTree methods
    engine.register_fn("list", git_tree_list);
    engine.register_fn("find", git_tree_find);
    engine.register_fn("get", git_tree_get);

    // Register GitRepo methods
    engine.register_type::<GitRepo>();
    engine.register_fn("path", git_repo_path);
    engine.register_fn("has_changes", git_repo_has_changes);
    engine.register_fn("pull", git_repo_pull);
    engine.register_fn("reset", git_repo_reset);
    engine.register_fn("commit", git_repo_commit);
    engine.register_fn("push", git_repo_push);

    // Register git_clone function for testing
    engine.register_fn("git_clone", git_clone);

    Ok(())
}

// Helper functions for error conversion
fn git_error_to_rhai_error<T>(result: Result<T, GitError>) -> Result<T, Box<EvalAltResult>> {
    result.map_err(|e| {
        Box::new(EvalAltResult::ErrorRuntime(
            format!("Git error: {}", e).into(),
            rhai::Position::NONE,
        ))
    })
}

//
// GitTree Function Wrappers
//

/// Wrapper for GitTree::new
///
/// Creates a new GitTree with the specified base path.
pub fn git_tree_new(base_path: &str) -> Result<GitTree, Box<EvalAltResult>> {
    git_error_to_rhai_error(GitTree::new(base_path))
}

/// Wrapper for GitTree::list
///
/// Lists all git repositories under the base path.
pub fn git_tree_list(git_tree: &mut GitTree) -> Result<Array, Box<EvalAltResult>> {
    let repos = git_error_to_rhai_error(git_tree.list())?;

    // Convert Vec<String> to Rhai Array
    let mut array = Array::new();
    for repo in repos {
        array.push(Dynamic::from(repo));
    }

    Ok(array)
}

/// Wrapper for GitTree::find
///
/// Finds repositories matching a pattern and returns them as an array of GitRepo objects.
/// Assumes the underlying GitTree::find Rust method now returns Result<Vec<GitRepo>, GitError>.
pub fn git_tree_find(git_tree: &mut GitTree, pattern: &str) -> Result<Array, Box<EvalAltResult>> {
    let repos: Vec<GitRepo> = git_error_to_rhai_error(git_tree.find(pattern))?;

    // Convert Vec<GitRepo> to Rhai Array
    let mut array = Array::new();
    for repo in repos {
        array.push(Dynamic::from(repo));
    }

    Ok(array)
}

/// Wrapper for GitTree::get
///
/// Gets a single GitRepo object based on an exact name or URL.
/// The underlying Rust GitTree::get method returns Result<Vec<GitRepo>, GitError>.
/// This wrapper ensures that for Rhai, 'get' returns a single GitRepo or an error
/// if zero or multiple repositories are found (for local names/patterns),
/// or if a URL operation fails or unexpectedly yields not exactly one result.
pub fn git_tree_get(
    git_tree: &mut GitTree,
    name_or_url: &str,
) -> Result<GitRepo, Box<EvalAltResult>> {
    let mut repos_vec: Vec<GitRepo> = git_error_to_rhai_error(git_tree.get(name_or_url))?;

    match repos_vec.len() {
        1 => Ok(repos_vec.remove(0)), // Efficient for Vec of size 1, transfers ownership
        0 => Err(Box::new(EvalAltResult::ErrorRuntime(
            format!("Git error: Repository '{}' not found.", name_or_url).into(),
            rhai::Position::NONE,
        ))),
        _ => Err(Box::new(EvalAltResult::ErrorRuntime(
            format!(
                "Git error: Multiple repositories ({}) found matching '{}'. Use find() for patterns or provide a more specific name for get().",
                repos_vec.len(),
                name_or_url
            )
            .into(),
            rhai::Position::NONE,
        ))),
    }
}

//
// GitRepo Function Wrappers
//

/// Wrapper for GitRepo::path
///
/// Gets the path of the repository.
pub fn git_repo_path(git_repo: &mut GitRepo) -> String {
    git_repo.path().to_string()
}

/// Wrapper for GitRepo::has_changes
///
/// Checks if the repository has uncommitted changes.
pub fn git_repo_has_changes(git_repo: &mut GitRepo) -> Result<bool, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.has_changes())
}

/// Wrapper for GitRepo::pull
///
/// Pulls the latest changes from the remote repository.
pub fn git_repo_pull(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.pull())
}

/// Wrapper for GitRepo::reset
///
/// Resets any local changes in the repository.
pub fn git_repo_reset(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.reset())
}

/// Wrapper for GitRepo::commit
///
/// Commits changes in the repository.
pub fn git_repo_commit(
    git_repo: &mut GitRepo,
    message: &str,
) -> Result<GitRepo, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.commit(message))
}

/// Wrapper for GitRepo::push
///
/// Pushes changes to the remote repository.
pub fn git_repo_push(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResult>> {
    git_error_to_rhai_error(git_repo.push())
}

/// Clone a git repository to a temporary location
///
/// This function clones a repository from the given URL to a temporary directory
/// and returns the GitRepo object for further operations.
///
/// # Arguments
///
/// * `url` - The URL of the git repository to clone
///
/// # Returns
///
/// * `Ok(GitRepo)` - The cloned repository object
/// * `Err(Box<EvalAltResult>)` - If the clone operation failed
pub fn git_clone(url: &str) -> Result<GitRepo, Box<EvalAltResult>> {
    // Get base path from environment or use default temp directory
    let base_path = std::env::var("GIT_DEFAULT_BASE_PATH").unwrap_or_else(|_| {
        std::env::temp_dir()
            .join("sal_git_clones")
            .to_string_lossy()
            .to_string()
    });

    // Create GitTree and clone the repository
    let git_tree = git_error_to_rhai_error(GitTree::new(&base_path))?;
    let repos = git_error_to_rhai_error(git_tree.get(url))?;

    // Return the first (and should be only) repository
    repos.into_iter().next().ok_or_else(|| {
        Box::new(EvalAltResult::ErrorRuntime(
            "Git error: No repository was cloned".into(),
            rhai::Position::NONE,
        ))
    })
}