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\n
section
- 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 ofStr
boolean
- value oftrue | false
number
- a signed integer ofisize
list
- 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.