ZINI — Yet Another Zig INI Parser

ZINI is the most consistent INI file parser library written in ZIG. The parser is designed to be robust and handle various INI file formats.


Features

  • Comments: Ignores lines starting with ; or #.

  • Includes: Supports including other INI files using the include directive.

  • Sections: Parses sections denoted by [section_name].

  • Section nesting: Handles nested sections (sub sections) in the format [section.subsection] [section subsection sub] or [section subsection].

  • Key-Value Pairs: Extracts key-value pairs from sections (key = value).

  • Multiline Values: Supports multiline values using escaped newlines within double quotes.

  • Character Escaping: Handles escaped characters within values.

  • Value type: enumerations, booleans,integers, floats, strings and arrays.

  • Read Support: Read from File, and Strings.

Installation

Developers tend to either use

  • The latest tagged release of Zig
  • The latest build of Zigs master branch

Depending on which developer you are, you need to run different zig fetch commands:

# Version of zini that works with a tagged release of Zig
# Replace `<REPLACE ME>` with the version of zini that you want to use
# See: https://github.com/loo-re/zini/releases
zig fetch --save https://github.com/loo-re/zini/archive/refs/tags/<REPLACE ME>.tar.gz

# Version of zini that works with latest build of Zigs master branch
zig fetch --save git+https://github.com/loo-re/zini

Then add the following to build.zig:

const zini = b.dependency("zini", .{});
exe.root_module.addImport("zini", zini.module("zini"));

Example

const std = @import("std");
const Parser = @import("zini").Parser;
const errors = @import("zini").errors;


pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();
    const Answer = enum{ Yes,No};

    var parser = try Parser.init(allocator);
    defer parser.deinit();

    // Load INI text
    const ini_text =
        \\[section]
        \\key = value
        \\agree = yes
        \\multiline_key = "line1 \
        \\                 line2 \
        \\                 line3"
        \\[section sub]
        \\key = sub section
    ;

    parser.loadText(ini_text) catch |e| {
        switch (e) {
            errors.InvalidFormat => {
                std.debug.print("Error InvalidFormat \n[{any}] {s}\n", .{
                    parser.line.number,
                    parser.line.content,
                });
                std.process.exit(2);
            },
            else => {
                std.debug.print("Error {any}\n", .{e});
                std.process.exit(1);
            },
        }
    };

    // Accessing values
    if (parser.section("section")) |section| {
        const value = section.getString("key", "");
        std.debug.print("key: {s}\n", .{value});
        const agree = section.getEnum(Answer,"agree", .No);
        std.debug.print("agree: {s}\n", .{@tagName(agree)});

        const multiline_value = section.getString("multiline_key", "");
        std.debug.print("multiline_key: {s}\n", .{multiline_value});
        if (section.section("sub")) |sub| {
            const sub_value = sub.getString("key", "");
            std.debug.print("section.sub.key: {s}\n", .{sub_value});
        }
    }
}
6 Likes