This commit is contained in:
2025-05-08 09:54:20 +03:00
parent 104097ee4b
commit 2cd9faf4fa
52 changed files with 1115 additions and 1757 deletions

View File

@@ -1,212 +0,0 @@
# Git Interface Redesign Plan
## Current Understanding
The current git interface consists of standalone functions like `git_clone`, `git_list`, `git_update`, etc. We want to replace this with an object-oriented interface using a builder pattern that allows for method chaining.
## New Interface Design
### Core Components
```mermaid
classDiagram
class GitTree {
+String base_path
+new(base_path: &str) Result<GitTree, GitError>
+list() Result<Vec<String>, GitError>
+find(pattern: &str) Result<Vec<String>, GitError>
+get(path_pattern: &str) Result<Vec<GitRepo>, GitError>
}
class GitRepo {
+String path
+pull() Result<GitRepo, GitError>
+reset() Result<GitRepo, GitError>
+push() Result<GitRepo, GitError>
+commit(message: &str) Result<GitRepo, GitError>
+has_changes() Result<bool, GitError>
}
GitTree --> GitRepo : creates
```
### Implementation Details
1. **GitTree Class**:
- Constructor takes a base path parameter that specifies where all git repositories will be located
- Methods for listing and finding repositories
- A `get()` method that returns one or more GitRepo objects based on a path pattern
- The `get()` method can also accept a URL (git or http format) and will clone the repository if it doesn't exist
2. **GitRepo Class**:
- Represents a single git repository
- Methods for common git operations: pull, reset, push, commit
- Each method returns a Result containing either the GitRepo object (for chaining) or an error
- If an operation fails, subsequent operations in the chain are skipped
3. **Error Handling**:
- Each method returns a Result type for immediate error handling
- Errors are propagated up the call chain
- The existing GitError enum will be reused
## Implementation Plan
### 1. Create the GitTree and GitRepo Structs in git.rs
```rust
pub struct GitTree {
base_path: String,
}
pub struct GitRepo {
path: String,
}
```
### 2. Implement the GitTree Methods
```rust
impl GitTree {
pub fn new(base_path: &str) -> Result<Self, GitError> {
// Validate the base path
// Create the directory if it doesn't exist
Ok(GitTree {
base_path: base_path.to_string(),
})
}
pub fn list(&self) -> Result<Vec<String>, GitError> {
// List all git repositories under the base path
}
pub fn find(&self, pattern: &str) -> Result<Vec<String>, GitError> {
// Find repositories matching the pattern
}
pub fn get(&self, path_pattern: &str) -> Result<Vec<GitRepo>, GitError> {
// Find repositories matching the pattern
// Return GitRepo objects for each match
}
}
```
### 3. Implement the GitRepo Methods
```rust
impl GitRepo {
pub fn pull(&self) -> Result<Self, GitError> {
// Pull the latest changes
// Return self for chaining or an error
}
pub fn reset(&self) -> Result<Self, GitError> {
// Reset any local changes
// Return self for chaining or an error
}
pub fn push(&self) -> Result<Self, GitError> {
// Push changes to the remote
// Return self for chaining or an error
}
pub fn commit(&self, message: &str) -> Result<Self, GitError> {
// Commit changes with the given message
// Return self for chaining or an error
}
pub fn has_changes(&self) -> Result<bool, GitError> {
// Check if the repository has uncommitted changes
}
}
```
### 4. Update the Rhai Wrappers in rhai/git.rs
```rust
// Register the GitTree and GitRepo types with Rhai
pub fn register_git_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register the GitTree type
engine.register_type::<GitTree>();
engine.register_fn("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("pull", git_repo_pull);
engine.register_fn("reset", git_repo_reset);
engine.register_fn("push", git_repo_push);
engine.register_fn("commit", git_repo_commit);
engine.register_fn("has_changes", git_repo_has_changes);
Ok(())
}
```
### 5. Update Tests and Examples
- Update the test files to use the new interface
- Create new examples demonstrating the builder pattern and method chaining
## Usage Examples
### Example 1: Basic Repository Operations
```rhai
// Create a new GitTree object
let git_tree = new("/home/user/code");
// List all repositories
let repos = git_tree.list();
print(`Found ${repos.len()} repositories`);
// Find repositories matching a pattern
let matching = git_tree.find("my-project*");
print(`Found ${matching.len()} matching repositories`);
// Get a repository and perform operations
let repo = git_tree.get("my-project")[0];
let result = repo.pull().reset().commit("Update files").push();
```
### Example 2: Working with Multiple Repositories
```rhai
// Create a new GitTree object
let git_tree = new("/home/user/code");
// Get all repositories matching a pattern
let repos = git_tree.get("project*");
print(`Found ${repos.len()} matching repositories`);
// Perform operations on all repositories
for repo in repos {
let result = repo.pull();
if result.is_ok() {
print(`Successfully pulled ${repo.path}`);
} else {
print(`Failed to pull ${repo.path}: ${result.error}`);
}
}
```
### Example 3: Cloning a Repository
```rhai
// Create a new GitTree object
let git_tree = new("/home/user/code");
// Clone a repository by URL
let repos = git_tree.get("https://github.com/username/repo.git");
let repo = repos[0];
print(`Repository cloned to: ${repo.path}`);
```
## Migration Strategy
1. Implement the new interface in git.rs and rhai/git.rs
2. Update all tests and examples to use the new interface
3. Remove the old standalone functions

View File

@@ -1,565 +0,0 @@
# Package Management Module Implementation Plan
## Overview
The package management module will:
1. Provide a factory called `PackHero` that detects the current platform
2. Implement platform-specific package managers for Ubuntu (apt) and macOS (brew)
3. Support operations: install, remove, update, upgrade, list installed packages, search for packages, and check if a package is installed
4. Include debug functionality similar to buildah
5. Ensure all operations are non-interactive and have proper error propagation
6. Be wrapped in Rhai for scripting access
## Architecture
```mermaid
classDiagram
class PackageError {
+CommandFailed(String)
+CommandExecutionFailed(std::io::Error)
+UnsupportedPlatform(String)
+Other(String)
}
class PackHero {
-platform: Platform
-debug: bool
+new() PackHero
+detect_platform() Platform
+set_debug(bool) PackHero
+debug() bool
+install(package: &str) Result
+remove(package: &str) Result
+update() Result
+upgrade() Result
+list_installed() Result
+search(query: &str) Result
+is_installed(package: &str) Result
}
class Platform {
<<enumeration>>
Ubuntu
MacOS
Unknown
}
class PackageManager {
<<interface>>
+install(package: &str) Result
+remove(package: &str) Result
+update() Result
+upgrade() Result
+list_installed() Result
+search(query: &str) Result
+is_installed(package: &str) Result
}
class AptPackageManager {
-debug: bool
+new(debug: bool) AptPackageManager
+install(package: &str) Result
+remove(package: &str) Result
+update() Result
+upgrade() Result
+list_installed() Result
+search(query: &str) Result
+is_installed(package: &str) Result
}
class BrewPackageManager {
-debug: bool
+new(debug: bool) BrewPackageManager
+install(package: &str) Result
+remove(package: &str) Result
+update() Result
+upgrade() Result
+list_installed() Result
+search(query: &str) Result
+is_installed(package: &str) Result
}
PackHero --> Platform : uses
PackHero --> PackageManager : uses
PackageManager <|.. AptPackageManager : implements
PackageManager <|.. BrewPackageManager : implements
```
## Implementation Details
### 1. Package Error Type
Create a custom error type for package management operations:
```rust
pub enum PackageError {
CommandFailed(String),
CommandExecutionFailed(std::io::Error),
UnsupportedPlatform(String),
Other(String),
}
```
### 2. Platform Detection
Implement platform detection to determine which package manager to use:
```rust
pub enum Platform {
Ubuntu,
MacOS,
Unknown,
}
impl Platform {
pub fn detect() -> Self {
// Check for macOS
if std::path::Path::new("/usr/bin/sw_vers").exists() {
return Platform::MacOS;
}
// Check for Ubuntu
if std::path::Path::new("/etc/lsb-release").exists() {
// Read the file to confirm it's Ubuntu
if let Ok(content) = std::fs::read_to_string("/etc/lsb-release") {
if content.contains("Ubuntu") {
return Platform::Ubuntu;
}
}
}
Platform::Unknown
}
}
```
### 3. Package Manager Trait
Define a trait for package managers to implement:
```rust
pub trait PackageManager {
fn install(&self, package: &str) -> Result<CommandResult, PackageError>;
fn remove(&self, package: &str) -> Result<CommandResult, PackageError>;
fn update(&self) -> Result<CommandResult, PackageError>;
fn upgrade(&self) -> Result<CommandResult, PackageError>;
fn list_installed(&self) -> Result<Vec<String>, PackageError>;
fn search(&self, query: &str) -> Result<Vec<String>, PackageError>;
fn is_installed(&self, package: &str) -> Result<bool, PackageError>;
}
```
### 4. Platform-Specific Implementations
#### Ubuntu (apt) Implementation
```rust
pub struct AptPackageManager {
debug: bool,
}
impl AptPackageManager {
pub fn new(debug: bool) -> Self {
Self { debug }
}
}
impl PackageManager for AptPackageManager {
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
execute_package_command(&["apt-get", "install", "-y", package], self.debug)
}
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
execute_package_command(&["apt-get", "remove", "-y", package], self.debug)
}
fn update(&self) -> Result<CommandResult, PackageError> {
execute_package_command(&["apt-get", "update", "-y"], self.debug)
}
fn upgrade(&self) -> Result<CommandResult, PackageError> {
execute_package_command(&["apt-get", "upgrade", "-y"], self.debug)
}
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
let result = execute_package_command(&["dpkg", "--get-selections"], self.debug)?;
let packages = result.stdout
.lines()
.filter_map(|line| {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 && parts[1] == "install" {
Some(parts[0].to_string())
} else {
None
}
})
.collect();
Ok(packages)
}
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
let result = execute_package_command(&["apt-cache", "search", query], self.debug)?;
let packages = result.stdout
.lines()
.map(|line| {
let parts: Vec<&str> = line.split_whitespace().collect();
if !parts.is_empty() {
parts[0].to_string()
} else {
String::new()
}
})
.filter(|s| !s.is_empty())
.collect();
Ok(packages)
}
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
let result = execute_package_command(&["dpkg", "-s", package], self.debug);
match result {
Ok(cmd_result) => Ok(cmd_result.success),
Err(_) => Ok(false),
}
}
}
```
#### macOS (brew) Implementation
```rust
pub struct BrewPackageManager {
debug: bool,
}
impl BrewPackageManager {
pub fn new(debug: bool) -> Self {
Self { debug }
}
}
impl PackageManager for BrewPackageManager {
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
execute_package_command(&["brew", "install", package], self.debug)
}
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
execute_package_command(&["brew", "uninstall", package], self.debug)
}
fn update(&self) -> Result<CommandResult, PackageError> {
execute_package_command(&["brew", "update"], self.debug)
}
fn upgrade(&self) -> Result<CommandResult, PackageError> {
execute_package_command(&["brew", "upgrade"], self.debug)
}
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
let result = execute_package_command(&["brew", "list", "--formula"], self.debug)?;
let packages = result.stdout
.lines()
.map(|line| line.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
Ok(packages)
}
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
let result = execute_package_command(&["brew", "search", query], self.debug)?;
let packages = result.stdout
.lines()
.map(|line| line.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
Ok(packages)
}
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
let result = execute_package_command(&["brew", "list", package], self.debug);
match result {
Ok(cmd_result) => Ok(cmd_result.success),
Err(_) => Ok(false),
}
}
}
```
### 5. Command Execution with Debug Support
Implement a function to execute package management commands with debug support:
```rust
// Thread-local storage for debug flag
thread_local! {
static DEBUG: std::cell::RefCell<bool> = std::cell::RefCell::new(false);
}
/// Set the debug flag for the current thread
pub fn set_thread_local_debug(debug: bool) {
DEBUG.with(|cell| {
*cell.borrow_mut() = debug;
});
}
/// Get the debug flag for the current thread
pub fn thread_local_debug() -> bool {
DEBUG.with(|cell| {
*cell.borrow()
})
}
/// Execute a package management command and return the result
pub fn execute_package_command(args: &[&str], debug: bool) -> Result<CommandResult, PackageError> {
// Save the current debug flag
let previous_debug = thread_local_debug();
// Set the thread-local debug flag
set_thread_local_debug(debug);
if debug {
println!("Executing command: {}", args.join(" "));
}
let output = Command::new(args[0])
.args(&args[1..])
.output();
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
match output {
Ok(output) => {
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
let result = CommandResult {
stdout,
stderr,
success: output.status.success(),
code: output.status.code().unwrap_or(-1),
};
// Always output stdout/stderr when debug is true
if debug {
if !result.stdout.is_empty() {
println!("Command stdout: {}", result.stdout);
}
if !result.stderr.is_empty() {
println!("Command stderr: {}", result.stderr);
}
if result.success {
println!("Command succeeded with code {}", result.code);
} else {
println!("Command failed with code {}", result.code);
}
}
if result.success {
Ok(result)
} else {
// If command failed and debug is false, output stderr
if !debug {
println!("Command failed with code {}: {}", result.code, result.stderr.trim());
}
Err(PackageError::CommandFailed(format!("Command failed with code {}: {}",
result.code, result.stderr.trim())))
}
},
Err(e) => {
// Always output error information
println!("Command execution failed: {}", e);
Err(PackageError::CommandExecutionFailed(e))
}
}
}
```
### 6. PackHero Factory
Implement the PackHero factory to provide a unified interface:
```rust
pub struct PackHero {
platform: Platform,
debug: bool,
}
impl PackHero {
pub fn new() -> Self {
let platform = Platform::detect();
Self {
platform,
debug: false,
}
}
pub fn set_debug(&mut self, debug: bool) -> &mut Self {
self.debug = debug;
self
}
pub fn debug(&self) -> bool {
self.debug
}
fn get_package_manager(&self) -> Result<Box<dyn PackageManager>, PackageError> {
match self.platform {
Platform::Ubuntu => Ok(Box::new(AptPackageManager::new(self.debug))),
Platform::MacOS => Ok(Box::new(BrewPackageManager::new(self.debug))),
Platform::Unknown => Err(PackageError::UnsupportedPlatform("Unsupported platform".to_string())),
}
}
pub fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
let pm = self.get_package_manager()?;
pm.install(package)
}
pub fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
let pm = self.get_package_manager()?;
pm.remove(package)
}
pub fn update(&self) -> Result<CommandResult, PackageError> {
let pm = self.get_package_manager()?;
pm.update()
}
pub fn upgrade(&self) -> Result<CommandResult, PackageError> {
let pm = self.get_package_manager()?;
pm.upgrade()
}
pub fn list_installed(&self) -> Result<Vec<String>, PackageError> {
let pm = self.get_package_manager()?;
pm.list_installed()
}
pub fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
let pm = self.get_package_manager()?;
pm.search(query)
}
pub fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
let pm = self.get_package_manager()?;
pm.is_installed(package)
}
}
```
### 7. Rhai Integration
Update the Rhai OS module to include the package management functions:
```rust
// In rhai/os.rs
// Register package management functions
engine.register_fn("package_install", package_install);
engine.register_fn("package_remove", package_remove);
engine.register_fn("package_update", package_update);
engine.register_fn("package_upgrade", package_upgrade);
engine.register_fn("package_list", package_list);
engine.register_fn("package_search", package_search);
engine.register_fn("package_is_installed", package_is_installed);
engine.register_fn("package_set_debug", package_set_debug);
// Wrapper for os::package::install
pub fn package_install(package: &str) -> Result<String, Box<EvalAltResult>> {
let hero = os::package::PackHero::new();
hero.install(package)
.map(|_| "Package installed successfully".to_string())
.to_rhai_error()
}
// Wrapper for os::package::remove
pub fn package_remove(package: &str) -> Result<String, Box<EvalAltResult>> {
let hero = os::package::PackHero::new();
hero.remove(package)
.map(|_| "Package removed successfully".to_string())
.to_rhai_error()
}
// Wrapper for os::package::update
pub fn package_update() -> Result<String, Box<EvalAltResult>> {
let hero = os::package::PackHero::new();
hero.update()
.map(|_| "Package lists updated successfully".to_string())
.to_rhai_error()
}
// Wrapper for os::package::upgrade
pub fn package_upgrade() -> Result<String, Box<EvalAltResult>> {
let hero = os::package::PackHero::new();
hero.upgrade()
.map(|_| "Packages upgraded successfully".to_string())
.to_rhai_error()
}
// Wrapper for os::package::list_installed
pub fn package_list() -> Result<Array, Box<EvalAltResult>> {
let hero = os::package::PackHero::new();
let packages = hero.list_installed().to_rhai_error()?;
// Convert Vec<String> to Rhai Array
let mut array = Array::new();
for package in packages {
array.push(package.into());
}
Ok(array)
}
// Wrapper for os::package::search
pub fn package_search(query: &str) -> Result<Array, Box<EvalAltResult>> {
let hero = os::package::PackHero::new();
let packages = hero.search(query).to_rhai_error()?;
// Convert Vec<String> to Rhai Array
let mut array = Array::new();
for package in packages {
array.push(package.into());
}
Ok(array)
}
// Wrapper for os::package::is_installed
pub fn package_is_installed(package: &str) -> Result<bool, Box<EvalAltResult>> {
let hero = os::package::PackHero::new();
hero.is_installed(package).to_rhai_error()
}
// Global debug flag for package management
static mut PACKAGE_DEBUG: bool = false;
// Wrapper for setting package debug mode
pub fn package_set_debug(debug: bool) -> bool {
unsafe {
PACKAGE_DEBUG = debug;
PACKAGE_DEBUG
}
}
```
## Implementation Steps
1. Create the package.rs file in the os directory
2. Implement the PackageError enum
3. Implement the Platform enum with detection logic
4. Implement the PackageManager trait
5. Implement the AptPackageManager for Ubuntu
6. Implement the BrewPackageManager for macOS
7. Implement the debug functionality with thread-local storage
8. Implement the PackHero factory
9. Update os/mod.rs to include the package module
10. Update rhai/os.rs to include the package management functions
11. Test the implementation on both Ubuntu and macOS
## Testing Strategy
1. Create unit tests for each platform-specific implementation
2. Create integration tests that verify the PackHero factory correctly detects the platform and uses the appropriate package manager
3. Create Rhai examples that demonstrate the use of the package management functions

View File

@@ -34,7 +34,6 @@ pub use os::{
pub use process::{
register_process_module,
// Run functions
run, run_silent, run_with_options, new_run_options,
// Process management functions
which, kill, process_list, process_get
};

View File

@@ -2,8 +2,9 @@
//!
//! This module provides Rhai wrappers for the functions in the Process module.
use rhai::{Engine, EvalAltResult, Array, Dynamic, Map};
use rhai::{Engine, EvalAltResult, Array, Dynamic};
use crate::process::{self, CommandResult, ProcessInfo, RunError, ProcessError};
use std::clone::Clone;
/// Register Process module functions with the Rhai engine
///
@@ -16,43 +17,36 @@ use crate::process::{self, CommandResult, ProcessInfo, RunError, ProcessError};
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
pub fn register_process_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register types
register_process_types(engine)?;
// Register run functions
engine.register_fn("run", run);
engine.register_fn("run_silent", run_silent);
engine.register_fn("run_with_options", run_with_options);
engine.register_fn("new_run_options", new_run_options);
// Register process management functions
engine.register_fn("which", which);
engine.register_fn("kill", kill);
engine.register_fn("process_list", process_list);
engine.register_fn("process_get", process_get);
Ok(())
}
// register_process_types(engine)?; // Removed
/// Register Process module types with the Rhai engine
fn register_process_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register CommandResult type and methods
// Register CommandResult type and its methods
engine.register_type_with_name::<CommandResult>("CommandResult");
// Register getters for CommandResult properties
engine.register_get("stdout", |r: &mut CommandResult| r.stdout.clone());
engine.register_get("stderr", |r: &mut CommandResult| r.stderr.clone());
engine.register_get("success", |r: &mut CommandResult| r.success);
engine.register_get("code", |r: &mut CommandResult| r.code);
// Register ProcessInfo type and methods
// Register ProcessInfo type and its methods
engine.register_type_with_name::<ProcessInfo>("ProcessInfo");
// Register getters for ProcessInfo properties
engine.register_get("pid", |p: &mut ProcessInfo| p.pid);
engine.register_get("name", |p: &mut ProcessInfo| p.name.clone());
engine.register_get("memory", |p: &mut ProcessInfo| p.memory);
engine.register_get("cpu", |p: &mut ProcessInfo| p.cpu);
// Register CommandBuilder type and its methods
engine.register_type_with_name::<RhaiCommandBuilder>("CommandBuilder");
engine.register_fn("run", RhaiCommandBuilder::new_rhai); // This is the builder entry point
engine.register_fn("silent", RhaiCommandBuilder::silent); // Method on CommandBuilder
engine.register_fn("ignore_error", RhaiCommandBuilder::ignore_error); // Method on CommandBuilder
engine.register_fn("log", RhaiCommandBuilder::log); // Method on CommandBuilder
engine.register_fn("execute", RhaiCommandBuilder::execute_command); // Method on CommandBuilder
// Register other process management functions
engine.register_fn("which", which);
engine.register_fn("kill", kill);
engine.register_fn("process_list", process_list);
engine.register_fn("process_get", process_get);
Ok(())
}
@@ -66,6 +60,56 @@ fn run_error_to_rhai_error<T>(result: Result<T, RunError>) -> Result<T, Box<Eval
})
}
// Define a Rhai-facing builder struct
#[derive(Clone)]
struct RhaiCommandBuilder {
command: String,
die_on_error: bool,
is_silent: bool,
enable_log: bool,
}
impl RhaiCommandBuilder {
// Constructor function for Rhai (registered as `run`)
pub fn new_rhai(command: &str) -> Self {
Self {
command: command.to_string(),
die_on_error: true, // Default: die on error
is_silent: false,
enable_log: false,
}
}
// Rhai method: .silent()
pub fn silent(mut self) -> Self {
self.is_silent = true;
self
}
// Rhai method: .ignore_error()
pub fn ignore_error(mut self) -> Self {
self.die_on_error = false;
self
}
// Rhai method: .log()
pub fn log(mut self) -> Self {
self.enable_log = true;
self
}
// Rhai method: .execute() - Execute the command
pub fn execute_command(self) -> Result<CommandResult, Box<EvalAltResult>> {
let builder = process::run(&self.command)
.die(self.die_on_error)
.silent(self.is_silent)
.log(self.enable_log);
// Execute the command
run_error_to_rhai_error(builder.execute())
}
}
fn process_error_to_rhai_error<T>(result: Result<T, ProcessError>) -> Result<T, Box<EvalAltResult>> {
result.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
@@ -75,78 +119,6 @@ fn process_error_to_rhai_error<T>(result: Result<T, ProcessError>) -> Result<T,
})
}
/// Create a new Map with default run options
pub fn new_run_options() -> Map {
let mut map = Map::new();
map.insert("die".into(), Dynamic::from(true));
map.insert("silent".into(), Dynamic::from(false));
map.insert("async_exec".into(), Dynamic::from(false));
map.insert("log".into(), Dynamic::from(false));
map
}
//
// Run Function Wrappers
//
/// Wrapper for process::run_command
///
/// Run a command or multiline script with arguments.
pub fn run(command: &str) -> Result<CommandResult, Box<EvalAltResult>> {
run_error_to_rhai_error(process::run_command(command))
}
/// Wrapper for process::run_silent
///
/// Run a command or multiline script with arguments silently.
pub fn run_silent(command: &str) -> Result<CommandResult, Box<EvalAltResult>> {
run_error_to_rhai_error(process::run_silent(command))
}
/// Run a command with options specified in a Map
///
/// This provides a builder-style interface for Rhai scripts.
///
/// # Example
///
/// ```rhai
/// let options = new_run_options();
/// options.die = false;
/// options.silent = true;
/// let result = run("echo Hello", options);
/// ```
pub fn run_with_options(command: &str, options: Map) -> Result<CommandResult, Box<EvalAltResult>> {
let mut builder = process::run(command);
// Apply options from the map
if let Some(die) = options.get("die") {
if let Ok(die_val) = die.clone().as_bool() {
builder = builder.die(die_val);
}
}
if let Some(silent) = options.get("silent") {
if let Ok(silent_val) = silent.clone().as_bool() {
builder = builder.silent(silent_val);
}
}
if let Some(async_exec) = options.get("async_exec") {
if let Ok(async_val) = async_exec.clone().as_bool() {
builder = builder.async_exec(async_val);
}
}
if let Some(log) = options.get("log") {
if let Ok(log_val) = log.clone().as_bool() {
builder = builder.log(log_val);
}
}
// Execute the command
run_error_to_rhai_error(builder.execute())
}
//
// Process Management Function Wrappers
//

View File

@@ -1,133 +0,0 @@
# Implementation Plan: Rhai Wrappers for Text Tools
## 1. Overview
We'll create a new module `rhai/text.rs` that will provide Rhai wrappers for all functionality in the text module, including:
- TextReplacer (from replace.rs)
- TemplateBuilder (from template.rs)
- name_fix and path_fix functions (from fix.rs)
- dedent and prefix functions (from dedent.rs)
The implementation will follow the builder pattern for the TextReplacer and TemplateBuilder classes, similar to their Rust implementations, and will use the same error handling pattern as the existing Rhai modules.
## 2. Module Structure
```mermaid
graph TD
A[rhai/mod.rs] --> B[rhai/text.rs]
B --> C[TextReplacer Wrappers]
B --> D[TemplateBuilder Wrappers]
B --> E[Fix Function Wrappers]
B --> F[Dedent Function Wrappers]
B --> G[Error Handling]
B --> H[Registration Functions]
```
## 3. Implementation Details
### 3.1. Module Setup and Registration
1. Create a new file `rhai/text.rs`
2. Add the module to `rhai/mod.rs`
3. Implement a `register_text_module` function to register all text-related functions with the Rhai engine
4. Update the main `register` function in `rhai/mod.rs` to call `register_text_module`
### 3.2. TextReplacer Implementation
1. Register the TextReplacer type with the Rhai engine
2. Implement the following functions:
- `text_replacer_new()` - Creates a new TextReplacerBuilder
- `pattern(builder, pat)` - Sets the pattern to search for and automatically adds any previous pattern/replacement pair to the chain
- `replacement(builder, rep)` - Sets the replacement text
- `regex(builder, yes)` - Sets whether to use regex
- `case_insensitive(builder, yes)` - Sets whether the replacement should be case-insensitive
- `build(builder)` - Builds the TextReplacer with all configured replacement operations
- `replace(replacer, input)` - Applies all configured replacement operations to the input text
- `replace_file(replacer, path)` - Reads a file, applies all replacements, and returns the result as a string
- `replace_file_in_place(replacer, path)` - Reads a file, applies all replacements, and writes the result back to the file
- `replace_file_to(replacer, input_path, output_path)` - Reads a file, applies all replacements, and writes the result to a new file
### 3.3. TemplateBuilder Implementation
1. Register the TemplateBuilder type with the Rhai engine
2. Implement the following functions:
- `template_builder_open(template_path)` - Creates a new TemplateBuilder with the specified template path
- `add_var(builder, name, value)` - Adds a variable to the template context
- `add_vars(builder, vars_map)` - Adds multiple variables to the template context from a Map
- `render(builder)` - Renders the template with the current context
- `render_to_file(builder, output_path)` - Renders the template and writes the result to a file
### 3.4. Fix Functions Implementation
1. Implement wrappers for the following functions:
- `name_fix(text)` - Sanitizes a name by replacing special characters with underscores
- `path_fix(text)` - Applies name_fix to the filename part of a path
### 3.5. Dedent Functions Implementation
1. Implement wrappers for the following functions:
- `dedent(text)` - Removes common leading whitespace from a multiline string
- `prefix(text, prefix)` - Adds a prefix to each line of a multiline string
### 3.6. Error Handling
1. Implement helper functions to convert Rust errors to Rhai errors:
- `io_error_to_rhai_error` - Converts io::Error to EvalAltResult
- `tera_error_to_rhai_error` - Converts tera::Error to EvalAltResult
- `string_error_to_rhai_error` - Converts String error to EvalAltResult
## 4. Example Usage in Rhai Scripts
### TextReplacer Example
```rhai
// Create a TextReplacer with multiple replacements
let replacer = text_replacer_new()
.pattern("foo").replacement("bar").regex(true)
.pattern("hello").replacement("world")
.build();
// Use the replacer
let result = replacer.replace("foo hello foo");
println(result); // Outputs: "bar world bar"
// Replace in a file
let file_result = replacer.replace_file("input.txt");
println(file_result);
// Replace and write to a new file
replacer.replace_file_to("input.txt", "output.txt");
```
### TemplateBuilder Example
```rhai
// Create a TemplateBuilder
let template = template_builder_open("template.txt")
.add_var("name", "John")
.add_var("age", 30)
.add_var("items", ["apple", "banana", "cherry"]);
// Render the template
let result = template.render();
println(result);
// Render to a file
template.render_to_file("output.html");
```
### Fix and Dedent Examples
```rhai
// Use name_fix and path_fix
let fixed_name = name_fix("Hello World!");
println(fixed_name); // Outputs: "hello_world"
let fixed_path = path_fix("/path/to/Hello World!");
println(fixed_path); // Outputs: "/path/to/hello_world"
// Use dedent and prefix
let indented_text = " line 1\n line 2\n line 3";
let dedented = dedent(indented_text);
println(dedented); // Outputs: "line 1\nline 2\n line 3"
let text = "line 1\nline 2\nline 3";
let prefixed = prefix(text, " ");
println(prefixed); // Outputs: " line 1\n line 2\n line 3"

View File

@@ -1,27 +0,0 @@
extern crate sal;
use rhai::Engine;
use std::fs;
use std::error::Error;
// Import the SAL library
use sal::rhai;
fn main() -> Result<(), Box<dyn Error>> {
// Create a new Rhai engine
let mut engine = Engine::new();
// Register all SAL modules with the engine
rhai::register(&mut engine)?;
// Read the test script
let script = fs::read_to_string("test_git.rhai")?;
// Evaluate the script
match engine.eval::<()>(&script) {
Ok(_) => println!("Script executed successfully"),
Err(e) => eprintln!("Script execution error: {}", e),
}
Ok(())
}

View File

@@ -1,7 +1,7 @@
use regex::Regex;
use std::fs;
use std::io::{self, Read};
use std::io::{self, Read, Seek, SeekFrom};
use std::path::Path;
/// Represents the type of replacement to perform.