How to use
First, import Cfp on your Zig source file.
Create an app.conf file on your projects root directory, then copy and paste the following code into it.
# Flat section
global {
prop_1 = 100
prop_2 = true
prop_3 = "hello"
prop_4 = [100, true, "hello"]
}
# Nested sections
project {
one { one { prop = "hello" } }
two {
prop = [100, true, "hello"]
foo = "bar"
}
}
applet {
proj_1 {
host_name = "example.com"
shared_object = "../proj-1/zig-out/lib/lib-proj-1.so"
}
}
Syntax and Definitions
The above configuration snippet highlights a barebones config structure along with all supported node and data types.
Node Types
comment- single line comment ending with\nsection- contains arbitrary number of nested sections or propertiesproperty- contains arbitrary number of<key> = <value> | <value list>
Remarks: property can only be used within a section. Any top level property will cause the parser to fail with the InvalidFormat error.
Data Types
string- value ofStrboolean- value oftrue | falsenumber- a signed integer ofisizelist- any number of,separated[<value 1>,...<value N>]
Remarks list can contain any combination of above scaler types.
Flat vs Nested Section
A section with only property nodes is called a flat section, on the other hand a section with only section nodes is called a nested section.
Limitation
As of now, a section with mixed sections as below will result to a recursive panic with segmentation fault.
Code Example
Copy and paste the following function into your main.zig file.
/// **Remarks:** Return value must be freed by the caller.
fn getUri(heap: Allocator, child: []const u8) ![]const u8 {
const exe_dir = try std.fs.selfExeDirPathAlloc(heap);
defer heap.free(exe_dir);
if (std.mem.count(u8, exe_dir, "zig-out/bin") == 1) {
const fmt_str = "{s}/../../{s}";
return try std.fmt.allocPrint(heap, fmt_str, .{exe_dir, child});
}
unreachable;
}
The following code example demonstrates how to access configuration data at runtime. Cfp uses . notation to access nested sections and properties at any level.
Copy and paste the following code into your main function.
var gpa_mem = std.heap.DebugAllocator(.{}).init;
defer std.debug.assert(gpa_mem.deinit() == .ok);
const heap = gpa_mem.allocator();
const path = try getUri(heap, "app.conf");
defer heap.free(path);
try Cfp.init(heap, .{.abs_path = path});
defer Cfp.deinit();
// Extracts integer into the given integer type
std.debug.assert(try Cfp.getInt(u8, "global.prop_1") == 100);
std.debug.assert(try Cfp.getInt(u32, "global.prop_1") == 100);
std.debug.assert(try Cfp.getInt(isize, "global.prop_1") == 100);
std.debug.assert(try Cfp.getInt(usize, "global.prop_1") == 100);
// Extracts boolean value
std.debug.assert(try Cfp.getBool("global.prop_2") == true);
// Extracts string slice
const data = try Cfp.getStr("global.prop_3");
std.debug.assert(std.mem.eql(u8, "hello", data));
// Extracts List Values
const items = try Cfp.getList("global.prop_4");
std.debug.assert(items[0].number == 100);
std.debug.assert(items[1].boolean == true);
std.debug.assert(std.mem.eql(u8, "hello", items[2].string));
// Extracts string slice from a nested section
const data2 = try Cfp.getStr("project.one.one.prop");
std.debug.assert(std.mem.eql(u8, "hello", data2));
// Extracts List Values
const nested_items = try Cfp.getList("project.two.prop");
std.debug.assert(nested_items[0].number == 100);
std.debug.assert(nested_items[1].boolean == true);
std.debug.assert(std.mem.eql(u8, "hello", nested_items[2].string));
std.debug.print("Well done!\n", .{});
Remarks: You can also pass an env value and an absolute path for more complex setup when calling Cfp.init().
For dynamic and runtime known configuration make sure to checkout Cfp.getProperties() and Cfp.getSections() at API Reference.