A Zig library to expand variables in the style of Kubernetes pod manifests.
This is a port from Kubernetes under third_party/forked/golang/expansion, which is forked from Go (hence the Go license).
Kubernetes uses the $(VAR_NAME) syntax for variable expansion in pod specifications. This library provides the same expansion functionality for use in Zig applications.
- Expands
$(VAR_NAME)variable references - Supports escape sequences:
$$becomes a literal$ - Preserves undefined variables:
$(UNDEFINED)stays as-is - Ignores shell syntax:
${VAR}passes through unchanged - Supports multiple lookup contexts (searched in order)
zig fetch --save git+https://github.com/cloudboss/k8s-expandzThen in your build.zig:
const k8s_expand = b.dependency("k8s_expand", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("k8s_expand", k8s_expand.module("k8s_expand"));const std = @import("std");
const k8s_expand = @import("k8s_expand");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Create a variable map
var env = std.StringHashMap([]const u8).init(allocator);
defer env.deinit();
try env.put("GREETING", "Hello");
try env.put("NAME", "World");
// Create context (array of map pointers)
const context = [_]*const std.StringHashMap([]const u8){&env};
// Expand variables
const result = try k8s_expand.expand(allocator, "$(GREETING), $(NAME)!", &context);
defer allocator.free(result);
std.debug.print("{s}\n", .{result}); // Output: Hello, World!
}pub fn expand(
allocator: Allocator,
input: []const u8,
context: Context,
) ![]u8Expands all $(VAR_NAME) references in the input string. The returned string is allocated and must be freed by the caller.
pub fn expandMaybeAlloc(
allocator: Allocator,
input: []const u8,
context: Context,
) ![]const u8Optimized version that returns the original input if no expansion is needed. Check if result.ptr != input.ptr to determine if the result needs to be freed.
pub fn lookup(context: Context, name: []const u8) ?[]const u8Looks up a variable name in the context maps. Returns the value if found, or null if not found.
pub fn syntaxWrap(allocator: Allocator, input: []const u8) ![]u8Wraps a variable name in the expansion syntax: "FOO" becomes "$(FOO)".
| Input | Output | Description |
|---|---|---|
$(VAR) |
value |
Variable expansion (if VAR is defined) |
$(VAR) |
$(VAR) |
Preserved (if VAR is undefined) |
$$ |
$ |
Escaped dollar sign |
$VAR |
$VAR |
Bare variable (no parentheses) - passed through |
${VAR} |
${VAR} |
Shell syntax - passed through |
$(unclosed |
$(unclosed |
Unterminated - passed through |
You can provide multiple variable maps. They are searched in order, with the first match winning:
var env1 = std.StringHashMap([]const u8).init(allocator);
try env1.put("VAR", "from-env1");
var env2 = std.StringHashMap([]const u8).init(allocator);
try env2.put("VAR", "from-env2");
try env2.put("OTHER", "only-in-env2");
const context = [_]*const std.StringHashMap([]const u8){ &env1, &env2 };
// $(VAR) -> "from-env1" (first context wins)
// $(OTHER) -> "only-in-env2" (found in second context)zig build # Build the library
zig build test # Run testsSame license as the original k8s-expand library.