From 94f031c1989b516dec20b017b4a76ce115da3660 Mon Sep 17 00:00:00 2001 From: Altagos Date: Mon, 19 Jan 2026 20:49:46 +0100 Subject: [PATCH] pretty printing for primitive types --- example/main.zig | 48 ++++++++++++++ pretty.zig | 162 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) diff --git a/example/main.zig b/example/main.zig index e69de29..7c206d5 100644 --- a/example/main.zig +++ b/example/main.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const print = std.debug.print; + +const pretty_mod = @import("pretty"); +const pretty = pretty_mod.pretty; +const Pretty = pretty_mod.PrettyWithOptions; + +const Hello = enum { world }; + +pub fn main(init: std.process.Init) !void { + _ = init; + + print("Booleans\n", .{}); + print("Boolean true - {f}\n", .{pretty(true)}); + print( + "Boolean false - {f}\n", + .{Pretty(bool, .{ .show_type_names = false, .type_value_seperator = "" }).init(false)}, + ); + print( + "Boolean true with type name - {f}\n", + .{Pretty(bool, .{ .always_show_type_names = true, .type_value_seperator = ": " }).init(true)}, + ); + + print("\nUnsigned Integers\n", .{}); + print("Pretty u8 - {f}\n", .{pretty(@as(u8, 42))}); + print("Pretty u16 - {f}\n", .{pretty(@as(u16, 42))}); + print("Pretty u32 - {f}\n", .{pretty(@as(u32, 42))}); + print("Pretty u64 - {f}\n", .{pretty(@as(u64, 42))}); + print("Pretty usize - {f}\n", .{pretty(@as(usize, 42))}); + + print("\nSigned Integers\n", .{}); + print("Pretty comptime_int - {f}\n", .{pretty(42)}); + print("Pretty i8 - {f}\n", .{pretty(@as(i8, 42))}); + print("Pretty i16 - {f}\n", .{pretty(@as(i16, 42))}); + print("Pretty i32 - {f}\n", .{pretty(@as(i32, 42))}); + print("Pretty i64 - {f}\n", .{pretty(@as(i64, 42))}); + print("Pretty isize - {f}\n", .{pretty(@as(isize, 42))}); + + print("\nFloats\n", .{}); + print("Pretty comptime_float - {f}\n", .{pretty(3.131)}); + print("Pretty f16 - {f}\n", .{pretty(@as(f16, 3.141))}); + print("Pretty f32 - {f}\n", .{pretty(@as(f32, 3.141))}); + print("Pretty f64 - {f}\n", .{pretty(@as(f64, 3.141))}); + + print("\nEnums\n", .{}); + print("Pretty enum - {f}\n", .{pretty(Hello.world)}); + print("Pretty enum literal - {f}\n", .{pretty(.hello_world)}); +} diff --git a/pretty.zig b/pretty.zig index e69de29..f0abee6 100644 --- a/pretty.zig +++ b/pretty.zig @@ -0,0 +1,162 @@ +const std = @import("std"); +const Io = std.Io; + +pub const Options = struct { + indent_width: comptime_int = 2, + + show_type_names: bool = true, + always_show_type_names: bool = true, + + type_value_seperator: []const u8 = " = ", +}; + +pub fn pretty(value: anytype) Pretty(@TypeOf(value)) { + return Pretty(@TypeOf(value)).init(value); +} + +pub fn Pretty(comptime T: type) type { + return PrettyWithOptions(T, .{}); +} + +pub fn PrettyWithOptions(comptime T: type, comptime options: Options) type { + const global = struct { + var tty: ?Io.Terminal = 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.tty == null) { + var buffer: [1]u8 = undefined; + const stderr = std.debug.lockStderr(&buffer).terminal(); + defer std.debug.unlockStderr(); + + global.tty = stderr; + global.tty.?.writer = w; + } + + const comp_ctx = ComptimeContext{ .options = options }; + const run_ctx = RuntimeContext{ + .out = w, + .tty = global.tty.?, + }; + + return innerFmt(T, comp_ctx, this.value, run_ctx); + } + }; +} + +const RuntimeContext = struct { + out: *Io.Writer, + tty: Io.Terminal, + + depth: usize = 0, + + indent_level: usize = 0, + + pub fn print(this: RuntimeContext, comptime fmt: []const u8, args: anytype) error{WriteFailed}!void { + return this.out.print(fmt, args); + } + + pub fn setColor(this: RuntimeContext, color: Io.Terminal.Color) void { + this.tty.setColor(color) catch {}; + } + + pub fn resetColor(this: RuntimeContext) void { + this.tty.setColor(.reset) catch {}; + } +}; + +const ComptimeContext = struct { + depth: comptime_int = 0, + + indent_level: comptime_int = 0, + + options: Options, + exited_comptime: bool = false, + + pub fn inComptime(comptime this: ComptimeContext) bool { + if (!this.exited_comptime) return false; + return @inComptime(); + } +}; + +fn innerFmt( + comptime T: type, + comptime cctx: ComptimeContext, + value: T, + rctx: RuntimeContext, +) error{WriteFailed}!void { + const info = @typeInfo(T); + try printType(T, cctx, rctx, info); + + return switch (info) { + .bool => formatBool(rctx, value), + + .comptime_int, + .comptime_float, + .int, + .float, + .@"enum", + .enum_literal, + => formatValue(rctx, value), + + else => { + rctx.setColor(.red); + try rctx.print("Unimplemented! ({} = {any})", .{ info, value }); + rctx.resetColor(); + }, + }; +} + +inline fn printType( + comptime T: type, + comptime cctx: ComptimeContext, + rctx: RuntimeContext, + comptime info: std.builtin.Type, +) error{WriteFailed}!void { + if (cctx.depth != 0 or cctx.options.always_show_type_names) { + rctx.setColor(.dim); + + if (cctx.options.show_type_names) { + switch (info) { + .bool => try rctx.print("bool{s}", .{cctx.options.type_value_seperator}), + .comptime_int => try rctx.print("comptime_int{s}", .{cctx.options.type_value_seperator}), + .comptime_float => try rctx.print("comptime_float{s}", .{cctx.options.type_value_seperator}), + .int => |int| try rctx.print( + "{s}{}{s}", + .{ + if (int.signedness == .signed) "i" else "u", + int.bits, + cctx.options.type_value_seperator, + }, + ), + .float => |float| try rctx.print("f{}{s}", .{ float.bits, cctx.options.type_value_seperator }), + .enum_literal => try rctx.print("enum literal{s}", .{cctx.options.type_value_seperator}), + .@"enum" => try rctx.print("{s}{s}", .{ @typeName(T), cctx.options.type_value_seperator }), + else => try rctx.print("missing {}{s}", .{ info, cctx.options.type_value_seperator }), + } + } else { + try rctx.print("{s}", .{cctx.options.type_value_seperator}); + } + + rctx.resetColor(); + } +} + +inline fn formatBool(rctx: RuntimeContext, value: bool) !void { + rctx.setColor(if (value) .bright_green else .bright_red); + try rctx.print("{}", .{value}); + rctx.resetColor(); +} + +inline fn formatValue(rctx: RuntimeContext, value: anytype) !void { + rctx.setColor(.blue); + try rctx.print("{}", .{value}); + rctx.resetColor(); +} -- 2.51.2