//! 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>` - Ok if registration was successful, Err otherwise pub fn register_git_module(engine: &mut Engine) -> Result<(), Box> { // Register GitTree constructor engine.register_type::(); 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::(); 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(result: Result) -> Result> { 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> { 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> { let repos = git_error_to_rhai_error(git_tree.list())?; // Convert Vec 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, GitError>. pub fn git_tree_find(git_tree: &mut GitTree, pattern: &str) -> Result> { let repos: Vec = git_error_to_rhai_error(git_tree.find(pattern))?; // Convert Vec 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, 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> { let mut repos_vec: Vec = 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> { 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> { 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> { 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> { 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> { 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)` - If the clone operation failed pub fn git_clone(url: &str) -> Result> { // 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, )) }) }