This commit is contained in:
despiegk 2025-04-05 11:03:58 +02:00
parent 4be9445702
commit fe7a676cac
11 changed files with 1145 additions and 61 deletions

View File

@ -0,0 +1,207 @@
# 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<bool> = 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<CommandResult, BuildahError> {
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<CommandResult, BuildahError> {
// 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<CommandResult, BuildahError> {
// 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<Vec<Image>, 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<Vec<Image>, 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<Vec<Image>, 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

View File

@ -56,6 +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_set("debug", 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");
@ -217,6 +219,16 @@ pub fn get_builder_image(builder: &mut Builder) -> String {
builder.image().to_string() builder.image().to_string()
} }
/// Get the debug flag from a Builder
pub fn get_builder_debug(builder: &mut Builder) -> bool {
builder.debug()
}
/// Set the debug flag on a Builder
pub fn set_builder_debug(builder: &mut Builder, debug: bool) {
builder.set_debug(debug);
}
// Reset function for Builder // Reset function for Builder
pub fn builder_reset(builder: &mut Builder) -> Result<(), Box<EvalAltResult>> { pub fn builder_reset(builder: &mut Builder) -> Result<(), Box<EvalAltResult>> {
bah_error_to_rhai_error(builder.reset()) bah_error_to_rhai_error(builder.reset())

View File

@ -9,6 +9,7 @@ mod process;
mod buildah; mod buildah;
mod nerdctl; mod nerdctl;
mod git; mod git;
mod text;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -56,6 +57,16 @@ pub use nerdctl::{
pub use git::register_git_module; pub use git::register_git_module;
pub use crate::git::{GitTree, GitRepo}; pub use crate::git::{GitTree, GitRepo};
// Re-export text module
pub use text::register_text_module;
// Re-export text functions directly from text module
pub use crate::text::{
// Fix functions
name_fix, path_fix,
// Dedent functions
dedent, prefix
};
// Rename copy functions to avoid conflicts // Rename copy functions to avoid conflicts
pub use os::copy as os_copy; pub use os::copy as os_copy;
@ -89,11 +100,14 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
// Register Nerdctl module functions // Register Nerdctl module functions
nerdctl::register_nerdctl_module(engine)?; nerdctl::register_nerdctl_module(engine)?;
// Register Git module functions // Register Git module functions
git::register_git_module(engine)?; git::register_git_module(engine)?;
// Register Text module functions
text::register_text_module(engine)?;
// Future modules can be registered here // Future modules can be registered here
Ok(()) Ok(())
} }

229
src/rhai/text.rs Normal file
View File

@ -0,0 +1,229 @@
//! Rhai wrappers for Text module functions
//!
//! This module provides Rhai wrappers for the functions in the Text module.
use rhai::{Engine, EvalAltResult, Array, Dynamic, Map, Position};
use std::collections::HashMap;
use crate::text::{
TextReplacer, TextReplacerBuilder,
TemplateBuilder,
dedent, prefix,
name_fix, path_fix
};
/// Register Text module functions with the Rhai engine
///
/// # Arguments
///
/// * `engine` - The Rhai engine to register the functions with
///
/// # Returns
///
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
pub fn register_text_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register types
register_text_types(engine)?;
// Register TextReplacer functions
engine.register_fn("text_replacer_new", text_replacer_new);
// Register TextReplacer instance methods
engine.register_fn("pattern", builder_pattern);
engine.register_fn("replacement", builder_replacement);
engine.register_fn("regex", builder_regex);
engine.register_fn("case_insensitive", builder_case_insensitive);
engine.register_fn("and", builder_and);
engine.register_fn("build", builder_build);
engine.register_fn("replace", replacer_replace);
engine.register_fn("replace_file", replacer_replace_file);
engine.register_fn("replace_file_in_place", replacer_replace_file_in_place);
engine.register_fn("replace_file_to", replacer_replace_file_to);
// Register TemplateBuilder functions
engine.register_fn("template_builder_open", template_builder_open);
// Register TemplateBuilder instance methods
engine.register_fn("add_var", template_add_var_string);
engine.register_fn("add_var", template_add_var_int);
engine.register_fn("add_var", template_add_var_float);
engine.register_fn("add_var", template_add_var_bool);
engine.register_fn("add_var", template_add_var_array);
engine.register_fn("add_vars", template_add_vars);
engine.register_fn("render", template_render);
engine.register_fn("render_to_file", template_render_to_file);
// Register Fix functions
engine.register_fn("name_fix", crate::text::name_fix);
engine.register_fn("path_fix", crate::text::path_fix);
// Register Dedent functions
engine.register_fn("dedent", crate::text::dedent);
engine.register_fn("prefix", crate::text::prefix);
Ok(())
}
/// Register Text module types with the Rhai engine
fn register_text_types(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register TextReplacerBuilder type
engine.register_type_with_name::<TextReplacerBuilder>("TextReplacerBuilder");
// Register TextReplacer type
engine.register_type_with_name::<TextReplacer>("TextReplacer");
// Register TemplateBuilder type
engine.register_type_with_name::<TemplateBuilder>("TemplateBuilder");
// Register getters for TextReplacer properties (if any)
// Register getters for TemplateBuilder properties (if any)
Ok(())
}
// Helper functions for error conversion
fn io_error_to_rhai_error<T>(result: std::io::Result<T>) -> Result<T, Box<EvalAltResult>> {
result.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("IO error: {}", e).into(),
Position::NONE
))
})
}
fn tera_error_to_rhai_error<T>(result: Result<T, tera::Error>) -> Result<T, Box<EvalAltResult>> {
result.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
format!("Template error: {}", e).into(),
Position::NONE
))
})
}
fn string_error_to_rhai_error<T>(result: Result<T, String>) -> Result<T, Box<EvalAltResult>> {
result.map_err(|e| {
Box::new(EvalAltResult::ErrorRuntime(
e.into(),
Position::NONE
))
})
}
// TextReplacer implementation
/// Creates a new TextReplacerBuilder
pub fn text_replacer_new() -> TextReplacerBuilder {
TextReplacerBuilder::default()
}
/// Sets the pattern to search for
pub fn builder_pattern(builder: TextReplacerBuilder, pat: &str) -> TextReplacerBuilder {
builder.pattern(pat)
}
/// Sets the replacement text
pub fn builder_replacement(builder: TextReplacerBuilder, rep: &str) -> TextReplacerBuilder {
builder.replacement(rep)
}
/// Sets whether to use regex
pub fn builder_regex(builder: TextReplacerBuilder, yes: bool) -> TextReplacerBuilder {
builder.regex(yes)
}
/// Sets whether the replacement should be case-insensitive
pub fn builder_case_insensitive(builder: TextReplacerBuilder, yes: bool) -> TextReplacerBuilder {
builder.case_insensitive(yes)
}
/// Adds another replacement operation to the chain and resets the builder for a new operation
pub fn builder_and(builder: TextReplacerBuilder) -> TextReplacerBuilder {
builder.and()
}
/// Builds the TextReplacer with all configured replacement operations
pub fn builder_build(builder: TextReplacerBuilder) -> Result<TextReplacer, Box<EvalAltResult>> {
string_error_to_rhai_error(builder.build())
}
/// Applies all configured replacement operations to the input text
pub fn replacer_replace(replacer: &mut TextReplacer, input: &str) -> String {
replacer.replace(input)
}
/// Reads a file, applies all replacements, and returns the result as a string
pub fn replacer_replace_file(replacer: &mut TextReplacer, path: &str) -> Result<String, Box<EvalAltResult>> {
io_error_to_rhai_error(replacer.replace_file(path))
}
/// Reads a file, applies all replacements, and writes the result back to the file
pub fn replacer_replace_file_in_place(replacer: &mut TextReplacer, path: &str) -> Result<(), Box<EvalAltResult>> {
io_error_to_rhai_error(replacer.replace_file_in_place(path))
}
/// Reads a file, applies all replacements, and writes the result to a new file
pub fn replacer_replace_file_to(replacer: &mut TextReplacer, input_path: &str, output_path: &str) -> Result<(), Box<EvalAltResult>> {
io_error_to_rhai_error(replacer.replace_file_to(input_path, output_path))
}
// TemplateBuilder implementation
/// Creates a new TemplateBuilder with the specified template path
pub fn template_builder_open(template_path: &str) -> Result<TemplateBuilder, Box<EvalAltResult>> {
io_error_to_rhai_error(TemplateBuilder::open(template_path))
}
/// Adds a string variable to the template context
pub fn template_add_var_string(builder: TemplateBuilder, name: &str, value: &str) -> TemplateBuilder {
builder.add_var(name, value)
}
/// Adds an integer variable to the template context
pub fn template_add_var_int(builder: TemplateBuilder, name: &str, value: i64) -> TemplateBuilder {
builder.add_var(name, value)
}
/// Adds a float variable to the template context
pub fn template_add_var_float(builder: TemplateBuilder, name: &str, value: f64) -> TemplateBuilder {
builder.add_var(name, value)
}
/// Adds a boolean variable to the template context
pub fn template_add_var_bool(builder: TemplateBuilder, name: &str, value: bool) -> TemplateBuilder {
builder.add_var(name, value)
}
/// Adds an array variable to the template context
pub fn template_add_var_array(builder: TemplateBuilder, name: &str, value: Array) -> TemplateBuilder {
// Convert Rhai Array to Vec<String>
let vec: Vec<String> = value.iter()
.filter_map(|v| v.clone().into_string().ok())
.collect();
builder.add_var(name, vec)
}
/// Adds multiple variables to the template context from a Map
pub fn template_add_vars(builder: TemplateBuilder, vars: Map) -> TemplateBuilder {
// Convert Rhai Map to Rust HashMap
let mut hash_map = HashMap::new();
for (key, value) in vars.iter() {
if let Ok(val_str) = value.clone().into_string() {
hash_map.insert(key.to_string(), val_str);
}
}
// Add the variables
builder.add_vars(hash_map)
}
/// Renders the template with the current context
pub fn template_render(builder: &mut TemplateBuilder) -> Result<String, Box<EvalAltResult>> {
tera_error_to_rhai_error(builder.render())
}
/// Renders the template and writes the result to a file
pub fn template_render_to_file(builder: &mut TemplateBuilder, output_path: &str) -> Result<(), Box<EvalAltResult>> {
io_error_to_rhai_error(builder.render_to_file(output_path))
}

View File

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

View File

@ -0,0 +1,39 @@
// buildah_debug.rhai
// Demonstrates using the debug flag on the buildah Builder
println("Starting buildah debug example...");
// Define image and container names
let base_image = "ubuntu:22.04";
let container_name = "debug-test-container";
println(`Creating container '${container_name}' from base image '${base_image}'...`);
// Create a new buildah container using the builder pattern
let builder = bah_new(container_name, base_image);
// Enable debug mode
println("Enabling debug mode...");
builder.debug = true;
// Run a simple command to see debug output
println("Running a command with debug enabled...");
let result = builder.run("echo 'Hello from debug mode'");
// Disable debug mode
println("Disabling debug mode...");
builder.debug = false;
// Run another command without debug
println("Running a command with debug disabled...");
let result2 = builder.run("echo 'Hello without debug'");
// Enable debug mode again
println("Enabling debug mode again...");
builder.debug = true;
// Remove the container with debug enabled
println("Removing the container with debug enabled...");
builder.remove();
println("Debug example completed!");

View File

@ -0,0 +1,162 @@
// text_tools.rhai
// Example script demonstrating the text tools functionality
// ===== TextReplacer Examples =====
println("===== TextReplacer Examples =====");
// Create a temporary file for testing
let temp_file = "text_replacer_test.txt";
write_file(temp_file, "This is a foo bar example with FOO and foo occurrences.\nAnother line with foo and bar.");
// Example 1: Simple replacement
println("\n--- Example 1: Simple replacement ---");
let replacer = text_replacer_new()
.pattern("foo")
.replacement("REPLACED")
.build();
let result = replacer.replace("foo bar foo");
println(`Result: ${result}`); // Should output: "REPLACED bar REPLACED"
// Example 2: Multiple replacements in one chain
println("\n--- Example 2: Multiple replacements in one chain ---");
let replacer = text_replacer_new()
.pattern("foo").replacement("AAA")
.pattern("bar").replacement("BBB")
.build();
let result = replacer.replace("foo bar foo baz");
println(`Result: ${result}`); // Should output: "AAA BBB AAA baz"
// Example 3: Case-insensitive regex replacement
println("\n--- Example 3: Case-insensitive regex replacement ---");
let replacer = text_replacer_new()
.pattern("foo")
.replacement("case-insensitive")
.regex(true)
.case_insensitive(true)
.build();
let result = replacer.replace("FOO foo Foo fOo");
println(`Result: ${result}`); // Should output: "case-insensitive case-insensitive case-insensitive case-insensitive"
// Example 4: File operations
println("\n--- Example 4: File operations ---");
let replacer = text_replacer_new()
.pattern("foo").replacement("EXAMPLE")
.build();
// Replace and get result as string
let file_result = replacer.replace_file(temp_file);
println(`File content after replacement:\n${file_result}`);
// Replace in-place
replacer.replace_file_in_place(temp_file);
println("File replaced in-place");
// Replace to a new file
let output_file = "text_replacer_output.txt";
replacer.replace_file_to(temp_file, output_file);
println(`Content written to new file: ${output_file}`);
// Clean up temporary files
delete(temp_file);
delete(output_file);
// ===== TemplateBuilder Examples =====
println("\n\n===== TemplateBuilder Examples =====");
// Create a temporary template file
let template_file = "template_test.txt";
write_file(template_file, "Hello, {{ name }}! Welcome to {{ place }}.\n{% if show_greeting %}Glad to have you here!{% endif %}\nYour items:\n{% for item in items %} - {{ item }}{% if not loop.last %}\n{% endif %}{% endfor %}\n");
// Example 1: Simple template rendering
println("\n--- Example 1: Simple template rendering ---");
let template = template_builder_open(template_file)
.add_var("name", "John")
.add_var("place", "Rhai")
.add_var("show_greeting", true)
.add_var("items", ["apple", "banana", "cherry"]);
let result = template.render();
println(`Rendered template:\n${result}`);
// Example 2: Using a map for variables
println("\n--- Example 2: Using a map for variables ---");
let vars = #{
name: "Alice",
place: "Template World"
};
let template = template_builder_open(template_file)
.add_vars(vars)
.add_var("show_greeting", false)
.add_var("items", ["laptop", "phone", "tablet"]);
let result = template.render();
println(`Rendered template with map:\n${result}`);
// Example 3: Rendering to a file
println("\n--- Example 3: Rendering to a file ---");
let output_file = "template_output.txt";
let template = template_builder_open(template_file)
.add_var("name", "Bob")
.add_var("place", "File Output")
.add_var("show_greeting", true)
.add_var("items", ["document", "spreadsheet", "presentation"]);
template.render_to_file(output_file);
println(`Template rendered to file: ${output_file}`);
println(`Content of the rendered file:\n${read_file(output_file)}`);
// Clean up temporary files
delete(template_file);
delete(output_file);
// ===== Fix Functions Examples =====
println("\n\n===== Fix Functions Examples =====");
// Example 1: name_fix
println("\n--- Example 1: name_fix ---");
let fixed_name = name_fix("Hello World!");
println(`Original: "Hello World!"`);
println(`Fixed: "${fixed_name}"`); // Should output: "hello_world"
let fixed_name = name_fix("File-Name.txt");
println(`Original: "File-Name.txt"`);
println(`Fixed: "${fixed_name}"`); // Should output: "file_name.txt"
let fixed_name = name_fix("Résumé.doc");
println(`Original: "Résumé.doc"`);
println(`Fixed: "${fixed_name}"`); // Should output: "rsum.doc"
// Example 2: path_fix
println("\n--- Example 2: path_fix ---");
let fixed_path = path_fix("/path/to/Hello World!");
println(`Original: "/path/to/Hello World!"`);
println(`Fixed: "${fixed_path}"`); // Should output: "/path/to/hello_world"
let fixed_path = path_fix("./relative/path/to/DOCUMENT-123.pdf");
println(`Original: "./relative/path/to/DOCUMENT-123.pdf"`);
println(`Fixed: "${fixed_path}"`); // Should output: "./relative/path/to/document_123.pdf"
// ===== Dedent Functions Examples =====
println("\n\n===== Dedent Functions Examples =====");
// Example 1: dedent
println("\n--- Example 1: dedent ---");
let indented_text = " line 1\n line 2\n line 3";
println(`Original:\n${indented_text}`);
let dedented = dedent(indented_text);
println(`Dedented:\n${dedented}`); // Should output: "line 1\nline 2\n line 3"
// Example 2: prefix
println("\n--- Example 2: prefix ---");
let text = "line 1\nline 2\nline 3";
println(`Original:\n${text}`);
let prefixed = prefix(text, " ");
println(`Prefixed:\n${prefixed}`); // Should output: " line 1\n line 2\n line 3"
// Return success message
"Text tools example completed successfully!"

View File

@ -31,6 +31,7 @@ impl ReplacementOperation {
/// Text replacer that can perform multiple replacement operations /// Text replacer that can perform multiple replacement operations
/// in a single pass over the input text. /// in a single pass over the input text.
#[derive(Clone)]
pub struct TextReplacer { pub struct TextReplacer {
operations: Vec<ReplacementOperation>, operations: Vec<ReplacementOperation>,
} }
@ -82,7 +83,7 @@ impl TextReplacer {
} }
/// Builder for the TextReplacer. /// Builder for the TextReplacer.
#[derive(Default)] #[derive(Default, Clone)]
pub struct TextReplacerBuilder { pub struct TextReplacerBuilder {
operations: Vec<ReplacementOperation>, operations: Vec<ReplacementOperation>,
pattern: Option<String>, pattern: Option<String>,

View File

@ -5,6 +5,7 @@ use std::path::Path;
use tera::{Context, Tera}; use tera::{Context, Tera};
/// A builder for creating and rendering templates using the Tera template engine. /// A builder for creating and rendering templates using the Tera template engine.
#[derive(Clone)]
pub struct TemplateBuilder { pub struct TemplateBuilder {
template_path: String, template_path: String,
context: Context, context: Context,
@ -254,7 +255,7 @@ mod tests {
fn test_template_with_hashmap_vars() -> Result<(), Box<dyn std::error::Error>> { fn test_template_with_hashmap_vars() -> Result<(), Box<dyn std::error::Error>> {
// Create a temporary template file // Create a temporary template file
let mut temp_file = NamedTempFile::new()?; let mut temp_file = NamedTempFile::new()?;
writeln!(temp_file, "{{ greeting }}, {{ name }}!")?; writeln!(temp_file, "{{{{ greeting }}}}, {{{{ name }}}}!")?;
temp_file.flush()?; temp_file.flush()?;
// Create a HashMap of variables // Create a HashMap of variables

View File

@ -1,5 +1,5 @@
use crate::process::CommandResult; use crate::process::CommandResult;
use crate::virt::buildah::{execute_buildah_command, BuildahError, Image}; use crate::virt::buildah::{execute_buildah_command, BuildahError, Image, thread_local_debug, set_thread_local_debug};
use std::collections::HashMap; use std::collections::HashMap;
/// Builder struct for buildah operations /// Builder struct for buildah operations
@ -116,7 +116,19 @@ impl Builder {
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn run(&self, command: &str) -> Result<CommandResult, BuildahError> { pub fn run(&self, command: &str) -> Result<CommandResult, BuildahError> {
if let Some(container_id) = &self.container_id { if let Some(container_id) = &self.container_id {
execute_buildah_command(&["run", container_id, "sh", "-c", command]) // 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 { } else {
Err(BuildahError::Other("No container ID available".to_string())) Err(BuildahError::Other("No container ID available".to_string()))
} }
@ -134,7 +146,19 @@ impl Builder {
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn run_with_isolation(&self, command: &str, isolation: &str) -> Result<CommandResult, BuildahError> { pub fn run_with_isolation(&self, command: &str, isolation: &str) -> Result<CommandResult, BuildahError> {
if let Some(container_id) = &self.container_id { if let Some(container_id) = &self.container_id {
execute_buildah_command(&["run", "--isolation", isolation, container_id, "sh", "-c", command]) // 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", "--isolation", isolation, container_id, "sh", "-c", command]);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
} else { } else {
Err(BuildahError::Other("No container ID available".to_string())) Err(BuildahError::Other("No container ID available".to_string()))
} }
@ -152,7 +176,19 @@ impl Builder {
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn copy(&self, source: &str, dest: &str) -> Result<CommandResult, BuildahError> { pub fn copy(&self, source: &str, dest: &str) -> Result<CommandResult, BuildahError> {
if let Some(container_id) = &self.container_id { if let Some(container_id) = &self.container_id {
execute_buildah_command(&["copy", container_id, source, dest]) // 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(&["copy", container_id, source, dest]);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
} else { } else {
Err(BuildahError::Other("No container ID available".to_string())) Err(BuildahError::Other("No container ID available".to_string()))
} }
@ -170,7 +206,19 @@ impl Builder {
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn add(&self, source: &str, dest: &str) -> Result<CommandResult, BuildahError> { pub fn add(&self, source: &str, dest: &str) -> Result<CommandResult, BuildahError> {
if let Some(container_id) = &self.container_id { if let Some(container_id) = &self.container_id {
execute_buildah_command(&["add", container_id, source, dest]) // 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(&["add", container_id, source, dest]);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
} else { } else {
Err(BuildahError::Other("No container ID available".to_string())) Err(BuildahError::Other("No container ID available".to_string()))
} }
@ -187,7 +235,19 @@ impl Builder {
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn commit(&self, image_name: &str) -> Result<CommandResult, BuildahError> { pub fn commit(&self, image_name: &str) -> Result<CommandResult, BuildahError> {
if let Some(container_id) = &self.container_id { if let Some(container_id) = &self.container_id {
execute_buildah_command(&["commit", container_id, image_name]) // 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(&["commit", container_id, image_name]);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
} else { } else {
Err(BuildahError::Other("No container ID available".to_string())) Err(BuildahError::Other("No container ID available".to_string()))
} }
@ -200,7 +260,19 @@ impl Builder {
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn remove(&self) -> Result<CommandResult, BuildahError> { pub fn remove(&self) -> Result<CommandResult, BuildahError> {
if let Some(container_id) = &self.container_id { if let Some(container_id) = &self.container_id {
execute_buildah_command(&["rm", 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(&["rm", container_id]);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
} else { } else {
Err(BuildahError::Other("No container ID available".to_string())) Err(BuildahError::Other("No container ID available".to_string()))
} }
@ -213,9 +285,18 @@ impl Builder {
/// * `Result<(), BuildahError>` - Success or error /// * `Result<(), BuildahError>` - Success or error
pub fn reset(&mut self) -> Result<(), BuildahError> { pub fn reset(&mut self) -> Result<(), BuildahError> {
if let Some(container_id) = &self.container_id { 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);
// Try to remove the container // Try to remove the container
let result = execute_buildah_command(&["rm", container_id]); let result = execute_buildah_command(&["rm", container_id]);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
// Clear the container_id regardless of whether the removal succeeded // Clear the container_id regardless of whether the removal succeeded
self.container_id = None; self.container_id = None;
@ -256,7 +337,19 @@ impl Builder {
// Convert Vec<String> to Vec<&str> for execute_buildah_command // Convert Vec<String> to Vec<&str> for execute_buildah_command
let args: Vec<&str> = args_owned.iter().map(|s| s.as_str()).collect(); let args: Vec<&str> = args_owned.iter().map(|s| s.as_str()).collect();
execute_buildah_command(&args) // 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(&args);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
} else { } else {
Err(BuildahError::Other("No container ID available".to_string())) Err(BuildahError::Other("No container ID available".to_string()))
} }
@ -268,6 +361,7 @@ impl Builder {
/// ///
/// * `Result<Vec<Image>, BuildahError>` - List of images or error /// * `Result<Vec<Image>, BuildahError>` - List of images or error
pub fn images() -> Result<Vec<Image>, BuildahError> { pub fn images() -> Result<Vec<Image>, BuildahError> {
// Use default debug value (false) for static method
let result = execute_buildah_command(&["images", "--json"])?; let result = execute_buildah_command(&["images", "--json"])?;
// Try to parse the JSON output // Try to parse the JSON output
@ -339,9 +433,36 @@ impl Builder {
/// ///
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_remove(image: &str) -> Result<CommandResult, BuildahError> { pub fn image_remove(image: &str) -> Result<CommandResult, BuildahError> {
// Use default debug value (false) for static method
execute_buildah_command(&["rmi", image]) execute_buildah_command(&["rmi", image])
} }
/// Remove an image with debug output
///
/// # Arguments
///
/// * `image` - Image ID or name
/// * `debug` - Whether to enable debug output
///
/// # Returns
///
/// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_remove_with_debug(image: &str, debug: bool) -> Result<CommandResult, 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(&["rmi", image]);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
}
/// Pull an image from a registry /// Pull an image from a registry
/// ///
/// # Arguments /// # Arguments
@ -353,6 +474,7 @@ impl Builder {
/// ///
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_pull(image: &str, tls_verify: bool) -> Result<CommandResult, BuildahError> { pub fn image_pull(image: &str, tls_verify: bool) -> Result<CommandResult, BuildahError> {
// Use default debug value (false) for static method
let mut args = vec!["pull"]; let mut args = vec!["pull"];
if !tls_verify { if !tls_verify {
@ -364,6 +486,41 @@ impl Builder {
execute_buildah_command(&args) execute_buildah_command(&args)
} }
/// Pull an image from a registry with debug output
///
/// # Arguments
///
/// * `image` - Image name
/// * `tls_verify` - Whether to verify TLS
/// * `debug` - Whether to enable debug output
///
/// # Returns
///
/// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_pull_with_debug(image: &str, tls_verify: bool, debug: bool) -> Result<CommandResult, BuildahError> {
// Save the current debug flag
let previous_debug = thread_local_debug();
// Set the thread-local debug flag
set_thread_local_debug(debug);
let mut args = vec!["pull"];
if !tls_verify {
args.push("--tls-verify=false");
}
args.push(image);
// Execute the command
let result = execute_buildah_command(&args);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
}
/// Push an image to a registry /// Push an image to a registry
/// ///
/// # Arguments /// # Arguments
@ -376,6 +533,7 @@ impl Builder {
/// ///
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Result<CommandResult, BuildahError> { pub fn image_push(image: &str, destination: &str, tls_verify: bool) -> Result<CommandResult, BuildahError> {
// Use default debug value (false) for static method
let mut args = vec!["push"]; let mut args = vec!["push"];
if !tls_verify { if !tls_verify {
@ -388,6 +546,43 @@ impl Builder {
execute_buildah_command(&args) execute_buildah_command(&args)
} }
/// Push an image to a registry with debug output
///
/// # Arguments
///
/// * `image` - Image name
/// * `destination` - Destination registry
/// * `tls_verify` - Whether to verify TLS
/// * `debug` - Whether to enable debug output
///
/// # Returns
///
/// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_push_with_debug(image: &str, destination: &str, tls_verify: bool, debug: bool) -> Result<CommandResult, BuildahError> {
// Save the current debug flag
let previous_debug = thread_local_debug();
// Set the thread-local debug flag
set_thread_local_debug(debug);
let mut args = vec!["push"];
if !tls_verify {
args.push("--tls-verify=false");
}
args.push(image);
args.push(destination);
// Execute the command
let result = execute_buildah_command(&args);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
}
/// Tag an image /// Tag an image
/// ///
/// # Arguments /// # Arguments
@ -399,9 +594,37 @@ impl Builder {
/// ///
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_tag(image: &str, new_name: &str) -> Result<CommandResult, BuildahError> { pub fn image_tag(image: &str, new_name: &str) -> Result<CommandResult, BuildahError> {
// Use default debug value (false) for static method
execute_buildah_command(&["tag", image, new_name]) execute_buildah_command(&["tag", image, new_name])
} }
/// Tag an image with debug output
///
/// # Arguments
///
/// * `image` - Image ID or name
/// * `new_name` - New tag for the image
/// * `debug` - Whether to enable debug output
///
/// # Returns
///
/// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_tag_with_debug(image: &str, new_name: &str, debug: bool) -> Result<CommandResult, 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(&["tag", image, new_name]);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
}
/// Commit a container to an image with advanced options /// Commit a container to an image with advanced options
/// ///
/// # Arguments /// # Arguments
@ -416,6 +639,7 @@ impl Builder {
/// ///
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_commit(container: &str, image_name: &str, format: Option<&str>, squash: bool, rm: bool) -> Result<CommandResult, BuildahError> { pub fn image_commit(container: &str, image_name: &str, format: Option<&str>, squash: bool, rm: bool) -> Result<CommandResult, BuildahError> {
// Use default debug value (false) for static method
let mut args = vec!["commit"]; let mut args = vec!["commit"];
if let Some(format_str) = format { if let Some(format_str) = format {
@ -437,6 +661,54 @@ impl Builder {
execute_buildah_command(&args) execute_buildah_command(&args)
} }
/// Commit a container to an image with advanced options and debug output
///
/// # Arguments
///
/// * `container` - Container ID or name
/// * `image_name` - Name for the new image
/// * `format` - Optional format (oci or docker)
/// * `squash` - Whether to squash layers
/// * `rm` - Whether to remove the container after commit
/// * `debug` - Whether to enable debug output
///
/// # Returns
///
/// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn image_commit_with_debug(container: &str, image_name: &str, format: Option<&str>, squash: bool, rm: bool, debug: bool) -> Result<CommandResult, BuildahError> {
// Save the current debug flag
let previous_debug = thread_local_debug();
// Set the thread-local debug flag
set_thread_local_debug(debug);
let mut args = vec!["commit"];
if let Some(format_str) = format {
args.push("--format");
args.push(format_str);
}
if squash {
args.push("--squash");
}
if rm {
args.push("--rm");
}
args.push(container);
args.push(image_name);
// Execute the command
let result = execute_buildah_command(&args);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
}
/// Build an image from a Containerfile/Dockerfile /// Build an image from a Containerfile/Dockerfile
/// ///
/// # Arguments /// # Arguments
@ -450,6 +722,7 @@ impl Builder {
/// ///
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn build(tag: Option<&str>, context_dir: &str, file: &str, isolation: Option<&str>) -> Result<CommandResult, BuildahError> { pub fn build(tag: Option<&str>, context_dir: &str, file: &str, isolation: Option<&str>) -> Result<CommandResult, BuildahError> {
// Use default debug value (false) for static method
let mut args = Vec::new(); let mut args = Vec::new();
args.push("build"); args.push("build");
@ -470,4 +743,51 @@ impl Builder {
execute_buildah_command(&args) execute_buildah_command(&args)
} }
/// Build an image from a Containerfile/Dockerfile with debug output
///
/// # Arguments
///
/// * `tag` - Optional tag for the image
/// * `context_dir` - Directory containing the Containerfile/Dockerfile
/// * `file` - Path to the Containerfile/Dockerfile
/// * `isolation` - Optional isolation method
/// * `debug` - Whether to enable debug output
///
/// # Returns
///
/// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn build_with_debug(tag: Option<&str>, context_dir: &str, file: &str, isolation: Option<&str>, debug: bool) -> Result<CommandResult, BuildahError> {
// Save the current debug flag
let previous_debug = thread_local_debug();
// Set the thread-local debug flag
set_thread_local_debug(debug);
let mut args = Vec::new();
args.push("build");
if let Some(tag_value) = tag {
args.push("-t");
args.push(tag_value);
}
if let Some(isolation_value) = isolation {
args.push("--isolation");
args.push(isolation_value);
}
args.push("-f");
args.push(file);
args.push(context_dir);
// Execute the command
let result = execute_buildah_command(&args);
// Restore the previous debug flag
set_thread_local_debug(previous_debug);
result
}
} }

View File

@ -14,7 +14,7 @@ use super::{BuildahError, Builder};
/// ///
/// * `Result<CommandResult, BuildahError>` - Command result or error /// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahError> { pub fn execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahError> {
// Get the current thread-local Builder instance if available // Get the debug flag from thread-local storage
let debug = thread_local_debug(); let debug = thread_local_debug();
if debug { if debug {
@ -37,6 +37,7 @@ pub fn execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahEr
code: output.status.code().unwrap_or(-1), code: output.status.code().unwrap_or(-1),
}; };
// Always output stdout/stderr when debug is true
if debug { if debug {
if !result.stdout.is_empty() { if !result.stdout.is_empty() {
println!("Command stdout: {}", result.stdout); println!("Command stdout: {}", result.stdout);
@ -45,26 +46,28 @@ pub fn execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahEr
if !result.stderr.is_empty() { if !result.stderr.is_empty() {
println!("Command stderr: {}", result.stderr); 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 { if result.success {
if debug {
println!("Command succeeded with code {}", result.code);
}
Ok(result) Ok(result)
} else { } else {
let error_msg = format!("Command failed with code {}: {}", // If command failed and debug is false, output stderr
result.code, result.stderr.trim()); if !debug {
if debug { println!("Command failed with code {}: {}", result.code, result.stderr.trim());
println!("Command failed: {}", error_msg);
} }
Err(BuildahError::CommandFailed(error_msg)) Err(BuildahError::CommandFailed(format!("Command failed with code {}: {}",
result.code, result.stderr.trim())))
} }
}, },
Err(e) => { Err(e) => {
if debug { // Always output error information
println!("Command execution failed: {}", e); println!("Command execution failed: {}", e);
}
Err(BuildahError::CommandExecutionFailed(e)) Err(BuildahError::CommandExecutionFailed(e))
} }
} }
@ -89,41 +92,4 @@ pub fn thread_local_debug() -> bool {
}) })
} }
/// Execute a buildah command with debug output // This function is no longer needed as the debug functionality is now integrated into execute_buildah_command
///
/// # Arguments
///
/// * `args` - The command arguments
/// * `builder` - Reference to a Builder instance for debug output
///
/// # Returns
///
/// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn execute_buildah_command_with_debug(args: &[&str], builder: &Builder) -> Result<CommandResult, BuildahError> {
if builder.debug() {
println!("Executing buildah command: buildah {}", args.join(" "));
}
let result = execute_buildah_command(args);
if builder.debug() {
match &result {
Ok(cmd_result) => {
if !cmd_result.stdout.is_empty() {
println!("Command stdout: {}", cmd_result.stdout);
}
if !cmd_result.stderr.is_empty() {
println!("Command stderr: {}", cmd_result.stderr);
}
println!("Command succeeded with code {}", cmd_result.code);
},
Err(e) => {
println!("Command failed: {}", e);
}
}
}
result
}