| Crates.io | varsubst |
| lib.rs | varsubst |
| version | 0.0.1 |
| created_at | 2025-11-11 00:57:51.902773+00 |
| updated_at | 2025-11-11 00:57:51.902773+00 |
| description | High-performance variable substitution library with single-pass parsing |
| homepage | |
| repository | https://github.com/AprilNEA/varsubst |
| max_upload_size | |
| id | 1926512 |
| size | 92,583 |
High-performance variable substitution library and CLI tool written in Rust, inspired by envsubst but with O(n) single-pass parsing and additional features.
${VAR}: Standard brace-delimited variables (always supported)$VAR: Short form variables (optional, enable with short_syntax feature)\$, \{, \} (enabled by default)cargo install --path . --features cli
Note: The CLI is an optional feature to keep the library lightweight. When used as a library, clap is not included as a dependency.
Add to your Cargo.toml:
[dependencies]
varsubst = "0.1"
use varsubst::substitute;
use std::collections::HashMap;
let mut vars = HashMap::new();
vars.insert("NAME", "World");
vars.insert("COUNT", "42");
let result = substitute("Hello ${NAME}! Count: ${COUNT}", &vars).unwrap();
assert_eq!(result, "Hello World! Count: 42");
use varsubst::substitute;
use std::collections::HashMap;
let vars = HashMap::new();
let result = substitute(r"Price: \${PRICE}", &vars).unwrap();
assert_eq!(result, "Price: ${PRICE}");
[dependencies]
varsubst = { version = "0.1", features = ["short_syntax"] }
use varsubst::substitute;
use std::collections::HashMap;
let mut vars = HashMap::new();
vars.insert("USER", "alice");
vars.insert("HOME", "/home/alice");
let result = substitute("User: $USER, Home: $HOME", &vars).unwrap();
assert_eq!(result, "User: alice, Home: /home/alice");
use varsubst::substitute_from_env;
std::env::set_var("MY_VAR", "test");
let result = substitute_from_env("Value: ${MY_VAR}").unwrap();
assert_eq!(result, "Value: test");
# From stdin
echo "Hello ${USER}!" | varsubst
# From file
varsubst template.txt
# With custom variables
varsubst -v NAME=Alice -v AGE=30 template.txt
# Without environment variables
varsubst --no-env -v NAME=Alice template.txt
# Save to file
varsubst template.txt -o output.txt
# Fail on undefined variables
varsubst -f template.txt
# Create a template
cat > template.txt << 'EOF'
User: ${USER}
Home: ${HOME}
Custom: ${CUSTOM_VAR}
EOF
# Substitute with environment and custom variables
export CUSTOM_VAR="my value"
varsubst template.txt
# Output:
# User: your_username
# Home: /home/your_username
# Custom: my value
Usage: varsubst [OPTIONS] [FILE]
Arguments:
[FILE] Input file (or stdin if not specified)
Options:
-o, --output <FILE> Output file (or stdout if not specified)
-v, --var <KEY=VALUE> Define variables (format: KEY=VALUE)
--no-env Don't use environment variables
-f, --fail-on-undefined Fail if undefined variables are found
-h, --help Print help
-V, --version Print version
[features]
default = ["escape"]
# Support $X (short variable syntax without braces)
short_syntax = []
# Support escape sequences (\$, \{, \})
escape = []
# CLI binary (optional, includes clap for command-line interface)
cli = ["dep:clap"]
Library Size: The library without CLI feature is only ~63KB, while the CLI binary with clap is ~950KB.
# Default (with escape)
cargo build
# With all features
cargo build --features short_syntax
# Minimal (no features)
cargo build --no-default-features
# Only short syntax (no escape)
cargo build --no-default-features --features short_syntax
| Scenario | envsubst-rs | varsubst | Improvement |
|---|---|---|---|
| Time Complexity | O(n × m) | O(n) | Linear vs Quadratic |
| String Scans | M (# of vars) | 1 | M× faster |
| Memory | Multiple passes | Single pass | More efficient |
Example: With 10 variables in a 1KB file:
envsubst-rs: Scans 10KB totalvarsubst: Scans 1KB total (10× faster)Run benchmarks:
cargo bench --bench substitution
Performance highlights (on Apple Silicon):
| Scenario | Time | Description |
|---|---|---|
| Single variable | ~198 ns | Hello ${VAR}! |
| Fast path (no vars) | ~29 ns | Pure text, 7× faster |
| 10 variables | ~450 ns | Linear scaling |
| 100 variables | ~3.5 µs | Still blazing fast |
| 1KB template | ~2.1 µs | ~476 MB/s throughput |
| Real-world config | ~672 ns | 8 variables, 200 chars |
Key optimizations:
See OPTIMIZATION_RESULTS.md for detailed analysis.
Variable names must:
a-z, A-Z) or underscore (_)Valid examples:
${VAR}${my_var}${_private}${VAR123}Invalid examples:
${} (empty)${123VAR} (starts with digit)${MY-VAR} (contains hyphen)The library provides detailed error types:
pub enum SubstError {
UnclosedBrace { position: usize },
InvalidVarName { name: String, position: usize },
}
Example:
use varsubst::substitute;
use std::collections::HashMap;
let vars = HashMap::new();
match substitute("Hello ${NAME", &vars) {
Err(varsubst::SubstError::UnclosedBrace { position }) => {
println!("Unclosed brace at position {}", position);
}
_ => {}
}
# Run all tests with default features
cargo test
# Test with all features
cargo test --all-features
# Test without default features
cargo test --no-default-features
# Test specific feature
cargo test --features short_syntax
Contributions are welcome! Please feel free to submit a Pull Request.
This project is dual-licensed under MIT OR Apache-2.0.
| Feature | envsubst-rs | varsubst |
|---|---|---|
${VAR} syntax |
✅ | ✅ |
$VAR syntax |
❌ | ✅ (feature) |
| Escape sequences | ❌ | ✅ (default) |
| Time complexity | O(n × m) | O(n) |
| Single-pass parsing | ❌ | ✅ |
| CLI tool | ❌ | ✅ |
| Error positions | ❌ | ✅ |
Check out the examples directory for more usage examples:
# Run library example
cargo run --example basic
# Run CLI example
cargo run -- examples/template.txt -v NAME=Alice
Inspired by:
envsubst utility