173 lines
6.2 KiB
Zig
173 lines
6.2 KiB
Zig
const std = @import("std");
|
|
|
|
/// Properly format a slice of numbers.
|
|
pub fn slice(any_slice: anytype) FmtSlice(std.meta.Elem(@TypeOf(any_slice))) {
|
|
return .{ .slice = any_slice };
|
|
}
|
|
|
|
fn FmtSlice(T: type) type {
|
|
return struct {
|
|
slice: []const T,
|
|
|
|
pub fn formatNumber(f: @This(), writer: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
|
|
return switch (@typeInfo(T)) {
|
|
.comptime_float, .float => try formatFloatSlice(f.slice, n, writer),
|
|
.comptime_int, .int => try formatIntSlice(f.slice, n, writer),
|
|
.bool => try formatBoolSlice(f.slice, n, writer),
|
|
.@"struct" => if (@hasField(T, "re") and @hasField(T, "im")) {
|
|
try formatComplexSlice(f.slice, n, writer);
|
|
} else if (@hasDecl(T, "toF32")) {
|
|
try formatFloatSlice(f.slice, n, writer);
|
|
} else {
|
|
try formatSliceAny(f.slice, n, writer);
|
|
},
|
|
else => @compileError("FmtSlice doesn't support type: " ++ @typeName(T)),
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn formatFloat(value: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
const x = switch (@typeInfo(@TypeOf(value))) {
|
|
.@"struct" => value.toF32(),
|
|
.float => value,
|
|
else => @compileError("formatFloat expects a float, got: " ++ @typeName(@TypeOf(value))),
|
|
};
|
|
return writer.printFloat(x, spec);
|
|
}
|
|
|
|
pub fn formatInt(value: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
switch (@typeInfo(@TypeOf(value))) {
|
|
.int => {},
|
|
else => @compileError("formatInt expects an int, got: " ++ @typeName(@TypeOf(value))),
|
|
}
|
|
return writer.printInt(value, spec.mode.base().?, spec.case, .{ .alignment = spec.alignment, .fill = spec.fill });
|
|
}
|
|
|
|
pub fn formatComplex(value: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
try writer.writeAll(".{.re=");
|
|
try writer.printFloat(value.re, spec);
|
|
try writer.writeAll(", .im=");
|
|
try writer.printFloat(value.im, spec);
|
|
try writer.writeAll("}");
|
|
}
|
|
|
|
pub fn formatBool(value: bool, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
try writer.alignBufferOptions(if (value) "1" else "0", .{ .alignment = spec.alignment, .fill = spec.fill });
|
|
}
|
|
|
|
pub fn formatAny(value: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
var buf: [48]u8 = undefined;
|
|
const T = @TypeOf(value);
|
|
const fmt = if (@hasDecl(T, "formatNumber")) "{d}" else "{f}";
|
|
|
|
const s = std.fmt.bufPrint(&buf, fmt, .{value}) catch blk: {
|
|
buf[45..].* = "...".*;
|
|
break :blk buf[0..];
|
|
};
|
|
return try writer.alignBufferOptions(s, .{ .alignment = spec.alignment, .fill = spec.fill });
|
|
}
|
|
|
|
pub fn formatSliceCustom(fmt_func: anytype, values: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
// use the format "width" for the number of columns instead of individual width.
|
|
const num_cols: usize = spec.width orelse 12;
|
|
var my_options = spec;
|
|
my_options.width = null;
|
|
const n: usize = values.len;
|
|
|
|
_ = try writer.write("{");
|
|
if (n <= num_cols) {
|
|
for (values, 0..) |v, i| {
|
|
// Force inlining so that the switch and the buffer can be done once.
|
|
try @call(.always_inline, fmt_func, .{ v, my_options, writer });
|
|
if (i < n - 1) _ = try writer.write(",");
|
|
}
|
|
} else {
|
|
const half = @divFloor(num_cols, 2);
|
|
for (values[0..half]) |v| {
|
|
try @call(.always_inline, fmt_func, .{ v, my_options, writer });
|
|
_ = try writer.write(",");
|
|
}
|
|
_ = try writer.write(" ..., ");
|
|
for (values[n - half ..], 0..) |v, i| {
|
|
try @call(.always_inline, fmt_func, .{ v, my_options, writer });
|
|
if (i < half - 1) _ = try writer.write(",");
|
|
}
|
|
}
|
|
_ = try writer.write("}");
|
|
}
|
|
|
|
pub fn formatSliceAny(values: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
return try formatSliceCustom(formatAny, values, spec, writer);
|
|
}
|
|
|
|
pub fn formatFloatSlice(values: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
return try formatSliceCustom(formatFloat, values, spec, writer);
|
|
}
|
|
|
|
pub fn formatIntSlice(values: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
return try formatSliceCustom(formatInt, values, spec, writer);
|
|
}
|
|
|
|
pub fn formatComplexSlice(values: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
return try formatSliceCustom(formatComplex, values, spec, writer);
|
|
}
|
|
|
|
pub fn formatBoolSlice(values: anytype, spec: std.fmt.Number, writer: *std.Io.Writer) !void {
|
|
return try formatSliceCustom(formatBool, values, spec, writer);
|
|
}
|
|
|
|
/// Format a struct using `format` method of subfields when possible.
|
|
pub fn any(any_val: anytype) FmtAny(@TypeOf(any_val)) {
|
|
return .{ .data = any_val };
|
|
}
|
|
|
|
fn FmtAny(Data: type) type {
|
|
return struct {
|
|
data: Data,
|
|
|
|
pub inline fn format(self: @This(), writer: *std.io.Writer) std.io.Writer.Error!void {
|
|
try printValue(writer, .{}, self.data, std.options.fmt_max_depth);
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Fix up of std.io.Writer.printValue that uses `format` method of subfields when possible
|
|
fn printValue(
|
|
w: *std.io.Writer,
|
|
options: std.fmt.Options,
|
|
value: anytype,
|
|
max_depth: usize,
|
|
) std.io.Writer.Error!void {
|
|
const T = @TypeOf(value);
|
|
if (std.meta.hasMethod(T, "format")) {
|
|
return try value.format(w);
|
|
}
|
|
if (std.meta.hasMethod(T, "formatNumber")) {
|
|
return try value.formatNumber(w, options.toNumber(.decimal, .lower));
|
|
}
|
|
|
|
if (max_depth == 0) {
|
|
try w.writeAll(".{ ... }");
|
|
return;
|
|
}
|
|
|
|
switch (@typeInfo(T)) {
|
|
.@"struct" => |info| {
|
|
try w.writeAll(".{ ");
|
|
inline for (info.fields, 0..) |f, i| {
|
|
if (i > 0) try w.writeAll(", ");
|
|
|
|
if (!info.is_tuple) {
|
|
try w.writeByte('.');
|
|
try w.writeAll(f.name);
|
|
try w.writeAll(" = ");
|
|
}
|
|
try printValue(w, options, @field(value, f.name), max_depth - 1);
|
|
}
|
|
try w.writeAll(" }");
|
|
},
|
|
inline else => try w.printValue("any", options, value, max_depth - 1),
|
|
}
|
|
}
|