This commit is contained in:
despiegk 2025-04-05 09:56:10 +02:00
parent 78db13d738
commit 4be9445702
8 changed files with 185 additions and 16 deletions

View File

@ -11,6 +11,7 @@ categories = ["os", "filesystem", "api-bindings"]
readme = "README.md"
[dependencies]
tera = "1.19.0" # Template engine for text rendering
# Cross-platform functionality
libc = "0.2"
cfg-if = "1.0"

View File

@ -0,0 +1,66 @@
use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::io::Write;
use tempfile::NamedTempFile;
use sal::text::TemplateBuilder;
fn main() -> Result<(), Box<dyn Error>> {
// Create a temporary template file for our examples
let temp_file = NamedTempFile::new()?;
let template_content = "Hello, {{ name }}! Welcome to {{ place }}.\n\
{% if show_greeting %}Glad to have you here!{% endif %}\n\
Your items:\n\
{% for item in items %} - {{ item }}{% if not loop.last %}\n{% endif %}{% endfor %}\n";
std::fs::write(temp_file.path(), template_content)?;
println!("Created temporary template at: {}", temp_file.path().display());
// Example 1: Simple variable replacement
println!("\n--- Example 1: Simple variable replacement ---");
let mut builder = TemplateBuilder::open(temp_file.path())?;
builder = builder
.add_var("name", "John")
.add_var("place", "Rust")
.add_var("show_greeting", true)
.add_var("items", vec!["apple", "banana", "cherry"]);
let result = builder.render()?;
println!("Rendered template:\n{}", result);
// Example 2: Using a HashMap for variables
println!("\n--- Example 2: Using a HashMap for variables ---");
let mut vars = HashMap::new();
vars.insert("name", "Alice");
vars.insert("place", "Template World");
let mut builder = TemplateBuilder::open(temp_file.path())?;
builder = builder
.add_vars(vars)
.add_var("show_greeting", false)
.add_var("items", vec!["laptop", "phone", "tablet"]);
let result = builder.render()?;
println!("Rendered template with HashMap:\n{}", result);
// Example 3: Rendering to a file
println!("\n--- Example 3: Rendering to a file ---");
let output_file = NamedTempFile::new()?;
let mut builder = TemplateBuilder::open(temp_file.path())?;
builder = builder
.add_var("name", "Bob")
.add_var("place", "File Output")
.add_var("show_greeting", true)
.add_var("items", vec!["document", "spreadsheet", "presentation"]);
builder.render_to_file(output_file.path())?;
println!("Template rendered to file: {}", output_file.path().display());
// Read the output file to verify
let output_content = std::fs::read_to_string(output_file.path())?;
println!("Content of the rendered file:\n{}", output_content);
Ok(())
}

View File

@ -4,7 +4,7 @@
use rhai::{Engine, EvalAltResult, Array, Dynamic, Map};
use std::collections::HashMap;
use crate::virt::buildah::{self, BuildahError, Image, Builder};
use crate::virt::buildah::{self, BuildahError, Image, Builder, ContentOperations};
use crate::process::CommandResult;
/// Register Buildah module functions with the Rhai engine
@ -150,12 +150,26 @@ pub fn builder_config(builder: &mut Builder, options: Map) -> Result<CommandResu
/// Write content to a file in the container
pub fn builder_write_content(builder: &mut Builder, content: &str, dest_path: &str) -> Result<CommandResult, Box<EvalAltResult>> {
bah_error_to_rhai_error(builder.write_content(content, dest_path))
if let Some(container_id) = builder.container_id() {
bah_error_to_rhai_error(ContentOperations::write_content(container_id, content, dest_path))
} else {
Err(Box::new(EvalAltResult::ErrorRuntime(
"No container ID available".into(),
rhai::Position::NONE
)))
}
}
/// Read content from a file in the container
pub fn builder_read_content(builder: &mut Builder, source_path: &str) -> Result<String, Box<EvalAltResult>> {
bah_error_to_rhai_error(builder.read_content(source_path))
if let Some(container_id) = builder.container_id() {
bah_error_to_rhai_error(ContentOperations::read_content(container_id, source_path))
} else {
Err(Box::new(EvalAltResult::ErrorRuntime(
"No container ID available".into(),
rhai::Position::NONE
)))
}
}
// Builder static methods

View File

@ -1,7 +1,9 @@
mod dedent;
mod fix;
mod replace;
mod template;
pub use dedent::*;
pub use fix::*;
pub use replace::*;
pub use template::*;

View File

@ -210,9 +210,9 @@ mod tests {
#[test]
fn test_template_rendering() -> Result<(), Box<dyn std::error::Error>> {
// Create a temporary template file
let mut temp_file = NamedTempFile::new()?;
writeln!(temp_file, "Hello, {{ name }}! Welcome to {{ place }}.")?;
temp_file.flush()?;
let temp_file = NamedTempFile::new()?;
let template_content = "Hello, {{ name }}! Welcome to {{ place }}.\n";
fs::write(temp_file.path(), template_content)?;
// Create a template builder and add variables
let mut builder = TemplateBuilder::open(temp_file.path())?;
@ -230,10 +230,9 @@ mod tests {
#[test]
fn test_template_with_multiple_vars() -> Result<(), Box<dyn std::error::Error>> {
// Create a temporary template file
let mut temp_file = NamedTempFile::new()?;
writeln!(temp_file, "{% if show_greeting %}Hello, {{ name }}!{% endif %}")?;
writeln!(temp_file, "{% for item in items %}{{ item }}{% if not loop.last %}, {% endif %}{% endfor %}")?;
temp_file.flush()?;
let temp_file = NamedTempFile::new()?;
let template_content = "{% if show_greeting %}Hello, {{ name }}!{% endif %}\n{% for item in items %}{{ item }}{% if not loop.last %}, {% endif %}{% endfor %}\n";
fs::write(temp_file.path(), template_content)?;
// Create a template builder and add variables
let mut builder = TemplateBuilder::open(temp_file.path())?;
@ -273,13 +272,13 @@ mod tests {
Ok(())
}
#[test]
fn test_render_to_file() -> Result<(), Box<dyn std::error::Error>> {
// Create a temporary template file
let mut temp_file = NamedTempFile::new()?;
writeln!(temp_file, "{{ message }}")?;
temp_file.flush()?;
let temp_file = NamedTempFile::new()?;
let template_content = "{{ message }}\n";
fs::write(temp_file.path(), template_content)?;
// Create an output file
let output_file = NamedTempFile::new()?;

View File

@ -11,6 +11,8 @@ pub struct Builder {
container_id: Option<String>,
/// Base image
image: String,
/// Debug mode
debug: bool,
}
impl Builder {
@ -37,6 +39,7 @@ impl Builder {
name: name.to_string(),
container_id: Some(container_id),
image: image.to_string(),
debug: false,
})
},
Err(BuildahError::CommandFailed(error_msg)) => {
@ -58,6 +61,7 @@ impl Builder {
name: name.to_string(),
container_id: Some(container_id),
image: image.to_string(),
debug: false,
})
} else {
// Couldn't extract container ID
@ -85,6 +89,17 @@ impl Builder {
&self.name
}
/// Get the debug mode
pub fn debug(&self) -> bool {
self.debug
}
/// Set the debug mode
pub fn set_debug(&mut self, debug: bool) -> &mut Self {
self.debug = debug;
self
}
/// Get the base image
pub fn image(&self) -> &str {
&self.image

View File

@ -0,0 +1,70 @@
use crate::process::CommandResult;
use crate::virt::buildah::{execute_buildah_command, BuildahError};
use std::fs::File;
use std::io::{Read, Write};
use tempfile::NamedTempFile;
/// Functions for working with file content in buildah containers
pub struct ContentOperations;
impl ContentOperations {
/// Write content to a file in the container
///
/// # Arguments
///
/// * `container_id` - The container ID
/// * `content` - The content to write
/// * `dest_path` - Destination path in the container
///
/// # Returns
///
/// * `Result<CommandResult, BuildahError>` - Command result or error
pub fn write_content(container_id: &str, content: &str, dest_path: &str) -> Result<CommandResult, BuildahError> {
// Create a temporary file
let mut temp_file = NamedTempFile::new()
.map_err(|e| BuildahError::Other(format!("Failed to create temporary file: {}", e)))?;
// Write content to the temporary file
temp_file.write_all(content.as_bytes())
.map_err(|e| BuildahError::Other(format!("Failed to write to temporary file: {}", e)))?;
// Flush the file to ensure content is written
temp_file.flush()
.map_err(|e| BuildahError::Other(format!("Failed to flush temporary file: {}", e)))?;
// Copy the temporary file to the container
let temp_path = temp_file.path().to_string_lossy().to_string();
execute_buildah_command(&["copy", container_id, &temp_path, dest_path])
}
/// Read content from a file in the container
///
/// # Arguments
///
/// * `container_id` - The container ID
/// * `source_path` - Source path in the container
///
/// # Returns
///
/// * `Result<String, BuildahError>` - File content or error
pub fn read_content(container_id: &str, source_path: &str) -> Result<String, BuildahError> {
// Create a temporary file
let temp_file = NamedTempFile::new()
.map_err(|e| BuildahError::Other(format!("Failed to create temporary file: {}", e)))?;
let temp_path = temp_file.path().to_string_lossy().to_string();
// Copy the file from the container to the temporary file
execute_buildah_command(&["copy", container_id, source_path, &temp_path])?;
// Read the content from the temporary file
let mut file = File::open(temp_file.path())
.map_err(|e| BuildahError::Other(format!("Failed to open temporary file: {}", e)))?;
let mut content = String::new();
file.read_to_string(&mut content)
.map_err(|e| BuildahError::Other(format!("Failed to read from temporary file: {}", e)))?;
Ok(content)
}
}

View File

@ -2,6 +2,7 @@ mod containers;
mod images;
mod cmd;
mod builder;
mod content;
#[cfg(test)]
mod containers_test;
@ -53,3 +54,4 @@ pub use containers::*;
#[deprecated(since = "0.2.0", note = "Use Builder methods instead")]
pub use images::*;
pub use cmd::*;
pub use content::ContentOperations;