From 2cd9faf4fab6e63097b5ce8355918e27e03cb0e0 Mon Sep 17 00:00:00 2001 From: despiegk Date: Thu, 8 May 2025 09:54:20 +0300 Subject: [PATCH] ... --- buildah_debug_implementation_plan.md | 207 ------- docs/git/git.md | 53 +- docs/os/download.md | 60 ++ docs/os/fs.md | 338 +++++++++++ docs/os/package.md | 157 +++++ docs/process/process.md | 223 +++++++ .../{ => _archive}/03_process_management.rhai | 0 .../{ => _archive}/06_file_read_write.rhai | 0 examples/{ => _archive}/container_example.rs | 0 .../{ => _archive}/containerd_grpc_setup.rhai | 0 examples/{ => _archive}/download_test.rhai | 0 examples/{ => _archive}/fs_test.rhai | 0 examples/{ => _archive}/install_deb.rhai | 0 examples/{ => _archive}/instructions_grpc.md | 0 .../{ => _archive}/package_management.rhai | 0 examples/{ => _archive}/package_test.rs | 0 examples/{ => _archive}/process_long.rhai | 0 .../{ => _archive}/process_silent_test.rhai | 0 examples/{ => _archive}/process_test.rhai | 0 examples/{ => _archive}/rfs_example.rhai | 0 examples/{ => _archive}/run_all_tests.rhai | 0 examples/{ => _archive}/run_test.rhai | 0 examples/{ => _archive}/sample.rhai | 0 examples/{ => _archive}/stdout_test.rhai | 0 examples/{ => _archive}/text_tools.rhai | 0 examples/{ => _archive}/write_read.rhai | 0 .../directories.rhai} | 4 +- .../files.rhai} | 0 .../hello.rhai} | 0 examples/{ => containers}/buildah.rhai | 0 examples/{ => containers}/buildah_debug.rhai | 0 .../{ => containers}/nerdctl_install.rhai | 0 .../{ => containers}/nerdctl_webserver.rhai | 0 examples/process/kill.rhai | 28 + examples/process/process_get.rhai | 39 ++ examples/process/process_list.rhai | 29 + examples/process/run_all_options.rhai | 36 ++ examples/process/run_basic.rhai | 18 + examples/process/run_ignore_error.rhai | 29 + examples/process/run_log.rhai | 13 + examples/process/run_silent.rhai | 22 + examples/process/which.rhai | 25 + examples/rhai_file_test_dir/append_file.txt | 5 - examples/rhai_file_test_dir/test_file.txt | 2 - rfs_implementation_plan.md | 474 --------------- src/git_interface_redesign_plan.md | 212 ------- src/package_implementation_plan.md | 565 ------------------ src/rhai/mod.rs | 1 - src/rhai/process.rs | 170 +++--- src/rhai_text_implementation_plan.md | 133 ----- src/run_git_test.rs | 27 - src/text/replace.rs | 2 +- 52 files changed, 1115 insertions(+), 1757 deletions(-) delete mode 100644 buildah_debug_implementation_plan.md create mode 100644 docs/os/download.md create mode 100644 docs/os/fs.md create mode 100644 docs/os/package.md create mode 100644 docs/process/process.md rename examples/{ => _archive}/03_process_management.rhai (100%) rename examples/{ => _archive}/06_file_read_write.rhai (100%) rename examples/{ => _archive}/container_example.rs (100%) rename examples/{ => _archive}/containerd_grpc_setup.rhai (100%) rename examples/{ => _archive}/download_test.rhai (100%) rename examples/{ => _archive}/fs_test.rhai (100%) rename examples/{ => _archive}/install_deb.rhai (100%) rename examples/{ => _archive}/instructions_grpc.md (100%) rename examples/{ => _archive}/package_management.rhai (100%) rename examples/{ => _archive}/package_test.rs (100%) rename examples/{ => _archive}/process_long.rhai (100%) rename examples/{ => _archive}/process_silent_test.rhai (100%) rename examples/{ => _archive}/process_test.rhai (100%) rename examples/{ => _archive}/rfs_example.rhai (100%) rename examples/{ => _archive}/run_all_tests.rhai (100%) rename examples/{ => _archive}/run_test.rhai (100%) rename examples/{ => _archive}/sample.rhai (100%) rename examples/{ => _archive}/stdout_test.rhai (100%) rename examples/{ => _archive}/text_tools.rhai (100%) rename examples/{ => _archive}/write_read.rhai (100%) rename examples/{05_directory_operations.rhai => basics/directories.rhai} (94%) rename examples/{02_file_operations.rhai => basics/files.rhai} (100%) rename examples/{01_hello_world.rhai => basics/hello.rhai} (100%) rename examples/{ => containers}/buildah.rhai (100%) rename examples/{ => containers}/buildah_debug.rhai (100%) rename examples/{ => containers}/nerdctl_install.rhai (100%) rename examples/{ => containers}/nerdctl_webserver.rhai (100%) create mode 100644 examples/process/kill.rhai create mode 100644 examples/process/process_get.rhai create mode 100644 examples/process/process_list.rhai create mode 100644 examples/process/run_all_options.rhai create mode 100644 examples/process/run_basic.rhai create mode 100644 examples/process/run_ignore_error.rhai create mode 100644 examples/process/run_log.rhai create mode 100644 examples/process/run_silent.rhai create mode 100644 examples/process/which.rhai delete mode 100644 examples/rhai_file_test_dir/append_file.txt delete mode 100644 examples/rhai_file_test_dir/test_file.txt delete mode 100644 rfs_implementation_plan.md delete mode 100644 src/git_interface_redesign_plan.md delete mode 100644 src/package_implementation_plan.md delete mode 100644 src/rhai_text_implementation_plan.md delete mode 100644 src/run_git_test.rs diff --git a/buildah_debug_implementation_plan.md b/buildah_debug_implementation_plan.md deleted file mode 100644 index 2276ac5..0000000 --- a/buildah_debug_implementation_plan.md +++ /dev/null @@ -1,207 +0,0 @@ -# Buildah Debug Implementation Plan - -## Current State - -1. The `Builder` struct already has a `debug` field and methods to get and set it (`debug()` and `set_debug()`). -2. There's a thread-local `DEBUG` variable with functions to get and set it. -3. The `execute_buildah_command` function checks the thread-local debug flag and outputs some debug information. -4. There's an unused `execute_buildah_command_with_debug` function that takes a `Builder` reference. - -## Requirements - -1. Use only the Builder's debug flag, not the thread-local debug flag. -2. When debug is true, output stdout/stderr regardless of whether the command succeeds or fails. -3. If debug is false but there's an error, still output all information. - -## Implementation Plan - -### 1. Keep the Existing Thread-Local DEBUG Variable - -We'll keep the existing thread-local DEBUG variable that's already in the code: - -```rust -// Thread-local storage for debug flag -thread_local! { - static DEBUG: std::cell::RefCell = std::cell::RefCell::new(false); -} -``` - -### 2. Modify the Builder Methods to Set/Clear the Thread-Local Debug Flag - -We'll modify each Builder method to set the thread-local debug flag from the Builder's debug flag before calling `execute_buildah_command` and restore it afterward: - -```rust -pub fn run(&self, command: &str) -> Result { - if let Some(container_id) = &self.container_id { - // Save the current debug flag - let previous_debug = thread_local_debug(); - - // Set the thread-local debug flag from the Builder's debug flag - set_thread_local_debug(self.debug); - - // Execute the command - let result = execute_buildah_command(&["run", container_id, "sh", "-c", command]); - - // Restore the previous debug flag - set_thread_local_debug(previous_debug); - - result - } else { - Err(BuildahError::Other("No container ID available".to_string())) - } -} -``` - -### 3. Keep the Existing execute_buildah_command Function - -The existing `execute_buildah_command` function already checks the thread-local debug flag, so we don't need to modify it: - -```rust -pub fn execute_buildah_command(args: &[&str]) -> Result { - // Get the debug flag from thread-local storage - let debug = thread_local_debug(); - - if debug { - println!("Executing buildah command: buildah {}", args.join(" ")); - } - - // ... rest of the function ... -} -``` - -### 4. Update the execute_buildah_command Function to Output stdout/stderr - -We need to modify the `execute_buildah_command` function to output stdout/stderr when debug is true, regardless of success/failure: - -```rust -pub fn execute_buildah_command(args: &[&str]) -> Result { - // Get the debug flag from thread-local storage - let debug = thread_local_debug(); - - if debug { - println!("Executing buildah command: buildah {}", args.join(" ")); - } - - let output = Command::new("buildah") - .args(args) - .output(); - - 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(BuildahError::CommandFailed(format!("Command failed with code {}: {}", - result.code, result.stderr.trim()))) - } - }, - Err(e) => { - // Always output error information - println!("Command execution failed: {}", e); - Err(BuildahError::CommandExecutionFailed(e)) - } - } -} -``` - -### 5. Handle Static Methods - -For static methods, we'll just call `execute_buildah_command` directly, which will use the thread-local debug flag: - -```rust -pub fn images() -> Result, BuildahError> { - let result = execute_buildah_command(&["images", "--json"])?; - // Rest of the method... -} -``` - -If we want to support debugging in static methods, we could add an optional debug parameter: - -```rust -pub fn images(debug: bool) -> Result, BuildahError> { - // Save the current debug flag - let previous_debug = thread_local_debug(); - - // Set the thread-local debug flag - set_thread_local_debug(debug); - - // Execute the command - let result = execute_buildah_command(&["images", "--json"]); - - // Restore the previous debug flag - set_thread_local_debug(previous_debug); - - // Process the result - match result { - Ok(cmd_result) => { - // Parse JSON and return images... - }, - Err(e) => Err(e), - } -} - -// Backward compatibility method -pub fn images() -> Result, BuildahError> { - Self::images(false) -} -``` - -### 6. Update Rhai Bindings - -We still need to update the Rhai bindings to expose the debug functionality: - -```rust -// Add a debug getter and setter -engine.register_get("debug", |builder: &mut Builder| builder.debug()); -engine.register_set("debug", |builder: &mut Builder, debug: bool| { builder.set_debug(debug); }); -``` - -### 7. Remove Unused Code - -We can remove the unused `execute_buildah_command_with_debug` function. - -## Implementation Flow - -1. Modify the `execute_buildah_command` function to output stdout/stderr when debug is true -2. Update all Builder methods to set/restore the thread-local debug flag -3. Update static methods to optionally accept a debug parameter -4. Update Rhai bindings to expose the debug functionality -5. Remove unused code - -## Benefits - -1. Minimal changes to the existing codebase -2. No changes to function signatures -3. Backward compatibility with existing code -4. Improved debugging capabilities \ No newline at end of file diff --git a/docs/git/git.md b/docs/git/git.md index 19b953e..ebe3287 100644 --- a/docs/git/git.md +++ b/docs/git/git.md @@ -183,36 +183,33 @@ Commits staged changes. Pushes committed changes to the remote. - **Description**: Performs `git push` to the default remote and branch. -- **Returns**: `GitRepo` - The same `GitRepo` object for chaining. Halts on error (e.g., network issues, push rejected). -- **Rhai Example** (assuming `app_repo` is a `GitRepo` object with committed changes): - ```rhai - print(`Pushing changes for ${app_repo.path()}...`); - app_repo.push(); // Halts on error - print("Push successful."); - ``` +- **Returns**: `GitRepo` + +```rhai +print(`Pushing changes for ${app_repo.path()}...`); +app_repo.push(); // Halts on error +print("Push successful."); +``` ## Chaining Operations -`GitRepo` methods like `pull`, `reset`, `commit`, and `push` return the `GitRepo` object, allowing for chained operations. If any operation in the chain fails, script execution halts. +```rhai +let git_tree = git_tree_new("./my_projects"); +// Assumes "my_writable_app" exists and you have write access. +// get() will halt if not found. +let app_repo = git_tree.get("my_writable_app"); +print(`Performing chained operations on ${app_repo.path()}`); -- **Rhai Example**: - ```rhai - let git_tree = git_tree_new("./my_projects"); - // Assumes "my_writable_app" exists and you have write access. - // get() will halt if not found. - let app_repo = git_tree.get("my_writable_app"); - print(`Performing chained operations on ${app_repo.path()}`); +// This example demonstrates a common workflow. +// Ensure the repo state is suitable (e.g., changes exist for commit/push). +app_repo.pull() + .commit("Rhai: Chained operations - automated update") // Commits if pull results in changes or local changes existed and were staged. + .push(); +print("Chained pull, commit, and push reported successful."); - // This example demonstrates a common workflow. - // Ensure the repo state is suitable (e.g., changes exist for commit/push). - app_repo.pull() - .commit("Rhai: Chained operations - automated update") // Commits if pull results in changes or local changes existed and were staged. - .push(); - print("Chained pull, commit, and push reported successful."); - - // Alternative: - // app_repo.pull(); - // if app_repo.has_changes() { - // app_repo.commit("Updates").push(); - // } - ``` +// Alternative: +// app_repo.pull(); +// if app_repo.has_changes() { +// app_repo.commit("Updates").push(); +// } +``` diff --git a/docs/os/download.md b/docs/os/download.md new file mode 100644 index 0000000..5d80338 --- /dev/null +++ b/docs/os/download.md @@ -0,0 +1,60 @@ +# os.download Module + +### `download(url, dest, min_size_kb)` + +Download a file from URL to destination using the curl command. + +- **Description**: Downloads a file from the given `url`. If `dest` is a directory, the filename is derived from the URL. If `dest` is a file path, it is used directly. Requires the `curl` command to be available. Halts script execution on download or file writing errors, or if the downloaded file size is less than `min_size_kb`. Returns the destination path. +- **Returns**: `String` - The path where the file was downloaded. +- **Arguments**: + - `url`: `String` - The URL of the file to download. + - `dest`: `String` - The destination path (directory or file). + - `min_size_kb`: `Integer` - The minimum expected size of the downloaded file in kilobytes. + +```rhai +let download_url = "https://example.com/archive.zip"; +let download_dest_dir = "/tmp/downloads"; +print(`Downloading ${download_url} to ${download_dest_dir}...`); +let downloaded_file_path = os::download(download_url, download_dest_dir, 50); // Halts on error +print(`Downloaded to: ${downloaded_file_path}`); +``` + +--- + +### `download_file(url, dest, min_size_kb)` + +Download a file from URL to a specific file destination using the curl command. + +- **Description**: Downloads a file from the given `url` directly to the specified file `dest`. Requires the `curl` command. Halts script execution on download or file writing errors, or if the downloaded file size is less than `min_size_kb`. Returns the destination path. +- **Returns**: `String` - The path where the file was downloaded. +- **Arguments**: + - `url`: `String` - The URL of the file to download. + - `dest`: `String` - The full path where the file should be saved. + - `min_size_kb`: `Integer` - The minimum expected size of the downloaded file in kilobytes. + +```rhai +let data_url = "https://example.com/dataset.tar.gz"; +let local_path = "/opt/data/dataset.tar.gz"; +print(`Downloading ${data_url} to ${local_path}...`); +os::download_file(data_url, local_path, 1024); // Halts on error +print(`Downloaded dataset to: ${local_path}`); +``` + +--- + +### `download_install(url, min_size_kb)` + +Download a file and install it if it\'s a supported package format. + +- **Description**: Downloads a file from the given `url` to a temporary location and then attempts to install it using the appropriate system package manager if the file format is supported (e.g., `.deb` on Ubuntu, `.pkg` or `.dmg` on MacOS). Requires the `curl` command and the system package manager. Halts script execution on download, installation, or file size errors. Returns a success message. +- **Returns**: `String` - A success message upon successful download and installation attempt. +- **Arguments**: + - `url`: `String` - The URL of the package file to download and install. + - `min_size_kb`: `Integer` - The minimum expected size of the downloaded file in kilobytes. + +```rhai +let package_url = "https://example.com/mytool.deb"; +print(`Downloading and installing ${package_url}...`); +os::download_install(package_url, 300); // Halts on error +print("Installation attempt finished."); +``` diff --git a/docs/os/fs.md b/docs/os/fs.md new file mode 100644 index 0000000..4c0e814 --- /dev/null +++ b/docs/os/fs.md @@ -0,0 +1,338 @@ +# os.fs Module + +The `os` module provides functions for interacting with the operating system, including file system operations, command execution checks, downloads, and package management. + +All functions that interact with the file system or external commands will halt the script execution if an error occurs, unless explicitly noted otherwise. + +--- + +### `copy(src, dest)` + +Recursively copy a file or directory from source to destination. + +- **Description**: Performs a recursive copy operation. Halts script execution on failure. +- **Returns**: `String` - The destination path. +- **Arguments**: + - `src`: `String` - The path to the source file or directory. + - `dest`: `String` - The path to the destination file or directory. + +```rhai +print("Copying directory..."); +let source_dir = "/tmp/source_data"; +let dest_dir = "/backup/source_data"; +let copied_path = os::copy(source_dir, dest_dir); // Halts on error +print(`Copied ${source_dir} to ${copied_path}`); +``` + +--- + +### `exist(path)` + +Check if a file or directory exists. + +- **Description**: Checks for the presence of a file or directory at the given path. This function does NOT halt on error if the path is invalid or permissions prevent checking. +- **Returns**: `Boolean` - `true` if the path exists, `false` otherwise. +- **Arguments**: + - `path`: `String` - The path to check. + +```rhai +if os::exist("config.json") { + print(`${file_path} exists.`); +} else { + print(`${file_path} does not exist.`); +} +``` + +--- + +### `find_file(dir, filename)` + +Find a file in a directory (with support for wildcards). + +- **Description**: Searches for a file matching `filename` within the specified `dir`. Supports simple wildcards like `*` and `?`. Halts script execution if the directory cannot be read or if no file is found. +- **Returns**: `String` - The path to the first file found that matches the pattern. +- **Arguments**: + - `dir`: `String` - The directory to search within. + - `filename`: `String` - The filename pattern to search for (e.g., `"*.log"`). + +```rhai +let log_file = os::find_file("/var/log", "syslog*.log"); // Halts if not found or directory error +print(`Found log file: ${log_file}`); +``` + +--- + +### `find_files(dir, filename)` + +Find multiple files in a directory (recursive, with support for wildcards). + +- **Description**: Recursively searches for all files matching `filename` within the specified `dir` and its subdirectories. Supports simple wildcards. Halts script execution if the directory cannot be read. +- **Returns**: `Array` of `String` - An array containing paths to all matching files. +- **Arguments**: + - `dir`: `String` - The directory to start the recursive search from. + - `filename`: `String` - The filename pattern to search for (e.g., `"*.tmp"`). + +```rhai +let temp_files = os::find_files("/tmp", "*.swp"); // Halts on directory error +print("Found temporary files:"); +for file in temp_files { + print(`- ${file}`); +} +``` + +--- + +### `find_dir(dir, dirname)` + +Find a directory in a parent directory (with support for wildcards). + +- **Description**: Searches for a directory matching `dirname` within the specified `dir`. Supports simple wildcards. Halts script execution if the directory cannot be read or if no directory is found. +- **Returns**: `String` - The path to the first directory found that matches the pattern. +- **Arguments**: + - `dir`: `String` - The directory to search within. + - `dirname`: `String` - The directory name pattern to search for (e.g., `"backup_*"`). + +```rhai +let latest_backup_dir = os::find_dir("/mnt/backups", "backup_20*"); // Halts if not found or directory error +print(`Found backup directory: ${latest_backup_dir}`); +``` + +--- + +### `find_dirs(dir, dirname)` + +Find multiple directories in a parent directory (recursive, with support for wildcards). + +- **Description**: Recursively searches for all directories matching `dirname` within the specified `dir` and its subdirectories. Supports simple wildcards. Halts script execution if the directory cannot be read. +- **Returns**: `Array` of `String` - An array containing paths to all matching directories. +- **Arguments**: + - `dir`: `String` - The directory to start the recursive search from. + - `dirname`: `String` - The directory name pattern to search for (e.g., `"project_*_v?"`). + +```rhai +let project_versions = os::find_dirs("/home/user/dev", "project_*_v?"); // Halts on directory error +print("Found project version directories:"); +for dir in project_versions { + print(`- ${dir}`); +} +``` + +--- + +### `delete(path)` + +Delete a file or directory (defensive - doesn't error if file doesn't exist). + +- **Description**: Deletes the file or directory at the given path. If the path does not exist, the function does nothing and does not halt. Halts script execution on other errors (e.g., permission denied, directory not empty). Returns the path that was attempted to be deleted. +- **Returns**: `String` - The path that was given as input. +- **Arguments**: + - `path`: `String` - The path to the file or directory to delete. + +```rhai +let temp_path = "/tmp/temporary_item"; +print(`Attempting to delete: ${temp_path}`); +os::delete(temp_path); // Halts on permissions or non-empty directory error +print("Deletion attempt finished."); +``` + +--- + +### `mkdir(path)` + +Create a directory and all parent directories (defensive - doesn't error if directory exists). + +- **Description**: Creates the directory at the given path, including any necessary parent directories. If the directory already exists, the function does nothing and does not halt. Halts script execution on other errors (e.g., permission denied). Returns the path that was created (or already existed). +- **Returns**: `String` - The path that was created or checked. +- **Arguments**: + - `path`: `String` - The path to the directory to create. + +```rhai +let new_dir = "/data/processed/reports"; +print(`Ensuring directory exists: ${new_dir}`); +os::mkdir(new_dir); // Halts on permission error +print("Directory check/creation finished."); +``` + +--- + +### `file_size(path)` + +Get the size of a file in bytes. + +- **Description**: Returns the size of the file at the given path. Halts script execution if the file does not exist or cannot be accessed. +- **Returns**: `Integer` - The size of the file in bytes (as i64). +- **Arguments**: + - `path`: `String` - The path to the file. + +```rhai +let file_path = "important_document.pdf"; +let size = os::file_size(file_path); // Halts if file not found or cannot read +print(`File size: ${size} bytes`); +``` + +--- + +### `rsync(src, dest)` + +Sync directories using rsync (or platform equivalent). + +- **Description**: Synchronizes the contents of the source directory (`src`) to the destination directory (`dest`) using the system's available rsync-like command. Halts script execution on any error during the sync process. Returns a success message string. +- **Returns**: `String` - A success message indicating the operation completed. +- **Arguments**: + - `src`: `String` - The source directory. + - `dest`: `String` - The destination directory. + +```rhai +let source = "/local/project_files"; +let destination = "/remote/backup/project_files"; +print(`Syncing from ${source} to ${destination}...`); +let result_message = os::rsync(source, destination); // Halts on error +print(`Sync successful: ${result_message}`); +``` + +--- + +### `chdir(path)` + +Change the current working directory. + +- **Description**: Changes the current working directory of the script process. Halts script execution if the directory does not exist or cannot be accessed. Returns the new current working directory path. +- **Returns**: `String` - The absolute path of the directory the process changed into. +- **Arguments**: + - `path`: `String` - The path to change the working directory to. + +```rhai +print(`Current directory: ${os::chdir(".")}`); // Use "." to get current path +let new_cwd = "/tmp"; +os::chdir(new_cwd); // Halts if directory not found or access denied +print(`Changed directory to: ${os::chdir(".")}`); +``` + +--- + +### `file_read(path)` + +Read the contents of a file. + +- **Description**: Reads the entire content of the file at the given path into a string. Halts script execution if the file does not exist or cannot be read. +- **Returns**: `String` - The content of the file. +- **Arguments**: + - `path`: `String` - The path to the file. + +```rhai +let config_content = os::file_read("settings.conf"); // Halts if file not found or cannot read +print("Config content:"); +print(config_content); +``` + +--- + +### `file_write(path, content)` + +Write content to a file (creates the file if it doesn\'t exist, overwrites if it does). + +- **Description**: Writes the specified `content` to the file at the given `path`. If the file exists, its content is replaced. If it doesn't exist, it is created. Halts script execution on error (e.g., permission denied, invalid path). Returns the path written to. +- **Returns**: `String` - The path of the file written to. +- **Arguments**: + - `path`: `String` - The path to the file. + - `content`: `String` - The content to write to the file. + +```rhai +let output_path = "/tmp/hello.txt"; +let text_to_write = "Hello from Rhai!"; +os::file_write(output_path, text_to_write); // Halts on error +print(`Wrote to ${output_path}`); +``` + +--- + +### `file_write_append(path, content)` + +Append content to a file (creates the file if it doesn\'t exist). + +- **Description**: Appends the specified `content` to the end of the file at the given `path`. If the file does not exist, it is created. Halts script execution on error (e.g., permission denied, invalid path). Returns the path written to. +- **Returns**: `String` - The path of the file written to. +- **Arguments**: + - `path`: `String` - The path to the file. + - `content`: `String` - The content to append to the file. + +```rhai +let log_path = "application.log"; +let log_entry = "User login failed.\n"; +os::file_write_append(log_path, log_entry); // Halts on error +print(`Appended to ${log_path}`); +``` + +--- + +### `mv(src, dest)` + +Move a file or directory from source to destination. + +- **Description**: Moves the file or directory from `src` to `dest`. Halts script execution on error (e.g., permission denied, source not found, destination exists and cannot be overwritten). Returns the destination path. +- **Returns**: `String` - The path of the destination. +- **Arguments**: + - `src`: `String` - The path to the source file or directory. + - `dest`: `String` - The path to the destination. + +```rhai +let old_path = "/tmp/report.csv"; +let new_path = "/archive/reports/report_final.csv"; +os::mv(old_path, new_path); // Halts on error +print(`Moved ${old_path} to ${new_path}`); +``` + +--- + +### `which(command)` + +Check if a command exists in the system PATH. + +- **Description**: Searches the system's PATH environment variable for the executable `command`. This function does NOT halt on error; it returns an empty string if the command is not found. +- **Returns**: `String` - The full path to the command executable if found, otherwise an empty string (`""`). +- **Arguments**: + - `command`: `String` - The name of the command to search for (e.g., `"git"`). + +```rhai +let git_path = os::which("git"); +if git_path != "" { + print(`Git executable found at: ${git_path}`); +} else { + print("Git executable not found in PATH."); +} +``` + +--- + +### `cmd_ensure_exists(commands)` + +Ensure that one or more commands exist in the system PATH. + +- **Description**: Checks if all command names specified in the `commands` string (space or comma separated) exist in the system's PATH. Halts script execution if any of the commands are not found. Returns a success message if all commands are found. +- **Returns**: `String` - A success message. +- **Arguments**: + - `commands`: `String` - A string containing one or more command names, separated by spaces or commas (e.g., `"curl,tar,unzip"`). + +```rhai +print("Ensuring required commands are available..."); +os::cmd_ensure_exists("git curl docker"); // Halts if any command is missing +print("All required commands found."); +``` + +--- + +### `chmod_exec(path)` + +Make a file executable (equivalent to chmod +x). + +- **Description**: Sets the executable permission for the file at the given `path` for the owner, group, and others. Halts script execution on error (e.g., file not found, permission denied). Returns the path modified. +- **Returns**: `String` - The path of the file whose permissions were modified. +- **Arguments**: + - `path`: `String` - The path to the file. + +```rhai +let script_path = "/usr/local/bin/myscript"; +print(`Making ${script_path} executable...`); +os::chmod_exec(script_path); // Halts on error +print("Permissions updated."); +``` diff --git a/docs/os/package.md b/docs/os/package.md new file mode 100644 index 0000000..8dda6eb --- /dev/null +++ b/docs/os/package.md @@ -0,0 +1,157 @@ +# os.package Module + +### `package_install(package)` + +Install a package using the system package manager. + +- **Description**: Installs the specified `package` using the detected system package manager (e.g., `apt` on Ubuntu, `brew` on MacOS). Halts script execution if the package manager command fails. Returns a success message. +- **Returns**: `String` - A message indicating successful installation. +- **Arguments**: + - `package`: `String` - The name of the package to install (e.g., `"nano"`). + +```rhai +print("Installing 'nano' package..."); +os::package_install("nano"); // Halts on package manager error +print("'nano' installed successfully."); +``` + +--- + +### `package_remove(package)` + +Remove a package using the system package manager. + +- **Description**: Removes the specified `package` using the detected system package manager. Halts script execution if the package manager command fails. Returns a success message. +- **Returns**: `String` - A message indicating successful removal. +- **Arguments**: + - `package`: `String` - The name of the package to remove (e.g., `"htop"`). + +```rhai +print("Removing 'htop' package..."); +os::package_remove("htop"); // Halts on package manager error +print("'htop' removed successfully."); +``` + +--- + +### `package_update()` + +Update package lists using the system package manager. + +- **Description**: Updates the package lists that the system package manager uses (e.g., `apt update`, `brew update`). Halts script execution if the package manager command fails. Returns a success message. +- **Returns**: `String` - A message indicating successful update. +- **Arguments**: None. + +```rhai +print("Updating package lists..."); +os::package_update(); // Halts on package manager error +print("Package lists updated."); +``` + +--- + +### `package_upgrade()` + +Upgrade installed packages using the system package manager. + +- **Description**: Upgrades installed packages using the detected system package manager (e.g., `apt upgrade`, `brew upgrade`). Halts script execution if the package manager command fails. Returns a success message. +- **Returns**: `String` - A message indicating successful upgrade. +- **Arguments**: None. + +```rhai +print("Upgrading installed packages..."); +os::package_upgrade(); // Halts on package manager error +print("Packages upgraded."); +``` + +--- + +### `package_list()` + +List installed packages using the system package manager. + +- **Description**: Lists the names of packages installed on the system using the detected package manager. Halts script execution if the package manager command fails. +- **Returns**: `Array` of `String` - An array containing the names of installed packages. +- **Arguments**: None. + +```rhai +print("Listing installed packages..."); +let installed_packages = os::package_list(); // Halts on package manager error +for pkg in installed_packages { + print(`- ${pkg}`); +} +``` + +--- + +### `package_search(query)` + +Search for packages using the system package manager. + +- **Description**: Searches for packages matching the given `query` using the detected system package manager. Halts script execution if the package manager command fails. +- **Returns**: `Array` of `String` - An array containing the search results (package names and/or descriptions). +- **Arguments**: + - `query`: `String` - The search term. + +```rhai +print("Searching for 'python' packages..."); +let python_packages = os::package_search("python"); // Halts on package manager error +for pkg in python_packages { + print(`- ${pkg}`); +} +``` + +--- + +### `package_is_installed(package)` + +Check if a package is installed using the system package manager. + +- **Description**: Checks if the specified `package` is installed using the detected system package manager. Halts script execution if the package manager command itself fails (e.g., command not found), but does NOT halt if the package is simply not found. +- **Returns**: `Boolean` - `true` if the package is installed, `false` otherwise. +- **Arguments**: + - `package`: `String` - The name of the package to check (e.g., `"wget"`). + +```rhai +let package_name = "wget"; +if os::package_is_installed(package_name) { // Halts on package manager command error + print(`${package_name} is installed.`); +} else { + print(`${package_name} is not installed.`); +} +``` + +--- + +### `package_set_debug(debug)` + +Set the debug mode for package management operations. + +- **Description**: Enables or disables debug output for subsequent package management operations. This function does NOT halt on error and always returns the boolean value it was set to. +- **Returns**: `Boolean` - The boolean value that the debug flag was set to. +- **Arguments**: + - `debug`: `Boolean` - Set to `true` to enable debug output, `false` to disable. + +```rhai +print("Enabling package debug output."); +os::package_set_debug(true); +// Subsequent package operations will print debug info + +print("Disabling package debug output."); +os::package_set_debug(false); +``` + +--- + +### `package_platform()` + +Get the current platform name for package management. + +- **Description**: Returns the name of the operating system platform as detected by the package manager logic. This function does NOT halt on error; it returns `"Unknown"` if the platform cannot be determined. +- **Returns**: `String` - The platform name, one of `"Ubuntu"`, `"MacOS"`, or `"Unknown"`. +- **Arguments**: None. + +```rhai +let platform = os::package_platform(); // Does not halt on error +print(`Detected package platform: ${platform}`); +``` diff --git a/docs/process/process.md b/docs/process/process.md new file mode 100644 index 0000000..3718578 --- /dev/null +++ b/docs/process/process.md @@ -0,0 +1,223 @@ +# Process Module + +The `process` module provides functions for running external commands and managing system processes using a builder pattern for command execution. + +For running commands, you start with the `run()` function which returns a `CommandBuilder` object. You can then chain configuration methods like `silent()`, `ignore_error()`, and `log()` before finally calling the `do()` method to execute the command. + +By default, command execution using the builder (`.do()`) will halt the script execution if the command itself fails (returns a non-zero exit code) or if there's an operating system error preventing the command from running. You can change this behavior with `ignore_error()`. + +Other process management functions (`which`, `kill`, `process_list`, `process_get`) have specific error handling behaviors described below. + +--- + +### `CommandResult` + +An object returned by command execution functions (`.do()`) containing the result of the command. + +- **Properties**: + - `stdout`: `String` - The standard output of the command. + - `stderr`: `String` - The standard error of the command. + - `success`: `Boolean` - `true` if the command exited with code 0, `false` otherwise. + - `code`: `Integer` - The exit code of the command. + +```rhai +let result = run("echo hi").do(); +print(`Success: ${result.success}, Output: ${result.stdout}`); +``` + +--- + +### `ProcessInfo` + +An object found by process listing/getting functions (`process_list`, `process_get`) containing information about a running process. + +- **Properties**: + - `pid`: `Integer` - The process ID. + - `name`: `String` - The name of the process executable. + - `memory`: `Integer` - The memory usage of the process (unit depends on the operating system, typically KB or bytes). + - `cpu`: `Float` - The CPU usage percentage (value and meaning may vary by operating system). + +```rhai +let processes = process_list("my_service"); +if (processes.len() > 0) { + let first_proc = processes[0]; + print(`Process ${first_proc.name} (PID: ${first_proc.pid}) is running.`); +} +``` + +--- + +### `run(command)` + +Start building a command execution. + +- **Description**: Initializes a `CommandBuilder` for the given command string. This is the entry point to configure and run a process. +- **Returns**: `CommandBuilder` - A builder object for configuring the command. +- **Arguments**: + - `command`: `String` - The command string to execute. Can include arguments and be a simple multiline script. + +```rhai +let cmd_builder = run("ls -l"); +// Now you can chain methods like .silent(), .ignore_error(), .log() +``` + +--- + +### `CommandBuilder:silent()` + +Configure the command to run silently. + +- **Description**: Suppresses real-time standard output and standard error from being printed to the script's console during command execution. The output is still captured in the resulting `CommandResult`. +- **Returns**: `CommandBuilder` - Returns `self` for chaining. +- **Arguments**: None. + +```rhai +print("Running silent command..."); +run("echo This won\'t show directly").silent().do(); +print("Silent command finished."); +``` + +--- + +### `CommandBuilder:ignore_error()` + +Configure the command to ignore non-zero exit codes. + +- **Description**: By default, the `do()` method halts script execution if the command returns a non-zero exit code. Calling `ignore_error()` prevents this. The `CommandResult` will still indicate `success: false` and contain the non-zero `code`, allowing the script to handle the command failure explicitly. OS errors preventing the command from running will still cause a halt. +- **Returns**: `CommandBuilder` - Returns `self` for chaining. +- **Arguments**: None. + +```rhai +print("Running command that will fail but not halt..."); +let result = run("exit 1").ignore_error().do(); // Will not halt +if (!result.success) { + print(`Command failed as expected with code: ${result.code}`); +} +``` + +--- + +### `CommandBuilder:log()` + +Configure the command to log the execution details. + +- **Description**: Enables logging of the command string before execution. +- **Returns**: `CommandBuilder` - Returns `self` for chaining. +- **Arguments**: None. + +```rhai +print("Running command with logging..."); +run("ls /tmp").log().do(); // Will print the "ls /tmp" command before running +print("Command finished."); +``` + +--- + +### `CommandBuilder:do()` + +Execute the configured command. + +- **Description**: Runs the command with the options set by the builder methods. Waits for the command to complete and returns the `CommandResult`. This method is the final step in the command execution builder chain. Halts based on the `ignore_error()` setting and OS errors. +- **Returns**: `CommandResult` - An object containing the output and status of the command. +- **Arguments**: None. + +```rhai +print("Running command using builder..."); +let command_result = run("pwd") + .log() // Log the command + .silent() // Don't print output live + .do(); // Execute and get result (halts on error by default) + +print(`Command output: ${command_result.stdout}`); + +// Example with multiple options +let fail_result = run("command_that_does_not_exist") + .ignore_error() // Don't halt on non-zero exit (though OS error might still halt) + .silent() // Don't print error live + .do(); + +if (!fail_result.success) { + print(`Failed command exited with code: ${fail_result.code} and stderr: ${fail_result.stderr}`); +} +``` + +--- + +### `which(cmd)` + +Check if a command exists in the system PATH. + +- **Description**: Searches the system's PATH environment variable for the executable `cmd`. This function does NOT halt if the command is not found; it returns an empty string. +- **Returns**: `String` - The full path to the command executable if found, otherwise an empty string (`""`). +- **Arguments**: + - `cmd`: `String` - The name of the command to search for (e.g., `"node"`). + +```rhai +let node_path = which("node"); // Does not halt if node is not found +if (node_path != "") { + print(`Node executable found at: ${node_path}`); +} else { + print("Node executable not found in PATH."); +} +``` + +--- + +### `kill(pattern)` + +Kill processes matching a pattern. + +- **Description**: Terminates running processes whose names match the provided `pattern`. Uses platform-specific commands (like `pkill` or equivalent). Halts script execution on error interacting with the system process list or kill command. +- **Returns**: `String` - A success message indicating the kill attempt finished. +- **Arguments**: + - `pattern`: `String` - A pattern to match against process names (e.g., `"nginx"`). + +```rhai +print("Attempting to kill processes matching 'my_service'..."); +// Use with caution! +kill("my_service"); // Halts on OS error during kill attempt +print("Kill command sent."); +``` + +--- + +### `process_list(pattern)` + +List processes matching a pattern (or all if pattern is empty). + +- **Description**: Lists information about running processes whose names match the provided `pattern`. If `pattern` is an empty string `""`, lists all processes. Halts script execution on error interacting with the system process list. Returns an empty array if no processes match the pattern. +- **Returns**: `Array` of `ProcessInfo` - An array of objects, each containing `pid` (Integer), `name` (String), `memory` (Integer), and `cpu` (Float). +- **Arguments**: + - `pattern`: `String` - A pattern to match against process names, or `""` for all processes. + +```rhai +print("Listing processes matching 'bash'..."); +let bash_processes = process_list("bash"); // Halts on OS error +if (bash_processes.len() > 0) { + print("Found bash processes:"); + for proc in bash_processes { + print(`- PID: ${proc.pid}, Name: ${proc.name}, CPU: ${proc.cpu}%, Memory: ${proc.memory}`); + } +} else { + print("No bash processes found."); +} +``` + +--- + +### `process_get(pattern)` + +Get a single process matching the pattern (error if 0 or more than 1 match). + +- **Description**: Finds exactly one running process whose name matches the provided `pattern`. Halts script execution if zero or more than one process matches the pattern, or on error interacting with the system process list. +- **Returns**: `ProcessInfo` - An object containing `pid` (Integer), `name` (String), `memory` (Integer), and `cpu` (Float). +- **Arguments**: + - `pattern`: `String` - A pattern to match against process names, expected to match exactly one process. + +```rhai +let expected_service_name = "my_critical_service"; +print(`Getting process info for '${expected_service_name}'...`); +// This will halt if the service isn't running, or if multiple services have this name +let service_proc_info = process_get(expected_service_name); +print(`Found process: PID ${service_proc_info.pid}, Name: ${service_proc_info.name}`); +``` diff --git a/examples/03_process_management.rhai b/examples/_archive/03_process_management.rhai similarity index 100% rename from examples/03_process_management.rhai rename to examples/_archive/03_process_management.rhai diff --git a/examples/06_file_read_write.rhai b/examples/_archive/06_file_read_write.rhai similarity index 100% rename from examples/06_file_read_write.rhai rename to examples/_archive/06_file_read_write.rhai diff --git a/examples/container_example.rs b/examples/_archive/container_example.rs similarity index 100% rename from examples/container_example.rs rename to examples/_archive/container_example.rs diff --git a/examples/containerd_grpc_setup.rhai b/examples/_archive/containerd_grpc_setup.rhai similarity index 100% rename from examples/containerd_grpc_setup.rhai rename to examples/_archive/containerd_grpc_setup.rhai diff --git a/examples/download_test.rhai b/examples/_archive/download_test.rhai similarity index 100% rename from examples/download_test.rhai rename to examples/_archive/download_test.rhai diff --git a/examples/fs_test.rhai b/examples/_archive/fs_test.rhai similarity index 100% rename from examples/fs_test.rhai rename to examples/_archive/fs_test.rhai diff --git a/examples/install_deb.rhai b/examples/_archive/install_deb.rhai similarity index 100% rename from examples/install_deb.rhai rename to examples/_archive/install_deb.rhai diff --git a/examples/instructions_grpc.md b/examples/_archive/instructions_grpc.md similarity index 100% rename from examples/instructions_grpc.md rename to examples/_archive/instructions_grpc.md diff --git a/examples/package_management.rhai b/examples/_archive/package_management.rhai similarity index 100% rename from examples/package_management.rhai rename to examples/_archive/package_management.rhai diff --git a/examples/package_test.rs b/examples/_archive/package_test.rs similarity index 100% rename from examples/package_test.rs rename to examples/_archive/package_test.rs diff --git a/examples/process_long.rhai b/examples/_archive/process_long.rhai similarity index 100% rename from examples/process_long.rhai rename to examples/_archive/process_long.rhai diff --git a/examples/process_silent_test.rhai b/examples/_archive/process_silent_test.rhai similarity index 100% rename from examples/process_silent_test.rhai rename to examples/_archive/process_silent_test.rhai diff --git a/examples/process_test.rhai b/examples/_archive/process_test.rhai similarity index 100% rename from examples/process_test.rhai rename to examples/_archive/process_test.rhai diff --git a/examples/rfs_example.rhai b/examples/_archive/rfs_example.rhai similarity index 100% rename from examples/rfs_example.rhai rename to examples/_archive/rfs_example.rhai diff --git a/examples/run_all_tests.rhai b/examples/_archive/run_all_tests.rhai similarity index 100% rename from examples/run_all_tests.rhai rename to examples/_archive/run_all_tests.rhai diff --git a/examples/run_test.rhai b/examples/_archive/run_test.rhai similarity index 100% rename from examples/run_test.rhai rename to examples/_archive/run_test.rhai diff --git a/examples/sample.rhai b/examples/_archive/sample.rhai similarity index 100% rename from examples/sample.rhai rename to examples/_archive/sample.rhai diff --git a/examples/stdout_test.rhai b/examples/_archive/stdout_test.rhai similarity index 100% rename from examples/stdout_test.rhai rename to examples/_archive/stdout_test.rhai diff --git a/examples/text_tools.rhai b/examples/_archive/text_tools.rhai similarity index 100% rename from examples/text_tools.rhai rename to examples/_archive/text_tools.rhai diff --git a/examples/write_read.rhai b/examples/_archive/write_read.rhai similarity index 100% rename from examples/write_read.rhai rename to examples/_archive/write_read.rhai diff --git a/examples/05_directory_operations.rhai b/examples/basics/directories.rhai similarity index 94% rename from examples/05_directory_operations.rhai rename to examples/basics/directories.rhai index 453f651..717663d 100644 --- a/examples/05_directory_operations.rhai +++ b/examples/basics/directories.rhai @@ -1,5 +1,3 @@ -// 05_directory_operations.rhai -// Demonstrates directory operations using SAL, including the new chdir function // Create a test directory structure let base_dir = "rhai_dir_test"; @@ -81,4 +79,4 @@ println(`Directory change result: ${chdir_parent_result}`); // delete(base_dir); // println("Cleanup complete"); -"Directory operations script completed successfully!" \ No newline at end of file +"Directory operations script completed successfully!" diff --git a/examples/02_file_operations.rhai b/examples/basics/files.rhai similarity index 100% rename from examples/02_file_operations.rhai rename to examples/basics/files.rhai diff --git a/examples/01_hello_world.rhai b/examples/basics/hello.rhai similarity index 100% rename from examples/01_hello_world.rhai rename to examples/basics/hello.rhai diff --git a/examples/buildah.rhai b/examples/containers/buildah.rhai similarity index 100% rename from examples/buildah.rhai rename to examples/containers/buildah.rhai diff --git a/examples/buildah_debug.rhai b/examples/containers/buildah_debug.rhai similarity index 100% rename from examples/buildah_debug.rhai rename to examples/containers/buildah_debug.rhai diff --git a/examples/nerdctl_install.rhai b/examples/containers/nerdctl_install.rhai similarity index 100% rename from examples/nerdctl_install.rhai rename to examples/containers/nerdctl_install.rhai diff --git a/examples/nerdctl_webserver.rhai b/examples/containers/nerdctl_webserver.rhai similarity index 100% rename from examples/nerdctl_webserver.rhai rename to examples/containers/nerdctl_webserver.rhai diff --git a/examples/process/kill.rhai b/examples/process/kill.rhai new file mode 100644 index 0000000..5f6fcf5 --- /dev/null +++ b/examples/process/kill.rhai @@ -0,0 +1,28 @@ +print("Caution: Use the kill() function with extreme care as it can terminate running applications."); +print("Terminating essential system processes can make your system unstable or unusable."); +print(""); + +print("This example attempts to kill processes matching a specific name."); +print("Replace 'process_name_to_kill' with the actual name of a process you intend to stop."); +print("Make sure you know what the process does before attempting to kill it."); +print(""); + +let target_process_name = "process_name_to_kill"; // <--- CHANGE THIS TO A REAL PROCESS NAME (e.g., "sleep" if you start a sleep process) + +print(`Attempting to kill processes matching pattern: '${target_process_name}'...`); + +// To safely test this, you might want to start a simple process first, like 'sleep 60 &'. +// Then replace 'process_name_to_kill' with 'sleep'. + +// Uncomment the line below to execute the kill command. +// let result_message = kill(target_process_name); // Halts on OS error during kill attempt + +// if result_message != "" { +// print(`Kill command sent. Result: ${result_message}`); +// } else { +// print("Kill command finished, but no message returned (check for errors above)."); +// } + +print(""); +print("kill() example finished (command was commented out for safety)."); +print("Uncomment the 'kill(...)' line to make it active."); \ No newline at end of file diff --git a/examples/process/process_get.rhai b/examples/process/process_get.rhai new file mode 100644 index 0000000..b57969a --- /dev/null +++ b/examples/process/process_get.rhai @@ -0,0 +1,39 @@ +print("Getting a single process using process_get()...\n"); + +// process_get expects *exactly one* process matching the pattern. +// If zero or more than one processes match, it will halt script execution. + +// Example: Get information for a specific process name. +// Replace "my_critical_service" with a name that is likely to match +// exactly one running process on your system. +// Common examples might be "Dock" or "Finder" on macOS, +// "explorer.exe" on Windows, or a specific service name on Linux. +let target_process_name = "process_name_to_get"; // <--- CHANGE THIS TO A REAL, UNIQUE PROCESS NAME + +print(`Attempting to get info for process matching pattern: '${target_process_name}'...`); + +// This line will halt if the process is not found OR if multiple processes match the name. +// It will only proceed if exactly one process is found. +let service_proc_info = process_get(target_process_name); // Halts on 0 or >1 matches, or OS error + +print(`Successfully found exactly one process matching '${target_process_name}':`); + +// Access properties of the ProcessInfo object +print(`- PID: ${service_proc_info.pid}`); +print(`- Name: ${service_proc_info.name}`); +print(`- CPU: ${service_proc_info.cpu}%`); +print(`- Memory: ${service_proc_info.memory}`); + + +// To demonstrate the halting behavior, you could uncomment one of these: + +// Example that will halt if "nonexistent_process_xyz" is not running: +// print("\nAttempting to get a nonexistent process (will halt if not found)..."); +// let nonexistent_proc = process_get("nonexistent_process_xyz"); // This line likely halts + +// Example that might halt if "sh" matches multiple processes: +// print("\nAttempting to get 'sh' (might halt if multiple shell processes exist)..."); +// let sh_proc = process_get("sh"); // This line might halt depending on your system processes + + +print("\nprocess_get() example finished (if the script did not halt above)."); \ No newline at end of file diff --git a/examples/process/process_list.rhai b/examples/process/process_list.rhai new file mode 100644 index 0000000..e603130 --- /dev/null +++ b/examples/process/process_list.rhai @@ -0,0 +1,29 @@ +print("Listing processes using process_list()...\n"); + +// Example: List all processes (use empty string as pattern) +// print("Listing all running processes (this might be a long list!)...\n"); +// let all_processes = process_list(""); +// print(`Found ${all_processes.len()} total processes.`); +// // Optional: print details for a few processes +// for i in 0..min(all_processes.len(), 5) { +// let proc = all_processes[i]; +// print(`- PID: ${proc.pid}, Name: ${proc.name}, CPU: ${proc.cpu}%, Memory: ${proc.memory}`); +// } + +print("Listing processes matching 'bash'...\n"); + +// Example: List processes matching a pattern +let pattern_to_list = "bash"; // Or another common process like "SystemSettings" or "Finder" on macOS, "explorer.exe" on Windows, "systemd" on Linux +let matching_processes = process_list(pattern_to_list); // Halts on OS error during list attempt + +if (matching_processes.len() > 0) { + print(`Found ${matching_processes.len()} processes matching '${pattern_to_list}':`); + for proc in matching_processes { + // Access properties of the ProcessInfo object + print(`- PID: ${proc.pid}, Name: ${proc.name}, CPU: ${proc.cpu}%, Memory: ${proc.memory}`); + } +} else { + print(`No processes found matching '${pattern_to_list}'.`); +} + +print("\nprocess_list() example finished."); \ No newline at end of file diff --git a/examples/process/run_all_options.rhai b/examples/process/run_all_options.rhai new file mode 100644 index 0000000..9663f5d --- /dev/null +++ b/examples/process/run_all_options.rhai @@ -0,0 +1,36 @@ +print("Running a command using multiple builder options..."); + +// Example combining log, silent, and ignore_error +// This command will: +// 1. Be logged before execution (.log()) +// 2. Have its output suppressed during execution (.silent()) +// 3. Exit with a non-zero code (fail) +// 4. NOT halt the script execution because .ignore_error() is used +let result = run("echo 'This is logged and silent stdout'; echo 'This is logged and silent stderr' >&2; exit 5") + .log() // Log the command string + .silent() // Suppress real-time output + .ignore_error() // Prevent script halt on non-zero exit code + .execute(); // Execute the command + +print("Command execution finished."); + +// Print the captured result +print(`Success: ${result.success}`); // Should be false +print(`Exit Code: ${result.code}`); // Should be 5 +print(`Captured Stdout:\n${result.stdout}`); // Should contain the stdout string + + +// The script continues execution because ignore_error() was used +print("Script continues after handling the failed command."); + +// Another example with a successful command, still silent and logged +print("\nRunning another command (successful)..."); +let success_result = run("echo 'Success message'").log().silent().execute(); +print(`Command finished.`); +print(`Success: ${success_result.success}`); // Should be true +print(`Exit Code: ${success_result.code}`); // Should be 0 +print(`Captured Stdout:\n${success_result.stdout}`); + + + +print("\nrun().execute() all options example finished."); \ No newline at end of file diff --git a/examples/process/run_basic.rhai b/examples/process/run_basic.rhai new file mode 100644 index 0000000..d0a0647 --- /dev/null +++ b/examples/process/run_basic.rhai @@ -0,0 +1,18 @@ +print("Running a basic command using run().do()..."); + +// Execute a simple command +let result = run("echo Hello from run_basic!").do(); + +// Print the command result +print(`Command: echo Hello from run_basic!`); +print(`Success: ${result.success}`); +print(`Exit Code: ${result.code}`); +print(`Stdout:\n${result.stdout}`); +print(`Stderr:\n${result.stderr}`); + +// Example of a command that might fail (if 'nonexistent_command' doesn't exist) +// This will halt execution by default because ignore_error() is not used. +// print("Running a command that will fail (and should halt)..."); +// let fail_result = run("nonexistent_command").do(); // This line will cause the script to halt if the command doesn't exist + +print("Basic run() example finished."); \ No newline at end of file diff --git a/examples/process/run_ignore_error.rhai b/examples/process/run_ignore_error.rhai new file mode 100644 index 0000000..91521a6 --- /dev/null +++ b/examples/process/run_ignore_error.rhai @@ -0,0 +1,29 @@ +print("Running a command that will fail, but ignoring the error..."); + +// Run a command that exits with a non-zero code (will fail) +// Using .ignore_error() prevents the script from halting +let result = run("exit 1").ignore_error().do(); + +print(`Command finished.`); +print(`Success: ${result.success}`); // This should be false +print(`Exit Code: ${result.code}`); // This should be 1 + +// We can now handle the failure in the script +if (!result.success) { + print("Command failed, but we handled it because ignore_error() was used."); + // Optionally print stderr if needed + // print(`Stderr:\\n${result.stderr}`); +} else { + print("Command unexpectedly succeeded."); +} + +print("\nScript continued execution after the potentially failing command."); + +// Example of a command that might fail due to OS error (e.g., command not found) +// This *might* still halt depending on how the underlying Rust function handles it, +// as ignore_error() primarily prevents halting on *command* non-zero exit codes. +// let os_error_result = run("nonexistent_command_123").ignore_error().do(); +// print(`OS Error Command Success: ${os_error_result.success}`); +// print(`OS Error Command Exit Code: ${os_error_result.code}`); + +print("ignore_error() example finished."); \ No newline at end of file diff --git a/examples/process/run_log.rhai b/examples/process/run_log.rhai new file mode 100644 index 0000000..fe36e6e --- /dev/null +++ b/examples/process/run_log.rhai @@ -0,0 +1,13 @@ +print("Running a command using run().log().do()..."); + +// The .log() method will print the command string to the console before execution. +// This is useful for debugging or tracing which commands are being run. +let result = run("echo This command is logged").log().do(); + +print(`Command finished.`); +print(`Success: ${result.success}`); +print(`Exit Code: ${result.code}`); +print(`Stdout:\n${result.stdout}`); +print(`Stderr:\n${result.stderr}`); + +print("run().log() example finished."); \ No newline at end of file diff --git a/examples/process/run_silent.rhai b/examples/process/run_silent.rhai new file mode 100644 index 0000000..275d478 --- /dev/null +++ b/examples/process/run_silent.rhai @@ -0,0 +1,22 @@ +print("Running a command using run().silent().do()...\n"); + +// This command will print to standard output and standard error +// However, because .silent() is used, the output will not appear in the console directly +let result = run("echo 'This should be silent stdout.'; echo 'This should be silent stderr.' >&2; exit 0").silent().do(); + +// The output is still captured in the CommandResult +print(`Command finished.`); +print(`Success: ${result.success}`); +print(`Exit Code: ${result.code}`); +print(`Captured Stdout:\\n${result.stdout}`); +print(`Captured Stderr:\\n${result.stderr}`); + +// Example of a silent command that fails (but won't halt because we only suppress output) +// let fail_result = run("echo 'This is silent failure stderr.' >&2; exit 1").silent().do(); +// print(`Failed command finished (silent):`); +// print(`Success: ${fail_result.success}`); +// print(`Exit Code: ${fail_result.code}`); +// print(`Captured Stdout:\\n${fail_result.stdout}`); +// print(`Captured Stderr:\\n${fail_result.stderr}`); + +print("\nrun().silent() example finished."); \ No newline at end of file diff --git a/examples/process/which.rhai b/examples/process/which.rhai new file mode 100644 index 0000000..6b3eff3 --- /dev/null +++ b/examples/process/which.rhai @@ -0,0 +1,25 @@ +print("Checking if a command exists in the system PATH using which()...\n"); + +// Check for a command that likely exists (e.g., 'node' or 'git') +let command_name_exists = "node"; +let command_path_exists = which(command_name_exists); + +if (command_path_exists != "") { + print(`'${command_name_exists}' executable found at: ${command_path_exists}`); +} else { + print(`'${command_name_exists}' executable not found in PATH.`); +} + +print("\nChecking for a command that likely does NOT exist..."); + +// Check for a command that likely does not exist +let command_name_nonexistent = "nonexistent_command_abc_123"; +let command_path_nonexistent = which(command_name_nonexistent); + +if (command_path_nonexistent != "") { + print(`'${command_name_nonexistent}' executable found at: ${command_path_nonexistent}`); +} else { + print(`'${command_name_nonexistent}' executable not found in PATH.`); +} + +print("\nwhich() example finished."); \ No newline at end of file diff --git a/examples/rhai_file_test_dir/append_file.txt b/examples/rhai_file_test_dir/append_file.txt deleted file mode 100644 index bbaa23c..0000000 --- a/examples/rhai_file_test_dir/append_file.txt +++ /dev/null @@ -1,5 +0,0 @@ -Initial content - line 1 -Initial content - line 2 -Appended content - line 3 -Appended content - line 4 -Log entry #1 at \nLog entry #2 at \nLog entry #3 at \n \ No newline at end of file diff --git a/examples/rhai_file_test_dir/test_file.txt b/examples/rhai_file_test_dir/test_file.txt deleted file mode 100644 index 4e4dcea..0000000 --- a/examples/rhai_file_test_dir/test_file.txt +++ /dev/null @@ -1,2 +0,0 @@ -This is the first line of text. -This is the second line of text. \ No newline at end of file diff --git a/rfs_implementation_plan.md b/rfs_implementation_plan.md deleted file mode 100644 index 0ce99a1..0000000 --- a/rfs_implementation_plan.md +++ /dev/null @@ -1,474 +0,0 @@ -# RFS Wrapper Implementation Plan - -## Overview - -We'll create a Rust wrapper for the RFS (Remote File System) tool that follows the builder pattern, similar to the existing implementations for buildah and nerdctl in the codebase. This wrapper will provide a fluent API for mounting, unmounting, listing mounts, configuring mount options, and packing directories into filesystem layers. - -## Module Structure - -``` -src/virt/rfs/ -├── mod.rs # Module exports and common types -├── cmd.rs # Command execution functions -├── mount.rs # Mount operations -├── pack.rs # Packing operations -├── builder.rs # Builder pattern implementation -├── types.rs # Type definitions -└── error.rs # Error handling -``` - -## Implementation Details - -### 1. Error Handling - -```rust -// error.rs -#[derive(Debug)] -pub enum RfsError { - CommandFailed(String), - InvalidArgument(String), - MountFailed(String), - UnmountFailed(String), - ListFailed(String), - PackFailed(String), - Other(String), -} - -impl std::fmt::Display for RfsError { - // Implementation -} - -impl std::error::Error for RfsError { - // Implementation -} -``` - -### 2. Command Execution - -```rust -// cmd.rs -use crate::process::{run_command, CommandResult}; -use super::error::RfsError; - -pub fn execute_rfs_command(args: &[&str]) -> Result { - // Implementation similar to buildah and nerdctl -} -``` - -### 3. Types - -```rust -// types.rs -#[derive(Debug, Clone)] -pub struct Mount { - pub id: String, - pub source: String, - pub target: String, - pub fs_type: String, - pub options: Vec, -} - -#[derive(Debug, Clone)] -pub enum MountType { - Local, - SSH, - S3, - WebDAV, - // Other mount types -} - -#[derive(Debug, Clone)] -pub struct StoreSpec { - pub spec_type: String, - pub options: std::collections::HashMap, -} - -impl StoreSpec { - pub fn new(spec_type: &str) -> Self { - Self { - spec_type: spec_type.to_string(), - options: std::collections::HashMap::new(), - } - } - - pub fn with_option(mut self, key: &str, value: &str) -> Self { - self.options.insert(key.to_string(), value.to_string()); - self - } - - pub fn to_string(&self) -> String { - let mut result = self.spec_type.clone(); - - if !self.options.is_empty() { - result.push_str(":"); - let options: Vec = self.options - .iter() - .map(|(k, v)| format!("{}={}", k, v)) - .collect(); - result.push_str(&options.join(",")); - } - - result - } -} -``` - -### 4. Builder Pattern - -```rust -// builder.rs -use std::collections::HashMap; -use super::{Mount, MountType, RfsError, execute_rfs_command, StoreSpec}; - -#[derive(Clone)] -pub struct RfsBuilder { - source: String, - target: String, - mount_type: MountType, - options: HashMap, - mount_id: Option, - debug: bool, -} - -impl RfsBuilder { - pub fn new(source: &str, target: &str, mount_type: MountType) -> Self { - Self { - source: source.to_string(), - target: target.to_string(), - mount_type, - options: HashMap::new(), - mount_id: None, - debug: false, - } - } - - pub fn with_option(mut self, key: &str, value: &str) -> Self { - self.options.insert(key.to_string(), value.to_string()); - self - } - - pub fn with_options(mut self, options: HashMap<&str, &str>) -> Self { - for (key, value) in options { - self.options.insert(key.to_string(), value.to_string()); - } - self - } - - pub fn with_debug(mut self, debug: bool) -> Self { - self.debug = debug; - self - } - - pub fn mount(self) -> Result { - // Implementation - } - - pub fn unmount(&self) -> Result<(), RfsError> { - // Implementation - } - - // Other methods -} - -// Packing functionality -pub struct PackBuilder { - directory: String, - output: String, - store_specs: Vec, - debug: bool, -} - -impl PackBuilder { - pub fn new(directory: &str, output: &str) -> Self { - Self { - directory: directory.to_string(), - output: output.to_string(), - store_specs: Vec::new(), - debug: false, - } - } - - pub fn with_store_spec(mut self, store_spec: StoreSpec) -> Self { - self.store_specs.push(store_spec); - self - } - - pub fn with_store_specs(mut self, store_specs: Vec) -> Self { - self.store_specs.extend(store_specs); - self - } - - pub fn with_debug(mut self, debug: bool) -> Self { - self.debug = debug; - self - } - - pub fn pack(self) -> Result<(), RfsError> { - // Implementation for packing a directory into a filesystem layer - let mut args = vec!["pack"]; - - // Add output file - args.push("-m"); - args.push(&self.output); - - // Add store specs - if !self.store_specs.is_empty() { - args.push("-s"); - let specs: Vec = self.store_specs - .iter() - .map(|spec| spec.to_string()) - .collect(); - args.push(&specs.join(",")); - } - - // Add directory - args.push(&self.directory); - - // Convert to string slices for the command - let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); - - // Execute the command - let result = execute_rfs_command(&args_str)?; - - // Check for errors - if !result.success { - return Err(RfsError::PackFailed(result.stderr)); - } - - Ok(()) - } -} -``` - -### 5. Mount Operations - -```rust -// mount.rs -use super::{RfsBuilder, Mount, RfsError, execute_rfs_command}; - -pub fn list_mounts() -> Result, RfsError> { - // Implementation -} - -pub fn unmount_all() -> Result<(), RfsError> { - // Implementation -} - -// Other mount-related functions -``` - -### 6. Pack Operations - -```rust -// pack.rs -use super::{PackBuilder, StoreSpec, RfsError, execute_rfs_command}; - -pub fn pack_directory(directory: &str, output: &str, store_specs: &[StoreSpec]) -> Result<(), RfsError> { - PackBuilder::new(directory, output) - .with_store_specs(store_specs.to_vec()) - .pack() -} - -// Other pack-related functions -``` - -### 7. Module Exports - -```rust -// mod.rs -mod cmd; -mod error; -mod mount; -mod pack; -mod builder; -mod types; - -pub use error::RfsError; -pub use builder::{RfsBuilder, PackBuilder}; -pub use types::{Mount, MountType, StoreSpec}; -pub use mount::{list_mounts, unmount_all}; -pub use pack::pack_directory; - -// Re-export the execute_rfs_command function for use in other modules -pub(crate) use cmd::execute_rfs_command; -``` - -## Usage Examples - -### Mounting Example - -```rust -use crate::virt::rfs::{RfsBuilder, MountType}; - -// Create a new RFS mount with builder pattern -let mount = RfsBuilder::new("user@example.com:/remote/path", "/local/mount/point", MountType::SSH) - .with_option("port", "2222") - .with_option("identity_file", "/path/to/key") - .with_debug(true) - .mount()?; - -// List all mounts -let mounts = list_mounts()?; -for mount in mounts { - println!("Mount ID: {}, Source: {}, Target: {}", mount.id, mount.source, mount.target); -} - -// Unmount -mount.unmount()?; -``` - -### Packing Example - -```rust -use crate::virt::rfs::{PackBuilder, StoreSpec}; - -// Create store specifications -let store_spec1 = StoreSpec::new("file") - .with_option("path", "/path/to/store"); - -let store_spec2 = StoreSpec::new("s3") - .with_option("bucket", "my-bucket") - .with_option("region", "us-east-1"); - -// Pack a directory with builder pattern -let result = PackBuilder::new("/path/to/directory", "output.fl") - .with_store_spec(store_spec1) - .with_store_spec(store_spec2) - .with_debug(true) - .pack()?; - -// Or use the convenience function -pack_directory("/path/to/directory", "output.fl", &[store_spec1, store_spec2])?; -``` - -## Rhai Integration - -We'll also need to create a Rhai module to expose the RFS functionality to Rhai scripts: - -```rust -// src/rhai/rfs.rs -use rhai::{Engine, EvalAltResult, RegisterFn}; -use crate::virt::rfs::{RfsBuilder, MountType, list_mounts, unmount_all, PackBuilder, StoreSpec}; - -pub fn register(engine: &mut Engine) -> Result<(), Box> { - // Register RFS functions - engine.register_fn("rfs_mount", rfs_mount); - engine.register_fn("rfs_unmount", rfs_unmount); - engine.register_fn("rfs_list_mounts", rfs_list_mounts); - engine.register_fn("rfs_unmount_all", rfs_unmount_all); - engine.register_fn("rfs_pack", rfs_pack); - - Ok(()) -} - -// Function implementations -fn rfs_mount(source: &str, target: &str, mount_type: &str, options_map: rhai::Map) -> Result<(), Box> { - // Implementation -} - -fn rfs_unmount(target: &str) -> Result<(), Box> { - // Implementation -} - -fn rfs_list_mounts() -> Result> { - // Implementation -} - -fn rfs_unmount_all() -> Result<(), Box> { - // Implementation -} - -fn rfs_pack(directory: &str, output: &str, store_specs: &str) -> Result<(), Box> { - // Implementation -} -``` - -## Implementation Flow - -Here's a diagram showing the flow of the implementation: - -```mermaid -classDiagram - class RfsBuilder { - +String source - +String target - +MountType mount_type - +HashMap options - +Option~String~ mount_id - +bool debug - +new(source, target, mount_type) - +with_option(key, value) - +with_options(options) - +with_debug(debug) - +mount() - +unmount() - } - - class PackBuilder { - +String directory - +String output - +Vec~StoreSpec~ store_specs - +bool debug - +new(directory, output) - +with_store_spec(store_spec) - +with_store_specs(store_specs) - +with_debug(debug) - +pack() - } - - class Mount { - +String id - +String source - +String target - +String fs_type - +Vec~String~ options - } - - class MountType { - <> - Local - SSH - S3 - WebDAV - } - - class StoreSpec { - +String spec_type - +HashMap options - +new(spec_type) - +with_option(key, value) - +to_string() - } - - class RfsError { - <> - CommandFailed - InvalidArgument - MountFailed - UnmountFailed - ListFailed - PackFailed - Other - } - - RfsBuilder --> Mount : creates - RfsBuilder --> RfsError : may throw - RfsBuilder --> MountType : uses - PackBuilder --> RfsError : may throw - PackBuilder --> StoreSpec : uses - Mount --> RfsError : may throw -``` - -## Implementation Steps - -1. Create the directory structure for the RFS module -2. Implement the error handling module -3. Implement the command execution module -4. Define the types for mounts, mount operations, and store specifications -5. Implement the builder pattern for RFS operations (mount and pack) -6. Implement the mount operations -7. Implement the pack operations -8. Create the module exports -9. Add Rhai integration -10. Write tests for the implementation -11. Update documentation \ No newline at end of file diff --git a/src/git_interface_redesign_plan.md b/src/git_interface_redesign_plan.md deleted file mode 100644 index a24f81e..0000000 --- a/src/git_interface_redesign_plan.md +++ /dev/null @@ -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 - +list() Result, GitError> - +find(pattern: &str) Result, GitError> - +get(path_pattern: &str) Result, GitError> - } - - class GitRepo { - +String path - +pull() Result - +reset() Result - +push() Result - +commit(message: &str) Result - +has_changes() Result - } - - 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 { - // 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, GitError> { - // List all git repositories under the base path - } - - pub fn find(&self, pattern: &str) -> Result, GitError> { - // Find repositories matching the pattern - } - - pub fn get(&self, path_pattern: &str) -> Result, 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 { - // Pull the latest changes - // Return self for chaining or an error - } - - pub fn reset(&self) -> Result { - // Reset any local changes - // Return self for chaining or an error - } - - pub fn push(&self) -> Result { - // Push changes to the remote - // Return self for chaining or an error - } - - pub fn commit(&self, message: &str) -> Result { - // Commit changes with the given message - // Return self for chaining or an error - } - - pub fn has_changes(&self) -> Result { - // 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> { - // Register the GitTree type - engine.register_type::(); - 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::(); - 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 \ No newline at end of file diff --git a/src/package_implementation_plan.md b/src/package_implementation_plan.md deleted file mode 100644 index 2dac40e..0000000 --- a/src/package_implementation_plan.md +++ /dev/null @@ -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 { - <> - Ubuntu - MacOS - Unknown - } - - class PackageManager { - <> - +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; - fn remove(&self, package: &str) -> Result; - fn update(&self) -> Result; - fn upgrade(&self) -> Result; - fn list_installed(&self) -> Result, PackageError>; - fn search(&self, query: &str) -> Result, PackageError>; - fn is_installed(&self, package: &str) -> Result; -} -``` - -### 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 { - execute_package_command(&["apt-get", "install", "-y", package], self.debug) - } - - fn remove(&self, package: &str) -> Result { - execute_package_command(&["apt-get", "remove", "-y", package], self.debug) - } - - fn update(&self) -> Result { - execute_package_command(&["apt-get", "update", "-y"], self.debug) - } - - fn upgrade(&self) -> Result { - execute_package_command(&["apt-get", "upgrade", "-y"], self.debug) - } - - fn list_installed(&self) -> Result, 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, 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 { - 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 { - execute_package_command(&["brew", "install", package], self.debug) - } - - fn remove(&self, package: &str) -> Result { - execute_package_command(&["brew", "uninstall", package], self.debug) - } - - fn update(&self) -> Result { - execute_package_command(&["brew", "update"], self.debug) - } - - fn upgrade(&self) -> Result { - execute_package_command(&["brew", "upgrade"], self.debug) - } - - fn list_installed(&self) -> Result, 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, 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 { - 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 = 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 { - // 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, 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 { - let pm = self.get_package_manager()?; - pm.install(package) - } - - pub fn remove(&self, package: &str) -> Result { - let pm = self.get_package_manager()?; - pm.remove(package) - } - - pub fn update(&self) -> Result { - let pm = self.get_package_manager()?; - pm.update() - } - - pub fn upgrade(&self) -> Result { - let pm = self.get_package_manager()?; - pm.upgrade() - } - - pub fn list_installed(&self) -> Result, PackageError> { - let pm = self.get_package_manager()?; - pm.list_installed() - } - - pub fn search(&self, query: &str) -> Result, PackageError> { - let pm = self.get_package_manager()?; - pm.search(query) - } - - pub fn is_installed(&self, package: &str) -> Result { - 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> { - 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> { - 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> { - 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> { - 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> { - let hero = os::package::PackHero::new(); - let packages = hero.list_installed().to_rhai_error()?; - - // Convert Vec 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> { - let hero = os::package::PackHero::new(); - let packages = hero.search(query).to_rhai_error()?; - - // Convert Vec 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> { - 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 \ No newline at end of file diff --git a/src/rhai/mod.rs b/src/rhai/mod.rs index 0e5a6c7..d63565e 100644 --- a/src/rhai/mod.rs +++ b/src/rhai/mod.rs @@ -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 }; diff --git a/src/rhai/process.rs b/src/rhai/process.rs index eec001a..efb60de 100644 --- a/src/rhai/process.rs +++ b/src/rhai/process.rs @@ -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>` - Ok if registration was successful, Err otherwise pub fn register_process_module(engine: &mut Engine) -> Result<(), Box> { // 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> { - // Register CommandResult type and methods + // Register CommandResult type and its methods engine.register_type_with_name::("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"); - - // 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::("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(result: Result) -> Result 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> { + 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(result: Result) -> Result> { result.map_err(|e| { Box::new(EvalAltResult::ErrorRuntime( @@ -75,78 +119,6 @@ fn process_error_to_rhai_error(result: Result) -> Result 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> { - 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> { - 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> { - 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 // diff --git a/src/rhai_text_implementation_plan.md b/src/rhai_text_implementation_plan.md deleted file mode 100644 index c2f8061..0000000 --- a/src/rhai_text_implementation_plan.md +++ /dev/null @@ -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" \ No newline at end of file diff --git a/src/run_git_test.rs b/src/run_git_test.rs deleted file mode 100644 index 6af1caf..0000000 --- a/src/run_git_test.rs +++ /dev/null @@ -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> { - // 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(()) -} \ No newline at end of file diff --git a/src/text/replace.rs b/src/text/replace.rs index 5371719..be8b0ce 100644 --- a/src/text/replace.rs +++ b/src/text/replace.rs @@ -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.