Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

expandVariablesCmake

ConfigHeader.expandVariablesCmake
fn expandVariablesCmake(
    arena: Allocator,
    contents: []const u8,
    conf: *const Configuration,
    value_pairs: []const Value.Pair,
    value_map: *const ValueMap,
) ![]const u8

File

lib/compiler/Maker/Step/ConfigHeader.zig:507

Code

fn expandVariablesCmake(
    arena: Allocator,
    contents: []const u8,
    conf: *const Configuration,
    value_pairs: []const Value.Pair,
    value_map: *const ValueMap,
) ![]const u8 {
    var result: std.ArrayList(u8) = .empty;

    const valid_varname_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/_.+-";
    const open_var = "${";

    var curr: usize = 0;
    var source_offset: usize = 0;
    const Position = struct {
        source: usize,
        target: usize,
    };
    var var_stack: std.ArrayList(Position) = .empty;
    loop: while (curr < contents.len) : (curr += 1) {
        switch (contents[curr]) {
            '@' => blk: {
                if (std.mem.findScalarPos(u8, contents, curr + 1, '@')) |close_pos| {
                    if (close_pos == curr + 1) {
                        // closed immediately, preserve as a literal
                        break :blk;
                    }
                    const valid_varname_end = std.mem.findNonePos(u8, contents, curr + 1, valid_varname_chars) orelse 0;
                    if (valid_varname_end != close_pos) {
                        // contains invalid characters, preserve as a literal
                        break :blk;
                    }

                    const key = contents[curr + 1 .. close_pos];
                    const index = value_map.getIndex(key) orelse return error.MissingValue;
                    value_map.values()[index] = true; // Mark as used.
                    const value = value_pairs[index].index;
                    const missing = contents[source_offset..curr];
                    try result.appendSlice(arena, missing);
                    switch (value.unpack(conf)) {
                        .undef, .defined => {},
                        .bool => |b| try result.append(arena, if (b) '1' else '0'),
                        inline .i64, .u64 => |i| try result.print(arena, "{d}", .{i}),
                        .ident, .string => |s| try result.appendSlice(arena, s),
                    }

                    curr = close_pos;
                    source_offset = close_pos + 1;

                    continue :loop;
                }
            },
            '$' => blk: {
                const next = curr + 1;
                if (next == contents.len or contents[next] != '{') {
                    // no open bracket detected, preserve as a literal
                    break :blk;
                }
                const missing = contents[source_offset..curr];
                try result.appendSlice(arena, missing);
                try result.appendSlice(arena, open_var);

                source_offset = curr + open_var.len;
                curr = next;
                try var_stack.append(arena, .{
                    .source = curr,
                    .target = result.items.len - open_var.len,
                });

                continue :loop;
            },
            '}' => blk: {
                if (var_stack.items.len == 0) {
                    // no open bracket, preserve as a literal
                    break :blk;
                }
                const open_pos = var_stack.pop().?;
                if (source_offset == open_pos.source) {
                    source_offset += open_var.len;
                }
                const missing = contents[source_offset..curr];
                try result.appendSlice(arena, missing);

                const key_start = open_pos.target + open_var.len;
                const key = result.items[key_start..];
                if (key.len == 0) {
                    return error.MissingKey;
                }
                const index = value_map.getIndex(key) orelse return error.MissingValue;
                value_map.values()[index] = true; // Mark as used.
                const value = value_pairs[index].index;
                result.shrinkRetainingCapacity(result.items.len - key.len - open_var.len);
                switch (value.unpack(conf)) {
                    .undef, .defined => {},
                    .bool => |b| try result.append(arena, if (b) '1' else '0'),
                    inline .i64, .u64 => |i| try result.print(arena, "{d}", .{i}),
                    .ident, .string => |s| try result.appendSlice(arena, s),
                }

                source_offset = curr + 1;

                continue :loop;
            },
            '\\' => {
                // backslash is not considered a special character
                continue :loop;
            },
            else => {},
        }

        if (var_stack.items.len > 0 and std.mem.findScalar(u8, valid_varname_chars, contents[curr]) == null) {
            return error.InvalidCharacter;
        }
    }

    if (source_offset != contents.len) {
        const missing = contents[source_offset..];
        try result.appendSlice(arena, missing);
    }

    try result.shrinkToLen(arena);

    return result.toOwnedSliceAssert();
}