This commit is contained in:
despiegk 2025-04-05 11:21:58 +02:00
parent 88e4a2a4b1
commit e9a73327e3
5 changed files with 259 additions and 7 deletions

237
src/docs/docs/sal/text.md Normal file
View File

@ -0,0 +1,237 @@
# Text Manipulation Tools
The SAL text module provides powerful text manipulation capabilities that can be used from Rhai scripts. These include text replacement (with regex support), template rendering, string normalization, and text formatting utilities.
## Table of Contents
- [Text Replacement](#text-replacement)
- [Template Rendering](#template-rendering)
- [String Normalization](#string-normalization)
- [Text Formatting](#text-formatting)
## Text Replacement
The text replacement tools allow you to perform simple or complex text replacements, with support for regular expressions, case-insensitive matching, and file operations.
### Basic Usage
```rhai
// Create a new text replacer
let replacer = text_replacer_new()
.pattern("foo") // Set the pattern to search for
.replacement("bar") // Set the replacement text
.build(); // Build the replacer
// Apply the replacer to a string
let result = replacer.replace("foo bar foo");
// Result: "bar bar bar"
```
### Advanced Features
#### Regular Expressions
```rhai
// Create a replacer with regex support
let replacer = text_replacer_new()
.pattern("\\bfoo\\b") // Use regex pattern (word boundary)
.replacement("bar")
.regex(true) // Enable regex mode
.build();
// Apply the replacer to a string
let result = replacer.replace("foo foobar");
// Result: "bar foobar" (only replaces whole "foo" words)
```
#### Case-Insensitive Matching
```rhai
// Create a replacer with case-insensitive matching
let replacer = text_replacer_new()
.pattern("foo")
.replacement("bar")
.regex(true)
.case_insensitive(true) // Enable case-insensitive matching
.build();
// Apply the replacer to a string
let result = replacer.replace("FOO foo Foo");
// Result: "bar bar bar"
```
#### Multiple Replacements
```rhai
// Chain multiple replacements
let replacer = text_replacer_new()
.pattern("foo")
.replacement("bar")
.and() // Add another replacement operation
.pattern("baz")
.replacement("qux")
.build();
// Apply the replacer to a string
let result = replacer.replace("foo baz");
// Result: "bar qux"
```
#### File Operations
```rhai
// Create a replacer
let replacer = text_replacer_new()
.pattern("foo")
.replacement("bar")
.build();
// Replace in a file and get the result as a string
let result = replacer.replace_file("input.txt");
// Replace in a file and write back to the same file
replacer.replace_file_in_place("input.txt");
// Replace in a file and write to a new file
replacer.replace_file_to("input.txt", "output.txt");
```
## Template Rendering
The template rendering tools allow you to create and render templates with variables, using the powerful Tera template engine.
### Basic Usage
```rhai
// Create a template builder with a template file
let template = template_builder_open("template.txt")
.add_var("name", "John") // Add a string variable
.add_var("age", 30) // Add a numeric variable
.add_var("items", ["a", "b", "c"]); // Add an array variable
// Render the template
let result = template.render();
// Render to a file
template.render_to_file("output.txt");
```
### Template Variables
You can add variables of various types:
```rhai
let template = template_builder_open("template.txt")
.add_var("name", "John") // String
.add_var("age", 30) // Integer
.add_var("height", 1.85) // Float
.add_var("is_active", true) // Boolean
.add_var("items", ["a", "b", "c"]); // Array
```
### Using Map for Variables
```rhai
// Create a map of variables
let vars = #{
name: "Alice",
place: "Wonderland"
};
// Add all variables from the map
let template = template_builder_open("template.txt")
.add_vars(vars);
```
### Template Syntax
The template engine uses Tera, which supports:
- Variable interpolation: `{{ variable }}`
- Conditionals: `{% if condition %}...{% endif %}`
- Loops: `{% for item in items %}...{% endfor %}`
- Filters: `{{ variable | filter }}`
Example template:
```
Hello, {{ name }}!
{% if show_greeting %}
Welcome to {{ place }}.
{% endif %}
Your items:
{% for item in items %}
- {{ item }}{% if not loop.last %}{% endif %}
{% endfor %}
```
## String Normalization
The string normalization tools help convert strings to consistent formats for use as file names or paths.
### name_fix
Converts a string to a safe, normalized name by:
- Converting to lowercase
- Replacing spaces and special characters with underscores
- Removing non-alphanumeric characters
```rhai
let fixed_name = name_fix("Hello World!");
// Result: "hello_world"
let fixed_name = name_fix("File-Name.txt");
// Result: "file_name.txt"
```
### path_fix
Similar to name_fix, but preserves path separators:
```rhai
let fixed_path = path_fix("/path/to/Hello World!");
// Result: "/path/to/hello_world"
let fixed_path = path_fix("./relative/path/to/DOCUMENT-123.pdf");
// Result: "./relative/path/to/document_123.pdf"
```
## Text Formatting
Tools to help with text formatting and indentation.
### dedent
Removes common leading whitespace from multi-line strings:
```rhai
let indented_text = " line 1
line 2
line 3";
let dedented = dedent(indented_text);
// Result: "line 1
// line 2
// line 3"
```
### prefix
Adds a prefix to every line in a multi-line string:
```rhai
let text = "line 1
line 2
line 3";
let prefixed = prefix(text, " ");
// Result: " line 1
// line 2
// line 3"
```
## Examples
See the [text_tools.rhai](https://github.com/ourworld-tf/herocode/blob/main/sal/src/rhaiexamples/text_tools.rhai) example script for more detailed examples of using these text manipulation tools.

View File

@ -56,8 +56,8 @@ fn register_bah_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
engine.register_get("container_id", get_builder_container_id); engine.register_get("container_id", get_builder_container_id);
engine.register_get("name", get_builder_name); engine.register_get("name", get_builder_name);
engine.register_get("image", get_builder_image); engine.register_get("image", get_builder_image);
engine.register_get("debug", get_builder_debug); engine.register_get("debug_mode", get_builder_debug);
engine.register_set("debug", set_builder_debug); engine.register_set("debug_mode", set_builder_debug);
// Register Image type and methods (same as before) // Register Image type and methods (same as before)
engine.register_type_with_name::<Image>("BuildahImage"); engine.register_type_with_name::<Image>("BuildahImage");

View File

@ -14,6 +14,9 @@ println(`Creating container '${container_name}' from base image '${base_image}'.
// Create a new buildah container using the builder pattern // Create a new buildah container using the builder pattern
let builder = bah_new(container_name, base_image); let builder = bah_new(container_name, base_image);
println("Enabling debug mode...");
builder.debug_mode = true;
// Update package lists and install golang and nginx // Update package lists and install golang and nginx
println("Installing packages (this may take a while)..."); println("Installing packages (this may take a while)...");

View File

@ -14,7 +14,7 @@ let builder = bah_new(container_name, base_image);
// Enable debug mode // Enable debug mode
println("Enabling debug mode..."); println("Enabling debug mode...");
builder.debug = true; builder.debug_mode = true;
// Run a simple command to see debug output // Run a simple command to see debug output
println("Running a command with debug enabled..."); println("Running a command with debug enabled...");
@ -22,7 +22,7 @@ let result = builder.run("echo 'Hello from debug mode'");
// Disable debug mode // Disable debug mode
println("Disabling debug mode..."); println("Disabling debug mode...");
builder.debug = false; builder.debug_mode = false;
// Run another command without debug // Run another command without debug
println("Running a command with debug disabled..."); println("Running a command with debug disabled...");
@ -30,7 +30,7 @@ let result2 = builder.run("echo 'Hello without debug'");
// Enable debug mode again // Enable debug mode again
println("Enabling debug mode again..."); println("Enabling debug mode again...");
builder.debug = true; builder.debug_mode = true;
// Remove the container with debug enabled // Remove the container with debug enabled
println("Removing the container with debug enabled..."); println("Removing the container with debug enabled...");

View File

@ -34,7 +34,8 @@ impl ContentOperations {
// Copy the temporary file to the container // Copy the temporary file to the container
let temp_path = temp_file.path().to_string_lossy().to_string(); let temp_path = temp_file.path().to_string_lossy().to_string();
execute_buildah_command(&["copy", container_id, &temp_path, dest_path]) // Use add instead of copy for better handling of paths
execute_buildah_command(&["add", container_id, &temp_path, dest_path])
} }
/// Read content from a file in the container /// Read content from a file in the container
@ -55,7 +56,18 @@ impl ContentOperations {
let temp_path = temp_file.path().to_string_lossy().to_string(); let temp_path = temp_file.path().to_string_lossy().to_string();
// Copy the file from the container to the temporary file // Copy the file from the container to the temporary file
execute_buildah_command(&["copy", container_id, source_path, &temp_path])?; // Use mount to access the container's filesystem
let mount_result = execute_buildah_command(&["mount", container_id])?;
let mount_point = mount_result.stdout.trim();
// Construct the full path to the file in the container
let full_source_path = format!("{}{}", mount_point, source_path);
// Copy the file from the mounted container to the temporary file
execute_buildah_command(&["copy", container_id, &full_source_path, &temp_path])?;
// Unmount the container
execute_buildah_command(&["umount", container_id])?;
// Read the content from the temporary file // Read the content from the temporary file
let mut file = File::open(temp_file.path()) let mut file = File::open(temp_file.path())