<!-- {{# generate.module_header{} #}} -->
# Module :: genfile
[](https://github.com/emersion/stability-badges#experimental) [](https://github.com/Wandalen/wTools/actions/workflows/ModulegenfilePush.yml) [](https://docs.rs/genfile) [](https://discord.gg/m3YfbXpUUY)
A trait-based template processing and file generation library for Rust. **genfile** provides self-contained template archives with parameter storage, pluggable rendering engines, and testable in-memory file systems.
## Features
- **Self-Contained Archives**: Template files with embedded parameters stored inside (JSON/YAML serialization)
- **Binary + Text Support**: Handle both text templates and binary files (images, etc.) with base64 encoding
- **Pluggable Architecture**: Trait-based design for custom value types, renderers, and file systems
- **Testable**: Built-in `MemoryFileSystem` for fast, isolated testing without disk I/O
- **Security**: Path traversal validation prevents directory escape attacks
- **External Content**: Support for `FileRef` and `UrlRef` with custom resolvers and storage backends
- **Template Engine**: Default Handlebars renderer with support for custom engines
- **215 Tests**: Comprehensive test coverage including 27 security tests
## Quick Start
Add to your `Cargo.toml`:
```toml
[dependencies]
genfile_core = "0.1"
```
### Basic Example
```rust
use genfile_core::{ TemplateArchive, WriteMode, Value, MemoryFileSystem, HandlebarsRenderer, FileSystem };
use std::path::{ Path, PathBuf };
// Create a template archive
let mut archive = TemplateArchive::new( "my-project" );
// Add template files
archive.add_text_file(
PathBuf::from( "README.md" ),
"# {{project_name}}\n\n{{description}}",
WriteMode::Rewrite
);
archive.add_text_file(
PathBuf::from( "src/main.rs" ),
"fn main() {\n println!(\"Hello from {{project_name}}!\");\n}",
WriteMode::Rewrite
);
// Set parameter values
archive.set_value( "project_name", Value::String( "MyApp".into() ) );
archive.set_value( "description", Value::String( "A cool application".into() ) );
// Materialize to memory filesystem (for testing)
let renderer = HandlebarsRenderer::new();
let mut fs = MemoryFileSystem::new();
archive.materialize_with_components(
Path::new( "/output" ),
&renderer,
&mut fs
).unwrap();
// Verify generated files
assert!( fs.exists( &PathBuf::from( "/output/README.md" ) ) );
assert_eq!(
fs.read( &PathBuf::from( "/output/README.md" ) ).unwrap(),
"# MyApp\n\nA cool application"
);
```
### Sample
<!-- {{# generate.sample{} #}} -->
## Examples
### Archive with Binary Files
```rust
use genfile_core::{ TemplateArchive, FileContent, WriteMode, Value };
use std::path::PathBuf;
let mut archive = TemplateArchive::new( "website" );
// Text template
archive.add_text_file(
PathBuf::from( "index.html" ),
"<html><title>{{title}}</title></html>",
WriteMode::Rewrite
);
// Binary file (PNG header)
archive.add_binary_file(
PathBuf::from( "logo.png" ),
vec![ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A ]
);
archive.set_value( "title", Value::String( "My Site".into() ) );
```
### Serialization and Persistence
```rust
use genfile_core::TemplateArchive;
// Create and serialize to JSON
let archive = TemplateArchive::new( "config" );
let json = archive.to_json_pretty().unwrap();
// Save to file
std::fs::write( "template.json", json ).unwrap();
// Load from file
let json = std::fs::read_to_string( "template.json" ).unwrap();
let restored = TemplateArchive::from_json( &json ).unwrap();
```
### External Content Sources
```rust,ignore
use genfile_core::{ TemplateArchive, FileRef, UrlRef, WriteMode };
use std::path::PathBuf;
let mut archive = TemplateArchive::new( "docs" );
// Reference external file
archive.add_file_from(
PathBuf::from( "header.txt" ),
FileRef::new( PathBuf::from( "/templates/header.hbs" ) ),
WriteMode::Rewrite
);
// Reference URL
archive.add_file_from(
PathBuf::from( "footer.txt" ),
UrlRef::new( "https://example.com/templates/footer.hbs" ),
WriteMode::Rewrite
);
```
### Custom Storage Backend
```rust,ignore
use genfile_core::{ TemplateArchive, ContentStorage, FileContent, HandlebarsRenderer };
use std::path::Path;
struct CloudStorage;
impl ContentStorage for CloudStorage
{
fn store( &mut self, path: &Path, content: &FileContent ) -> Result< (), genfile_core::Error >
{
// Upload to S3, Azure, etc.
println!( "Uploading {} to cloud", path.display() );
Ok( () )
}
}
```
### Parameter Discovery and Analysis
```rust,ignore
use genfile_core::{ TemplateArchive, ParameterDescriptor, WriteMode };
use std::path::PathBuf;
let mut archive = TemplateArchive::new( "app" );
archive.add_text_file(
PathBuf::from( "config.txt" ),
"App: {{app_name}}",
WriteMode::Rewrite
);
// Add parameter definitions
archive.add_parameter( ParameterDescriptor
{
parameter: "app_name".into(),
is_mandatory: true,
default_value: None,
description: Some( "Application name".into() ),
});
// Discover parameters used in templates
let discovered = archive.discover_parameters();
// Analyze parameter usage
let usage = archive.analyze_parameter_usage();
// Find undefined parameters
let undefined = archive.get_undefined_parameters();
```
## API Overview
### Core Types
- **`TemplateArchive`** - Main entity for template operations, stores files, parameters, and values
- **`Template<V,R>`** - Alternative API with custom value types and renderers
- **`TemplateFile`** - Individual file with content, metadata, and optional external source
- **`FileContent`** - Enum for `Text(String)` or `Binary(Vec<u8>)`
- **`Value`** - Default parameter value type: `String`, `Number`, `Bool`, `List`
### Traits
- **`TemplateValue`** - Trait for custom parameter value types
- **`TemplateRenderer`** - Trait for pluggable rendering engines (default: Handlebars)
- **`FileSystem`** - Trait for file operations (`RealFileSystem` or `MemoryFileSystem`)
- **`ContentResolver`** - Trait for resolving external content sources
- **`ContentStorage`** - Trait for custom storage backends
### Content Sources
- **`ContentSource::Inline`** - Content embedded directly in archive
- **`ContentSource::File`** - Reference to external file path
- **`ContentSource::Url`** - Reference to remote URL
## Security
All file paths are validated to prevent directory traversal attacks:
```rust
use genfile_core::validate_path;
use std::path::Path;
// Valid paths
assert!( validate_path( Path::new( "src/lib.rs" ) ).is_ok() );
assert!( validate_path( Path::new( "./foo/bar.txt" ) ).is_ok() );
// Invalid paths (rejected)
assert!( validate_path( Path::new( "../etc/passwd" ) ).is_err() );
assert!( validate_path( Path::new( "foo/../../bar" ) ).is_err() );
```
27 dedicated security tests ensure protection against malicious paths.
## Testing
Use `MemoryFileSystem` for fast, isolated tests:
```rust,no_run
use genfile_core::{ TemplateArchive, MemoryFileSystem, HandlebarsRenderer, FileSystem };
use std::path::Path;
#[ test ]
fn test_generation()
{
let archive = TemplateArchive::new( "test" );
let renderer = HandlebarsRenderer::new();
let mut fs = MemoryFileSystem::new();
// No disk I/O - runs in memory
archive.materialize_with_components(
Path::new( "/output" ),
&renderer,
&mut fs
).unwrap();
// Fast assertions
assert!( fs.exists( Path::new( "/output/README.md" ) ) );
}
```
## Links
- [Documentation](https://docs.rs/genfile)
- [Repository](https://github.com/Wandalen/wTools)
- [Discord](https://discord.gg/m3YfbXpUUY)