Skip to main content

sal_git/
rhai.rs

1//! Rhai wrappers for Git module functions
2//!
3//! This module provides Rhai wrappers for the functions in the Git module.
4
5use crate::{GitError, GitRepo, GitTree};
6use rhai::{Array, Dynamic, Engine, EvalAltResult};
7
8/// Register Git module functions with the Rhai engine
9///
10/// # Arguments
11///
12/// * `engine` - The Rhai engine to register the functions with
13///
14/// # Returns
15///
16/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
17pub fn register_git_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
18    // Register GitTree constructor
19    engine.register_type::<GitTree>();
20    engine.register_fn("git_tree_new", git_tree_new);
21
22    // Register GitTree methods
23    engine.register_fn("list", git_tree_list);
24    engine.register_fn("find", git_tree_find);
25    engine.register_fn("get", git_tree_get);
26
27    // Register GitRepo methods
28    engine.register_type::<GitRepo>();
29    engine.register_fn("path", git_repo_path);
30    engine.register_fn("has_changes", git_repo_has_changes);
31    engine.register_fn("pull", git_repo_pull);
32    engine.register_fn("reset", git_repo_reset);
33    engine.register_fn("commit", git_repo_commit);
34    engine.register_fn("push", git_repo_push);
35
36    // Register git_clone function for testing
37    engine.register_fn("git_clone", git_clone);
38
39    Ok(())
40}
41
42// Helper functions for error conversion
43fn git_error_to_rhai_error<T>(result: Result<T, GitError>) -> Result<T, Box<EvalAltResult>> {
44    result.map_err(|e| {
45        Box::new(EvalAltResult::ErrorRuntime(
46            format!("Git error: {}", e).into(),
47            rhai::Position::NONE,
48        ))
49    })
50}
51
52//
53// GitTree Function Wrappers
54//
55
56/// Wrapper for GitTree::new
57///
58/// Creates a new GitTree with the specified base path.
59pub fn git_tree_new(base_path: &str) -> Result<GitTree, Box<EvalAltResult>> {
60    git_error_to_rhai_error(GitTree::new(base_path))
61}
62
63/// Wrapper for GitTree::list
64///
65/// Lists all git repositories under the base path.
66pub fn git_tree_list(git_tree: &mut GitTree) -> Result<Array, Box<EvalAltResult>> {
67    let repos = git_error_to_rhai_error(git_tree.list())?;
68
69    // Convert Vec<String> to Rhai Array
70    let mut array = Array::new();
71    for repo in repos {
72        array.push(Dynamic::from(repo));
73    }
74
75    Ok(array)
76}
77
78/// Wrapper for GitTree::find
79///
80/// Finds repositories matching a pattern and returns them as an array of GitRepo objects.
81/// Assumes the underlying GitTree::find Rust method now returns Result<Vec<GitRepo>, GitError>.
82pub fn git_tree_find(git_tree: &mut GitTree, pattern: &str) -> Result<Array, Box<EvalAltResult>> {
83    let repos: Vec<GitRepo> = git_error_to_rhai_error(git_tree.find(pattern))?;
84
85    // Convert Vec<GitRepo> to Rhai Array
86    let mut array = Array::new();
87    for repo in repos {
88        array.push(Dynamic::from(repo));
89    }
90
91    Ok(array)
92}
93
94/// Wrapper for GitTree::get
95///
96/// Gets a single GitRepo object based on an exact name or URL.
97/// The underlying Rust GitTree::get method returns Result<Vec<GitRepo>, GitError>.
98/// This wrapper ensures that for Rhai, 'get' returns a single GitRepo or an error
99/// if zero or multiple repositories are found (for local names/patterns),
100/// or if a URL operation fails or unexpectedly yields not exactly one result.
101pub fn git_tree_get(
102    git_tree: &mut GitTree,
103    name_or_url: &str,
104) -> Result<GitRepo, Box<EvalAltResult>> {
105    let mut repos_vec: Vec<GitRepo> = git_error_to_rhai_error(git_tree.get(name_or_url))?;
106
107    match repos_vec.len() {
108        1 => Ok(repos_vec.remove(0)), // Efficient for Vec of size 1, transfers ownership
109        0 => Err(Box::new(EvalAltResult::ErrorRuntime(
110            format!("Git error: Repository '{}' not found.", name_or_url).into(),
111            rhai::Position::NONE,
112        ))),
113        _ => Err(Box::new(EvalAltResult::ErrorRuntime(
114            format!(
115                "Git error: Multiple repositories ({}) found matching '{}'. Use find() for patterns or provide a more specific name for get().",
116                repos_vec.len(),
117                name_or_url
118            )
119            .into(),
120            rhai::Position::NONE,
121        ))),
122    }
123}
124
125//
126// GitRepo Function Wrappers
127//
128
129/// Wrapper for GitRepo::path
130///
131/// Gets the path of the repository.
132pub fn git_repo_path(git_repo: &mut GitRepo) -> String {
133    git_repo.path().to_string()
134}
135
136/// Wrapper for GitRepo::has_changes
137///
138/// Checks if the repository has uncommitted changes.
139pub fn git_repo_has_changes(git_repo: &mut GitRepo) -> Result<bool, Box<EvalAltResult>> {
140    git_error_to_rhai_error(git_repo.has_changes())
141}
142
143/// Wrapper for GitRepo::pull
144///
145/// Pulls the latest changes from the remote repository.
146pub fn git_repo_pull(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResult>> {
147    git_error_to_rhai_error(git_repo.pull())
148}
149
150/// Wrapper for GitRepo::reset
151///
152/// Resets any local changes in the repository.
153pub fn git_repo_reset(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResult>> {
154    git_error_to_rhai_error(git_repo.reset())
155}
156
157/// Wrapper for GitRepo::commit
158///
159/// Commits changes in the repository.
160pub fn git_repo_commit(
161    git_repo: &mut GitRepo,
162    message: &str,
163) -> Result<GitRepo, Box<EvalAltResult>> {
164    git_error_to_rhai_error(git_repo.commit(message))
165}
166
167/// Wrapper for GitRepo::push
168///
169/// Pushes changes to the remote repository.
170pub fn git_repo_push(git_repo: &mut GitRepo) -> Result<GitRepo, Box<EvalAltResult>> {
171    git_error_to_rhai_error(git_repo.push())
172}
173
174/// Clone a git repository to a temporary location
175///
176/// This function clones a repository from the given URL to a temporary directory
177/// and returns the GitRepo object for further operations.
178///
179/// # Arguments
180///
181/// * `url` - The URL of the git repository to clone
182///
183/// # Returns
184///
185/// * `Ok(GitRepo)` - The cloned repository object
186/// * `Err(Box<EvalAltResult>)` - If the clone operation failed
187pub fn git_clone(url: &str) -> Result<GitRepo, Box<EvalAltResult>> {
188    // Get base path from environment or use default temp directory
189    let base_path = std::env::var("GIT_DEFAULT_BASE_PATH").unwrap_or_else(|_| {
190        std::env::temp_dir()
191            .join("sal_git_clones")
192            .to_string_lossy()
193            .to_string()
194    });
195
196    // Create GitTree and clone the repository
197    let git_tree = git_error_to_rhai_error(GitTree::new(&base_path))?;
198    let repos = git_error_to_rhai_error(git_tree.get(url))?;
199
200    // Return the first (and should be only) repository
201    repos.into_iter().next().ok_or_else(|| {
202        Box::new(EvalAltResult::ErrorRuntime(
203            "Git error: No repository was cloned".into(),
204            rhai::Position::NONE,
205        ))
206    })
207}