From 0a2b039f28194edb26daf71177b62b646a4236cd Mon Sep 17 00:00:00 2001 From: Altagos Date: Sat, 8 Nov 2025 17:59:36 +0100 Subject: [PATCH] create custom pretty printer --- build.zig | 9 + src/meta/fmt.zig | 453 ++++++++++++++++++++++++++++++++++++++++++++++ src/meta/root.zig | 3 + 3 files changed, 465 insertions(+) create mode 100644 src/meta/fmt.zig create mode 100644 src/meta/root.zig diff --git a/build.zig b/build.zig index 15e4a3c..02d0d5f 100644 --- a/build.zig +++ b/build.zig @@ -7,6 +7,13 @@ pub fn build(b: *std.Build) void { const options = b.addOptions(); const known_folders = b.dependency("known_folders", .{}).module("known-folders"); + + const meta = b.createModule(.{ + .root_source_file = b.path("src/meta/root.zig"), + .target = target, + .optimize = optimize, + }); + const st = b.addModule("st", .{ .root_source_file = b.path("src/st/root.zig"), .target = target, @@ -31,8 +38,10 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .imports = &.{ .{ .name = "known-folders", .module = known_folders }, + // Modules .{ .name = "st", .module = st }, .{ .name = "agent", .module = agent }, + .{ .name = "meta", .module = meta }, }, }); diff --git a/src/meta/fmt.zig b/src/meta/fmt.zig new file mode 100644 index 0000000..d540f1a --- /dev/null +++ b/src/meta/fmt.zig @@ -0,0 +1,453 @@ +const std = @import("std"); +const Io = std.Io; +const Type = std.builtin.Type; + +pub fn pretty(value: anytype) Pretty(@TypeOf(value)) { + return Pretty(@TypeOf(value)).init(value); +} + +fn Context(comptime runtime: bool) type { + if (runtime) { + return struct { + indent_width: u8 = 2, + indent_level: usize = 0, + }; + } else { + return struct { + indent_width: comptime_int = 2, + indent_level: comptime_int = 0, + }; + } +} + +const Options = struct { + type_name: bool = true, + last_layer_type_name: bool = true, + + follow_pointers: bool = true, +}; + +pub fn Pretty(comptime T: type) type { + const global = struct { + var conf: ?std.Io.tty.Config = null; + }; + return struct { + value: T, + pub fn init(val: T) @This() { + return .{ .value = val }; + } + pub fn format(this: *const @This(), w: *std.Io.Writer) error{WriteFailed}!void { + if (global.conf == null) { + global.conf = .detect(.stderr()); + } + return innerFmt(w, global.conf.?, T, this.value, false, .{}, .{}); + } + }; +} + +const Color = enum { + dim, + reset, + text, + field, + value, +}; + +fn setColor( + w: *Io.Writer, + tty: Io.tty.Config, + color: Color, +) void { + switch (color) { + .dim => { + tty.setColor(w, .dim) catch {}; + }, + .reset => { + tty.setColor(w, .reset) catch {}; + }, + .text => { + tty.setColor(w, .italic) catch {}; + tty.setColor(w, .dim) catch {}; + tty.setColor(w, .blue) catch {}; + }, + .field => { + tty.setColor(w, .green) catch {}; + }, + .value => { + tty.setColor(w, .italic) catch {}; + tty.setColor(w, .blue) catch {}; + }, + } +} + +fn innerFmt( + w: *std.Io.Writer, + tty: std.Io.tty.Config, + comptime T: type, + value: T, + comptime runtime: bool, + ctx: Context(runtime), + opts: Options, +) error{WriteFailed}!void { + const info = @typeInfo(T); + + switch (info) { + .bool => try printBool(w, tty, value, opts), + .int => |int| try printInt(w, tty, int, value, opts), + .float => |float| try printFloat(w, tty, float, value, opts), + .@"enum", .enum_literal => try printEnum(w, tty, value, opts), + .optional => |optional| try printOptional(w, tty, optional, value, runtime, ctx, opts), + .pointer => |ptr| { + if (opts.follow_pointers) { + try printPointer(w, tty, ptr, value, runtime, ctx, opts); + } else {} + }, + .array => |array| try printArray(w, tty, array, value, runtime, ctx, opts), + .@"struct" => |str| try printStruct(T, w, tty, str, value, runtime, ctx, opts), + else => { + tty.setColor(w, .red) catch {}; + try w.print("Unimplemented! ({})", .{info}); + setColor(w, tty, .reset); + }, + } +} + +fn printBool( + w: *Io.Writer, + tty: Io.tty.Config, + value: bool, + opts: Options, +) !void { + if (opts.type_name) { + setColor(w, tty, .dim); + try w.print("bool => ", .{}); + setColor(w, tty, .reset); + } else { + tty.setColor(w, .dim) catch {}; + try w.print("=> ", .{}); + setColor(w, tty, .reset); + } + setColor(w, tty, .value); + if (value) { + tty.setColor(w, .bright_green) catch {}; + } else { + tty.setColor(w, .bright_red) catch {}; + } + try w.print("{}", .{value}); + setColor(w, tty, .reset); +} + +fn printInt( + w: *Io.Writer, + tty: Io.tty.Config, + int: Type.Int, + value: anytype, + opts: Options, +) !void { + if (opts.type_name) { + tty.setColor(w, .dim) catch {}; + try w.print("{s}{} => ", .{ if (int.signedness == .signed) "i" else "u", int.bits }); + setColor(w, tty, .reset); + } else { + tty.setColor(w, .dim) catch {}; + try w.print("=> ", .{}); + setColor(w, tty, .reset); + } + setColor(w, tty, .value); + try w.print("{}", .{value}); + setColor(w, tty, .reset); +} + +fn printFloat( + w: *Io.Writer, + tty: Io.tty.Config, + float: Type.Float, + value: anytype, + opts: Options, +) !void { + if (opts.type_name) { + tty.setColor(w, .dim) catch {}; + try w.print("f{} => ", .{float.bits}); + setColor(w, tty, .reset); + } else { + tty.setColor(w, .dim) catch {}; + try w.print("=> ", .{}); + setColor(w, tty, .reset); + } + setColor(w, tty, .value); + try w.print("{}", .{value}); + setColor(w, tty, .reset); +} + +fn printEnum( + w: *Io.Writer, + tty: Io.tty.Config, + value: anytype, + opts: Options, +) !void { + if (opts.type_name) { + tty.setColor(w, .dim) catch {}; + try w.print("{s} => ", .{@typeName(@TypeOf(value))}); + setColor(w, tty, .reset); + } else { + tty.setColor(w, .dim) catch {}; + try w.print("=> ", .{}); + setColor(w, tty, .reset); + } + setColor(w, tty, .value); + try w.print("{}", .{value}); + setColor(w, tty, .reset); +} + +fn printOptional( + w: *Io.Writer, + tty: Io.tty.Config, + optional: Type.Optional, + value: anytype, + comptime runtime: bool, + ctx: Context(runtime), + opts: Options, +) !void { + var next_opts = opts; + next_opts.type_name = if (!opts.type_name and !opts.last_layer_type_name) false else true; + next_opts.last_layer_type_name = opts.type_name; + + if (opts.type_name) { + tty.setColor(w, .dim) catch {}; + try w.print("?", .{}); + setColor(w, tty, .reset); + } + + if (value) |val| { + try innerFmt(w, tty, optional.child, val, runtime, ctx, next_opts); + } else { + if (opts.type_name) { + tty.setColor(w, .dim) catch {}; + try w.print("{s} => ", .{@typeName(optional.child)}); + setColor(w, tty, .reset); + } else { + tty.setColor(w, .dim) catch {}; + try w.print("=> ", .{}); + setColor(w, tty, .reset); + } + + tty.setColor(w, .blue) catch {}; + try w.writeAll("null"); + setColor(w, tty, .reset); + } +} + +fn printArray( + w: *Io.Writer, + tty: Io.tty.Config, + array: Type.Array, + value: anytype, + comptime runtime: bool, + ctx: Context(runtime), + opts: Options, +) !void { + comptime var next_ctx = ctx; + + var next_opts = opts; + next_opts.type_name = if (!opts.type_name and !opts.last_layer_type_name) false else true; + next_opts.last_layer_type_name = opts.type_name; + + if (opts.type_name) { + tty.setColor(w, .dim) catch {}; + try w.print("[{}]{s} =>", .{ array.len, @typeName(array.child) }); + setColor(w, tty, .reset); + } else { + tty.setColor(w, .dim) catch {}; + try w.print("=> ", .{}); + setColor(w, tty, .reset); + } + + inline for (value, 0..) |item, idx| { + try indent(w, runtime, ctx); + + setColor(w, tty, .field); + try w.print("[{}] ", .{idx}); + setColor(w, tty, .reset); + + next_ctx.indent_level = ctx.indent_level + 1; + next_opts.type_name = false; + + try innerFmt(w, tty, @TypeOf(item), item, runtime, next_ctx, next_opts); + } +} + +fn printPointer( + w: *Io.Writer, + tty: Io.tty.Config, + ptr: Type.Pointer, + value: anytype, + comptime runtime: bool, + ctx: Context(runtime), + opts: Options, +) !void { + var next_opts = opts; + next_opts.type_name = if (!opts.type_name and !opts.last_layer_type_name) false else true; + next_opts.last_layer_type_name = opts.type_name; + + switch (ptr.size) { + .one => { + if (opts.type_name) { + tty.setColor(w, .dim) catch {}; + try w.print("*{s}", .{if (ptr.is_const) "const " else ""}); + setColor(w, tty, .reset); + } + + if (ptr.child == anyopaque or @typeInfo(ptr.child) == .@"fn") + return; + + try innerFmt(w, tty, ptr.child, value, runtime, ctx, next_opts); + }, + .slice => { + if (opts.type_name) { + setColor(w, tty, .dim); + try w.print("[]{s}", .{if (ptr.is_const) "const " else ""}); + setColor(w, tty, .reset); + } + + if (ptr.child == u8) { + tty.setColor(w, .dim) catch {}; + try w.print("{s}=>", .{if (opts.type_name) "u8 " else ""}); + setColor(w, tty, .reset); + + try indent(w, runtime, ctx); + + setColor(w, tty, .text); + try w.print("\"{s}\"", .{value}); + setColor(w, tty, .reset); + return; + } + + setColor(w, tty, .dim); + try w.print("{s}{s}", .{ + if (opts.type_name) @typeName(ptr.child) else "", + if (opts.type_name) " =>" else "=>", + }); + setColor(w, tty, .reset); + + const run_ctx: Context(true) = .{ .indent_width = ctx.indent_width, .indent_level = ctx.indent_level }; + var next_ctx: Context(true) = .{ .indent_width = ctx.indent_width, .indent_level = ctx.indent_level }; + + var count: usize = 0; + for (value, 0..) |item, idx| { + try indent(w, true, run_ctx); + + setColor(w, tty, .field); + try w.print("[{}] ", .{idx}); + setColor(w, tty, .reset); + + next_ctx.indent_level = ctx.indent_level + 1; + next_opts.type_name = false; + + try innerFmt(w, tty, @TypeOf(item), item, true, next_ctx, next_opts); + count += 1; + } + + if (count == 0) { + setColor(w, tty, .dim); + try w.writeAll(" empty"); + setColor(w, tty, .reset); + } + }, + else => { + try w.print("unimplemented {}", .{ptr}); + }, + } +} + +fn printStruct( + comptime T: type, + w: *Io.Writer, + tty: Io.tty.Config, + str: Type.Struct, + value: anytype, + comptime runtime: bool, + ctx: Context(runtime), + opts: Options, +) !void { + var next_opts = opts; + next_opts.type_name = if (!opts.type_name and !opts.last_layer_type_name) false else true; + next_opts.last_layer_type_name = opts.type_name; + + setColor(w, tty, .dim); + if (opts.type_name) { + try w.print("{s} =>", .{@typeName(T)}); + } else { + try w.print("=>", .{}); + } + setColor(w, tty, .reset); + + if (runtime) { + var next_ctx: Context(true) = ctx; + + inline for (str.fields) |field| { + try indent(w, runtime, ctx); + + setColor(w, tty, .field); + try w.print(".{s}", .{field.name}); + setColor(w, tty, .reset); + + setColor(w, tty, .dim); + if (opts.type_name) { + try w.writeAll(": "); + } else try w.writeByte(' '); + setColor(w, tty, .reset); + + next_ctx.indent_level = ctx.indent_level + 1; + + try innerFmt( + w, + tty, + field.type, + @field(value, field.name), + runtime, + next_ctx, + next_opts, + ); + } + } else { + comptime var next_ctx: Context(false) = ctx; + + inline for (str.fields) |field| { + try indent(w, runtime, ctx); + + setColor(w, tty, .field); + try w.print(".{s}", .{field.name}); + setColor(w, tty, .reset); + + setColor(w, tty, .dim); + if (opts.type_name) { + try w.writeAll(": "); + } else try w.writeByte(' '); + setColor(w, tty, .reset); + + next_ctx.indent_level = ctx.indent_level + 1; + + try innerFmt( + w, + tty, + field.type, + @field(value, field.name), + runtime, + next_ctx, + next_opts, + ); + } + } +} + +fn indent(w: *Io.Writer, comptime runtime: bool, ctx: Context(runtime)) !void { + try w.writeByte('\n'); + if (runtime) { + for (0..((ctx.indent_level + 1) * ctx.indent_width)) |_| { + try w.writeByte(' '); + } + } else { + const text: [(ctx.indent_level + 1) * ctx.indent_width]u8 = @splat(' '); + try w.writeAll(&text); + } +} diff --git a/src/meta/root.zig b/src/meta/root.zig new file mode 100644 index 0000000..c0ab7dd --- /dev/null +++ b/src/meta/root.zig @@ -0,0 +1,3 @@ +pub const fmt = @import("fmt.zig"); + +pub const pretty = fmt.pretty; -- 2.51.2