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.

printValue

Asserts buffer capacity of at least 2 if value is a union.

Writer.printValue
pub fn printValue(
    w: *Writer,
    comptime fmt: []const u8,
    options: std.fmt.Options,
    value: anytype,
    max_depth: usize,
) Error!void

File

lib/std/Io/Writer.zig:1091

Code

pub fn printValue(
    w: *Writer,
    comptime fmt: []const u8,
    options: std.fmt.Options,
    value: anytype,
    max_depth: usize,
) Error!void {
    const T = @TypeOf(value);

    switch (fmt.len) {
        1 => switch (fmt[0]) {
            '*' => return w.printAddress(value),
            'f' => return value.format(w),
            'd' => switch (@typeInfo(T)) {
                .float, .comptime_float => return printFloat(w, value, options.toNumber(.decimal, .lower)),
                .int, .comptime_int => return printInt(w, value, 10, .lower, options),
                .@"struct" => return value.formatNumber(w, options.toNumber(.decimal, .lower)),
                .@"enum" => return printInt(w, @backingInt(value), 10, .lower, options),
                .vector => return printVector(w, fmt, options, value, max_depth),
                else => invalidFmtError(fmt, value),
            },
            'c' => return w.printAsciiChar(value, options),
            'u' => return w.printUnicodeCodepoint(value),
            'b' => switch (@typeInfo(T)) {
                .int, .comptime_int => return printInt(w, value, 2, .lower, options),
                .@"enum" => return printInt(w, @backingInt(value), 2, .lower, options),
                .@"struct" => return value.formatNumber(w, options.toNumber(.binary, .lower)),
                .vector => return printVector(w, fmt, options, value, max_depth),
                else => invalidFmtError(fmt, value),
            },
            'o' => switch (@typeInfo(T)) {
                .int, .comptime_int => return printInt(w, value, 8, .lower, options),
                .@"enum" => return printInt(w, @backingInt(value), 8, .lower, options),
                .@"struct" => return value.formatNumber(w, options.toNumber(.octal, .lower)),
                .vector => return printVector(w, fmt, options, value, max_depth),
                else => invalidFmtError(fmt, value),
            },
            'x' => switch (@typeInfo(T)) {
                .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
                .int, .comptime_int => return printInt(w, value, 16, .lower, options),
                .@"enum" => return printInt(w, @backingInt(value), 16, .lower, options),
                .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .lower)),
                .pointer => |info| switch (info.size) {
                    .one, .slice => {
                        const slice: []const u8 = value;
                        optionsForbidden(options);
                        return printHex(w, slice, .lower);
                    },
                    .many, .c => {
                        const slice: [:0]const u8 = std.mem.span(value);
                        optionsForbidden(options);
                        return printHex(w, slice, .lower);
                    },
                },
                .array => {
                    const slice: []const u8 = &value;
                    optionsForbidden(options);
                    return printHex(w, slice, .lower);
                },
                .vector => return printVector(w, fmt, options, value, max_depth),
                else => invalidFmtError(fmt, value),
            },
            'X' => switch (@typeInfo(T)) {
                .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .upper)),
                .int, .comptime_int => return printInt(w, value, 16, .upper, options),
                .@"enum" => return printInt(w, @backingInt(value), 16, .upper, options),
                .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .upper)),
                .pointer => |info| switch (info.size) {
                    .one, .slice => {
                        const slice: []const u8 = value;
                        optionsForbidden(options);
                        return printHex(w, slice, .upper);
                    },
                    .many, .c => {
                        const slice: [:0]const u8 = std.mem.span(value);
                        optionsForbidden(options);
                        return printHex(w, slice, .upper);
                    },
                },
                .array => {
                    const slice: []const u8 = &value;
                    optionsForbidden(options);
                    return printHex(w, slice, .upper);
                },
                .vector => return printVector(w, fmt, options, value, max_depth),
                else => invalidFmtError(fmt, value),
            },
            's' => switch (@typeInfo(T)) {
                .pointer => |info| switch (info.size) {
                    .one, .slice => {
                        const slice: []const u8 = value;
                        return w.alignBufferOptions(slice, options);
                    },
                    .many, .c => {
                        const slice: [:0]const u8 = std.mem.span(value);
                        return w.alignBufferOptions(slice, options);
                    },
                },
                .array => {
                    const slice: []const u8 = &value;
                    return w.alignBufferOptions(slice, options);
                },
                else => invalidFmtError(fmt, value),
            },
            'q' => switch (@typeInfo(T)) {
                .pointer => |info| switch (info.size) {
                    .one, .slice => return printStringEscaped(w, value),
                    .many, .c => return printStringEscaped(w, std.mem.span(value)),
                },
                .array => return printStringEscaped(w, &value),
                else => invalidFmtError(fmt, value),
            },
            'B' => switch (@typeInfo(T)) {
                .int, .comptime_int => return w.printByteSize(value, .decimal, options),
                .@"struct" => return value.formatByteSize(w, .decimal),
                else => invalidFmtError(fmt, value),
            },
            'e' => switch (@typeInfo(T)) {
                .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)),
                .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)),
                else => invalidFmtError(fmt, value),
            },
            'E' => switch (@typeInfo(T)) {
                .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .upper)),
                .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .upper)),
                else => invalidFmtError(fmt, value),
            },
            't' => switch (@typeInfo(T)) {
                .error_set => return w.alignBufferOptions(@errorName(value), options),
                .@"enum", .enum_literal, .@"union" => return w.alignBufferOptions(@tagName(value), options),
                else => invalidFmtError(fmt, value),
            },
            else => {},
        },
        2 => switch (fmt[0]) {
            'B' => switch (fmt[1]) {
                'i' => switch (@typeInfo(T)) {
                    .int, .comptime_int => return w.printByteSize(value, .binary, options),
                    .@"struct" => return value.formatByteSize(w, .binary),
                    else => invalidFmtError(fmt, value),
                },
                else => {},
            },
            else => {},
        },
        3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') switch (@typeInfo(T)) {
            .pointer => |info| switch (info.size) {
                .one, .slice => {
                    const slice: []const u8 = value;
                    optionsForbidden(options);
                    return w.printBase64(slice);
                },
                .many, .c => {
                    const slice: [:0]const u8 = std.mem.span(value);
                    optionsForbidden(options);
                    return w.printBase64(slice);
                },
            },
            .array => {
                const slice: []const u8 = &value;
                optionsForbidden(options);
                return w.printBase64(slice);
            },
            else => invalidFmtError(fmt, value),
        },
        else => {},
    }

    const is_any = comptime std.mem.eql(u8, fmt, ANY);

    switch (@typeInfo(T)) {
        .float, .comptime_float => {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            return printFloat(w, value, options.toNumber(.decimal, .lower));
        },
        .int, .comptime_int => {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            return printInt(w, value, 10, .lower, options);
        },
        .bool => {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            const string: []const u8 = if (value) "true" else "false";
            return w.alignBufferOptions(string, options);
        },
        .void => {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            return w.alignBufferOptions("void", options);
        },
        .optional => {
            const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '?')
                stripOptionalOrErrorUnionSpec(fmt)
            else if (is_any)
                ANY
            else
                @compileError("cannot print optional without a specifier (i.e. {?} or {any})");
            if (value) |payload| {
                return w.printValue(remaining_fmt, options, payload, max_depth);
            } else {
                return w.alignBufferOptions("null", options);
            }
        },
        .error_union => {
            const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '!')
                stripOptionalOrErrorUnionSpec(fmt)
            else if (is_any)
                ANY
            else
                @compileError("cannot print error union without a specifier (i.e. {!} or {any})");
            if (value) |payload| {
                return w.printValue(remaining_fmt, options, payload, max_depth);
            } else |err| {
                return w.printValue("", options, err, max_depth);
            }
        },
        .error_set => {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            optionsForbidden(options);
            return printErrorSet(w, value);
        },
        .@"enum" => |info| {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            optionsForbidden(options);
            if (info.mode == .exhaustive) {
                return printEnumExhaustive(w, value);
            } else {
                return printEnumNonexhaustive(w, value);
            }
        },
        .@"union" => |info| {
            if (!is_any) {
                if (fmt.len != 0) invalidFmtError(fmt, value);
                return printValue(w, ANY, options, value, max_depth);
            }
            if (max_depth == 0) {
                try w.writeAll(".{ ... }");
                return;
            }
            if (info.tag_type) |UnionTagType| {
                try w.writeAll(".{ .");
                try w.writeAll(@tagName(@as(UnionTagType, value)));
                try w.writeAll(" = ");
                inline for (info.field_names) |u_field_name| {
                    if (value == @field(UnionTagType, u_field_name)) {
                        try w.printValue(ANY, options, @field(value, u_field_name), max_depth - 1);
                    }
                }
                try w.writeAll(" }");
            } else switch (info.layout) {
                .auto => {
                    return w.writeAll(".{ ... }");
                },
                .@"extern", .@"packed" => {
                    if (info.field_names.len == 0) return w.writeAll(".{}");
                    try w.writeAll(".{ ");
                    inline for (info.field_names, 1..) |field_name, i| {
                        try w.writeByte('.');
                        try w.writeAll(field_name);
                        try w.writeAll(" = ");
                        try w.printValue(ANY, options, @field(value, field_name), max_depth - 1);
                        try w.writeAll(if (i < info.field_names.len) ", " else " }");
                    }
                },
            }
        },
        .@"struct" => |info| {
            if (!is_any) {
                if (fmt.len != 0) invalidFmtError(fmt, value);
                return printValue(w, ANY, options, value, max_depth);
            }
            if (info.is_tuple) {
                // Skip the type and field names when formatting tuples.
                if (max_depth == 0) {
                    try w.writeAll(".{ ... }");
                    return;
                }
                try w.writeAll(".{");
                inline for (info.field_names, 0..) |f_name, i| {
                    if (i == 0) {
                        try w.writeAll(" ");
                    } else {
                        try w.writeAll(", ");
                    }
                    try w.printValue(ANY, options, @field(value, f_name), max_depth - 1);
                }
                try w.writeAll(" }");
                return;
            }
            if (max_depth == 0) {
                try w.writeAll(".{ ... }");
                return;
            }
            try w.writeAll(".{");
            inline for (info.field_names, 0..) |f_name, i| {
                if (i == 0) {
                    try w.writeAll(" .");
                } else {
                    try w.writeAll(", .");
                }
                try w.writeAll(f_name);
                try w.writeAll(" = ");
                try w.printValue(ANY, options, @field(value, f_name), max_depth - 1);
            }
            try w.writeAll(" }");
        },
        .pointer => |ptr_info| switch (ptr_info.size) {
            .one => switch (@typeInfo(ptr_info.child)) {
                .array => |array_info| return w.printValue(fmt, options, @as([]const array_info.child, value), max_depth),
                .@"enum", .@"union", .@"struct" => return w.printValue(fmt, options, value.*, max_depth),
                else => {
                    var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };
                    try w.writeVecAll(&buffers);
                    try w.printInt(@intFromPtr(value), 16, .lower, options);
                    return;
                },
            },
            .many, .c => {
                if (!is_any) @compileError("cannot format pointer without a specifier (i.e. {s} or {*})");
                optionsForbidden(options);
                try w.printAddress(value);
            },
            .slice => {
                if (!is_any)
                    @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})");
                if (max_depth == 0) return w.writeAll("{ ... }");
                try w.writeAll("{ ");
                for (value, 0..) |elem, i| {
                    try w.printValue(fmt, options, elem, max_depth - 1);
                    if (i != value.len - 1) {
                        try w.writeAll(", ");
                    }
                }
                try w.writeAll(" }");
            },
        },
        .array => {
            if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})");
            return printArray(w, fmt, options, &value, max_depth);
        },
        .vector => |vector| {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            const array: [vector.len]vector.child = value;
            return printArray(w, fmt, options, &array, max_depth);
        },
        .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"),
        .type => {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            return w.alignBufferOptions(@typeName(value), options);
        },
        .enum_literal => {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            optionsForbidden(options);
            var vecs: [2][]const u8 = .{ ".", @tagName(value) };
            return w.writeVecAll(&vecs);
        },
        .null => {
            if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
            return w.alignBufferOptions("null", options);
        },
        else => @compileError("unable to format type '" ++ @typeName(T) ++ "'"),
    }
}