246 lines
12 KiB
Markdown
246 lines
12 KiB
Markdown
# SAL OS Module (`sal::os`)
|
|
|
|
The `sal::os` module provides a comprehensive suite of operating system interaction utilities. It aims to offer a cross-platform abstraction layer for common OS-level tasks, simplifying system programming in Rust.
|
|
|
|
This module is composed of three main sub-modules:
|
|
- [`fs`](#fs): File system operations.
|
|
- [`download`](#download): File downloading and basic installation.
|
|
- [`package`](#package): System package management.
|
|
|
|
## Key Design Points
|
|
|
|
The `sal::os` module is engineered with several core principles to provide a robust and developer-friendly interface for OS interactions:
|
|
|
|
- **Cross-Platform Abstraction**: A primary goal is to offer a unified API for common OS tasks, smoothing over differences between operating systems (primarily Linux and macOS). While it strives for abstraction, it leverages platform-specific tools (e.g., `rsync` on Linux, `robocopy` on Windows for `fs::copy` or `fs::rsync`; `apt` on Debian-based systems, `brew` on macOS for `package` management) for optimal performance and behavior when necessary.
|
|
- **Modular Structure**: Functionality is organized into logical sub-modules:
|
|
- `fs`: For comprehensive file and directory manipulation.
|
|
- `download`: For retrieving files from URLs, with support for extraction and basic installation.
|
|
- `package`: For interacting with system package managers.
|
|
- **Granular Error Handling**: Each sub-module features custom error enums (`FsError`, `DownloadError`, `PackageError`) to provide specific and actionable feedback, aiding in debugging and robust error management.
|
|
- **Sensible Defaults and Defensive Operations**: Many functions are designed to be "defensive," e.g., `mkdir` creates parent directories if they don't exist and doesn't fail if the directory already exists. `delete` doesn't error if the target is already gone.
|
|
- **Facade for Simplicity**: The `package` sub-module uses a `PackHero` facade to provide a simple entry point for common package operations, automatically detecting the underlying OS and package manager.
|
|
- **Rhai Scriptability**: A significant portion of the `sal::os` module's functionality is exposed to Rhai scripts via `herodo`, enabling powerful automation of OS-level tasks.
|
|
|
|
## `fs` - File System Operations
|
|
|
|
The `fs` sub-module (`sal::os::fs`) offers a robust set of functions for interacting with the file system.
|
|
|
|
**Key Features:**
|
|
|
|
* **Error Handling**: A custom `FsError` enum for detailed error reporting on file system operations.
|
|
* **File Operations**:
|
|
* `copy(src, dest)`: Copies files and directories, with support for wildcards and recursive copying. Uses platform-specific commands (`cp -R`, `robocopy /MIR`).
|
|
* `exist(path)`: Checks if a file or directory exists.
|
|
* `find_file(dir, filename_pattern)`: Finds a single file in a directory, supporting wildcards.
|
|
* `find_files(dir, filename_pattern)`: Finds multiple files in a directory, supporting wildcards.
|
|
* `file_size(path)`: Returns the size of a file in bytes.
|
|
* `file_read(path)`: Reads the entire content of a file into a string.
|
|
* `file_write(path, content)`: Writes content to a file, overwriting if it exists, and creating parent directories if needed.
|
|
* `file_write_append(path, content)`: Appends content to a file, creating it and parent directories if needed.
|
|
* **Directory Operations**:
|
|
* `find_dir(parent_dir, dirname_pattern)`: Finds a single directory within a parent directory, supporting wildcards.
|
|
* `find_dirs(parent_dir, dirname_pattern)`: Finds multiple directories recursively within a parent directory, supporting wildcards.
|
|
* `delete(path)`: Deletes files or directories.
|
|
* `mkdir(path)`: Creates a directory, including parent directories if necessary.
|
|
* `rsync(src, dest)`: Synchronizes directories using platform-specific commands (`rsync -a --delete`, `robocopy /MIR`).
|
|
* `chdir(path)`: Changes the current working directory.
|
|
* **Path Operations**:
|
|
* `mv(src, dest)`: Moves or renames files and directories. Handles cross-device moves by falling back to copy-then-delete.
|
|
* **Command Utilities**:
|
|
* `which(command_name)`: Checks if a command exists in the system's PATH and returns its path.
|
|
|
|
**Usage Example (fs):**
|
|
|
|
```rust
|
|
use sal::os::fs;
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
if !fs::exist("my_dir") {
|
|
fs::mkdir("my_dir")?;
|
|
println!("Created directory 'my_dir'");
|
|
}
|
|
|
|
fs::file_write("my_dir/example.txt", "Hello from SAL!")?;
|
|
let content = fs::file_read("my_dir/example.txt")?;
|
|
println!("File content: {}", content);
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
## `download` - File Downloading and Installation
|
|
|
|
The `download` sub-module (`sal::os::download`) provides utilities for downloading files from URLs and performing basic installation tasks.
|
|
|
|
**Key Features:**
|
|
|
|
* **Error Handling**: A custom `DownloadError` enum for download-specific errors.
|
|
* **File Downloading**:
|
|
* `download(url, dest_dir, min_size_kb)`: Downloads a file to a specified directory.
|
|
* Uses `curl` with progress display.
|
|
* Supports minimum file size checks.
|
|
* Automatically extracts common archive formats (`.tar.gz`, `.tgz`, `.tar`, `.zip`) into `dest_dir`.
|
|
* `download_file(url, dest_file_path, min_size_kb)`: Downloads a file to a specific file path without automatic extraction.
|
|
* **File Permissions**:
|
|
* `chmod_exec(path)`: Makes a file executable (equivalent to `chmod +x` on Unix-like systems).
|
|
* **Download and Install**:
|
|
* `download_install(url, min_size_kb)`: Downloads a file (to `/tmp/`) and attempts to install it if it's a supported package format.
|
|
* Currently supports `.deb` packages on Debian-based systems.
|
|
* For `.deb` files, it uses `sudo dpkg --install` and attempts `sudo apt-get install -f -y` to fix dependencies if needed.
|
|
* Handles archives by extracting them to `/tmp/` first.
|
|
|
|
**Usage Example (download):**
|
|
|
|
```rust
|
|
use sal::os::download;
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let archive_url = "https://example.com/my_archive.tar.gz";
|
|
let output_dir = "/tmp/my_app";
|
|
|
|
// Download and extract an archive
|
|
let extracted_path = download::download(archive_url, output_dir, 1024)?; // Min 1MB
|
|
println!("Archive extracted to: {}", extracted_path);
|
|
|
|
// Download a script and make it executable
|
|
let script_url = "https://example.com/my_script.sh";
|
|
let script_path = "/tmp/my_script.sh";
|
|
download::download_file(script_url, script_path, 0)?;
|
|
download::chmod_exec(script_path)?;
|
|
println!("Script downloaded and made executable at: {}", script_path);
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
## `package` - System Package Management
|
|
|
|
The `package` sub-module (`sal::os::package`) offers an abstraction layer for interacting with system package managers like APT (for Debian/Ubuntu) and Homebrew (for macOS).
|
|
|
|
**Key Features:**
|
|
|
|
* **Error Handling**: A custom `PackageError` enum.
|
|
* **Platform Detection**: Identifies the current OS (Ubuntu, macOS, or Unknown) to use the appropriate package manager.
|
|
* **`PackageManager` Trait**: Defines a common interface for package operations:
|
|
* `install(package_name)`
|
|
* `remove(package_name)`
|
|
* `update()` (updates package lists)
|
|
* `upgrade()` (upgrades all installed packages)
|
|
* `list_installed()`
|
|
* `search(query)`
|
|
* `is_installed(package_name)`
|
|
* **Implementations**:
|
|
* `AptPackageManager`: For Debian/Ubuntu systems (uses `apt-get`, `dpkg`).
|
|
* `BrewPackageManager`: For macOS systems (uses `brew`).
|
|
* **`PackHero` Facade**: A simple entry point to access package management functions in a platform-agnostic way.
|
|
* `PackHero::new().install("nginx")?`
|
|
|
|
**Usage Example (package):**
|
|
|
|
```rust
|
|
use sal::os::package::PackHero;
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let pack_hero = PackHero::new();
|
|
|
|
// Check if a package is installed
|
|
if !pack_hero.is_installed("htop")? {
|
|
println!("htop is not installed. Attempting to install...");
|
|
pack_hero.install("htop")?;
|
|
println!("htop installed successfully.");
|
|
} else {
|
|
println!("htop is already installed.");
|
|
}
|
|
|
|
// Update package lists
|
|
println!("Updating package lists...");
|
|
pack_hero.update()?;
|
|
println!("Package lists updated.");
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
## Rhai Scripting with `herodo`
|
|
|
|
The `sal::os` module is extensively scriptable via `herodo`, allowing for automation of various operating system tasks directly from Rhai scripts. The `sal::rhai::os` module registers the necessary functions.
|
|
|
|
### File System (`fs`) Functions
|
|
|
|
- `copy(src: String, dest: String) -> String`: Copies files/directories (supports wildcards).
|
|
- `exist(path: String) -> bool`: Checks if a file or directory exists.
|
|
- `find_file(dir: String, filename_pattern: String) -> String`: Finds a single file in `dir` matching `filename_pattern`.
|
|
- `find_files(dir: String, filename_pattern: String) -> Array`: Finds multiple files in `dir` (recursive).
|
|
- `find_dir(parent_dir: String, dirname_pattern: String) -> String`: Finds a single directory in `parent_dir`.
|
|
- `find_dirs(parent_dir: String, dirname_pattern: String) -> Array`: Finds multiple directories in `parent_dir` (recursive).
|
|
- `delete(path: String) -> String`: Deletes a file or directory.
|
|
- `mkdir(path: String) -> String`: Creates a directory (and parents if needed).
|
|
- `file_size(path: String) -> Int`: Returns file size in bytes.
|
|
- `rsync(src: String, dest: String) -> String`: Synchronizes directories.
|
|
- `chdir(path: String) -> String`: Changes the current working directory.
|
|
- `file_read(path: String) -> String`: Reads entire file content.
|
|
- `file_write(path: String, content: String) -> String`: Writes content to a file (overwrites).
|
|
- `file_write_append(path: String, content: String) -> String`: Appends content to a file.
|
|
- `mv(src: String, dest: String) -> String`: Moves/renames a file or directory.
|
|
- `which(command_name: String) -> String`: Checks if a command exists in PATH and returns its path.
|
|
- `cmd_ensure_exists(commands: String) -> String`: Ensures one or more commands (comma-separated) exist in PATH; throws an error if any are missing.
|
|
|
|
### Download Functions
|
|
|
|
- `download(url: String, dest_dir: String, min_size_kb: Int) -> String`: Downloads from `url` to `dest_dir`, extracts common archives.
|
|
- `download_file(url: String, dest_file_path: String, min_size_kb: Int) -> String`: Downloads from `url` to `dest_file_path` (no extraction).
|
|
- `download_install(url: String, min_size_kb: Int) -> String`: Downloads and attempts to install (e.g., `.deb` packages).
|
|
- `chmod_exec(path: String) -> String`: Makes a file executable (`chmod +x`).
|
|
|
|
### Package Management Functions
|
|
|
|
- `package_install(package_name: String) -> String`: Installs a package.
|
|
- `package_remove(package_name: String) -> String`: Removes a package.
|
|
- `package_update() -> String`: Updates package lists.
|
|
- `package_upgrade() -> String`: Upgrades all installed packages.
|
|
- `package_list() -> Array`: Lists all installed packages.
|
|
- `package_search(query: String) -> Array`: Searches for packages.
|
|
- `package_is_installed(package_name: String) -> bool`: Checks if a package is installed.
|
|
- `package_set_debug(debug: bool) -> bool`: Enables/disables debug logging for package operations.
|
|
- `package_platform() -> String`: Returns the detected package platform (e.g., "Ubuntu", "MacOS").
|
|
|
|
### Rhai Example
|
|
|
|
```rhai
|
|
// File system operations
|
|
let test_dir = "/tmp/sal_os_rhai_test";
|
|
if exist(test_dir) {
|
|
delete(test_dir);
|
|
}
|
|
mkdir(test_dir);
|
|
print(`Created directory: ${test_dir}`);
|
|
|
|
file_write(`${test_dir}/message.txt`, "Hello from Rhai OS module!");
|
|
let content = file_read(`${test_dir}/message.txt`);
|
|
print(`File content: ${content}`);
|
|
|
|
// Download operation (example URL, may not be active)
|
|
// let script_url = "https://raw.githubusercontent.com/someuser/somescript/main/script.sh";
|
|
// let script_path = `${test_dir}/downloaded_script.sh`;
|
|
// try {
|
|
// download_file(script_url, script_path, 0);
|
|
// chmod_exec(script_path);
|
|
// print(`Downloaded and made executable: ${script_path}`);
|
|
// } catch (e) {
|
|
// print(`Download example failed (this is okay for a test): ${e}`);
|
|
// }
|
|
|
|
// Package management (illustrative, requires sudo for install/remove/update)
|
|
print(`Package platform: ${package_platform()}`);
|
|
if !package_is_installed("htop") {
|
|
print("htop is not installed.");
|
|
// package_install("htop"); // Would require sudo
|
|
} else {
|
|
print("htop is already installed.");
|
|
}
|
|
|
|
print("OS module Rhai script finished.");
|
|
```
|
|
|
|
This module provides a powerful and convenient way to handle common OS-level tasks within your Rust applications.
|