A pretty printer for zig
zig

pretty printing for primitive types

altagos.dev 94f031c1 d3a0edca

verified
+210
+48
example/main.zig
··· 1 + const std = @import("std"); 2 + const print = std.debug.print; 3 + 4 + const pretty_mod = @import("pretty"); 5 + const pretty = pretty_mod.pretty; 6 + const Pretty = pretty_mod.PrettyWithOptions; 7 + 8 + const Hello = enum { world }; 9 + 10 + pub fn main(init: std.process.Init) !void { 11 + _ = init; 12 + 13 + print("Booleans\n", .{}); 14 + print("Boolean true - {f}\n", .{pretty(true)}); 15 + print( 16 + "Boolean false - {f}\n", 17 + .{Pretty(bool, .{ .show_type_names = false, .type_value_seperator = "" }).init(false)}, 18 + ); 19 + print( 20 + "Boolean true with type name - {f}\n", 21 + .{Pretty(bool, .{ .always_show_type_names = true, .type_value_seperator = ": " }).init(true)}, 22 + ); 23 + 24 + print("\nUnsigned Integers\n", .{}); 25 + print("Pretty u8 - {f}\n", .{pretty(@as(u8, 42))}); 26 + print("Pretty u16 - {f}\n", .{pretty(@as(u16, 42))}); 27 + print("Pretty u32 - {f}\n", .{pretty(@as(u32, 42))}); 28 + print("Pretty u64 - {f}\n", .{pretty(@as(u64, 42))}); 29 + print("Pretty usize - {f}\n", .{pretty(@as(usize, 42))}); 30 + 31 + print("\nSigned Integers\n", .{}); 32 + print("Pretty comptime_int - {f}\n", .{pretty(42)}); 33 + print("Pretty i8 - {f}\n", .{pretty(@as(i8, 42))}); 34 + print("Pretty i16 - {f}\n", .{pretty(@as(i16, 42))}); 35 + print("Pretty i32 - {f}\n", .{pretty(@as(i32, 42))}); 36 + print("Pretty i64 - {f}\n", .{pretty(@as(i64, 42))}); 37 + print("Pretty isize - {f}\n", .{pretty(@as(isize, 42))}); 38 + 39 + print("\nFloats\n", .{}); 40 + print("Pretty comptime_float - {f}\n", .{pretty(3.131)}); 41 + print("Pretty f16 - {f}\n", .{pretty(@as(f16, 3.141))}); 42 + print("Pretty f32 - {f}\n", .{pretty(@as(f32, 3.141))}); 43 + print("Pretty f64 - {f}\n", .{pretty(@as(f64, 3.141))}); 44 + 45 + print("\nEnums\n", .{}); 46 + print("Pretty enum - {f}\n", .{pretty(Hello.world)}); 47 + print("Pretty enum literal - {f}\n", .{pretty(.hello_world)}); 48 + }
+162
pretty.zig
··· 1 + const std = @import("std"); 2 + const Io = std.Io; 3 + 4 + pub const Options = struct { 5 + indent_width: comptime_int = 2, 6 + 7 + show_type_names: bool = true, 8 + always_show_type_names: bool = true, 9 + 10 + type_value_seperator: []const u8 = " = ", 11 + }; 12 + 13 + pub fn pretty(value: anytype) Pretty(@TypeOf(value)) { 14 + return Pretty(@TypeOf(value)).init(value); 15 + } 16 + 17 + pub fn Pretty(comptime T: type) type { 18 + return PrettyWithOptions(T, .{}); 19 + } 20 + 21 + pub fn PrettyWithOptions(comptime T: type, comptime options: Options) type { 22 + const global = struct { 23 + var tty: ?Io.Terminal = null; 24 + }; 25 + 26 + return struct { 27 + value: T, 28 + 29 + pub fn init(val: T) @This() { 30 + return .{ .value = val }; 31 + } 32 + 33 + pub fn format(this: *const @This(), w: *std.Io.Writer) error{WriteFailed}!void { 34 + if (global.tty == null) { 35 + var buffer: [1]u8 = undefined; 36 + const stderr = std.debug.lockStderr(&buffer).terminal(); 37 + defer std.debug.unlockStderr(); 38 + 39 + global.tty = stderr; 40 + global.tty.?.writer = w; 41 + } 42 + 43 + const comp_ctx = ComptimeContext{ .options = options }; 44 + const run_ctx = RuntimeContext{ 45 + .out = w, 46 + .tty = global.tty.?, 47 + }; 48 + 49 + return innerFmt(T, comp_ctx, this.value, run_ctx); 50 + } 51 + }; 52 + } 53 + 54 + const RuntimeContext = struct { 55 + out: *Io.Writer, 56 + tty: Io.Terminal, 57 + 58 + depth: usize = 0, 59 + 60 + indent_level: usize = 0, 61 + 62 + pub fn print(this: RuntimeContext, comptime fmt: []const u8, args: anytype) error{WriteFailed}!void { 63 + return this.out.print(fmt, args); 64 + } 65 + 66 + pub fn setColor(this: RuntimeContext, color: Io.Terminal.Color) void { 67 + this.tty.setColor(color) catch {}; 68 + } 69 + 70 + pub fn resetColor(this: RuntimeContext) void { 71 + this.tty.setColor(.reset) catch {}; 72 + } 73 + }; 74 + 75 + const ComptimeContext = struct { 76 + depth: comptime_int = 0, 77 + 78 + indent_level: comptime_int = 0, 79 + 80 + options: Options, 81 + exited_comptime: bool = false, 82 + 83 + pub fn inComptime(comptime this: ComptimeContext) bool { 84 + if (!this.exited_comptime) return false; 85 + return @inComptime(); 86 + } 87 + }; 88 + 89 + fn innerFmt( 90 + comptime T: type, 91 + comptime cctx: ComptimeContext, 92 + value: T, 93 + rctx: RuntimeContext, 94 + ) error{WriteFailed}!void { 95 + const info = @typeInfo(T); 96 + try printType(T, cctx, rctx, info); 97 + 98 + return switch (info) { 99 + .bool => formatBool(rctx, value), 100 + 101 + .comptime_int, 102 + .comptime_float, 103 + .int, 104 + .float, 105 + .@"enum", 106 + .enum_literal, 107 + => formatValue(rctx, value), 108 + 109 + else => { 110 + rctx.setColor(.red); 111 + try rctx.print("Unimplemented! ({} = {any})", .{ info, value }); 112 + rctx.resetColor(); 113 + }, 114 + }; 115 + } 116 + 117 + inline fn printType( 118 + comptime T: type, 119 + comptime cctx: ComptimeContext, 120 + rctx: RuntimeContext, 121 + comptime info: std.builtin.Type, 122 + ) error{WriteFailed}!void { 123 + if (cctx.depth != 0 or cctx.options.always_show_type_names) { 124 + rctx.setColor(.dim); 125 + 126 + if (cctx.options.show_type_names) { 127 + switch (info) { 128 + .bool => try rctx.print("bool{s}", .{cctx.options.type_value_seperator}), 129 + .comptime_int => try rctx.print("comptime_int{s}", .{cctx.options.type_value_seperator}), 130 + .comptime_float => try rctx.print("comptime_float{s}", .{cctx.options.type_value_seperator}), 131 + .int => |int| try rctx.print( 132 + "{s}{}{s}", 133 + .{ 134 + if (int.signedness == .signed) "i" else "u", 135 + int.bits, 136 + cctx.options.type_value_seperator, 137 + }, 138 + ), 139 + .float => |float| try rctx.print("f{}{s}", .{ float.bits, cctx.options.type_value_seperator }), 140 + .enum_literal => try rctx.print("enum literal{s}", .{cctx.options.type_value_seperator}), 141 + .@"enum" => try rctx.print("{s}{s}", .{ @typeName(T), cctx.options.type_value_seperator }), 142 + else => try rctx.print("missing {}{s}", .{ info, cctx.options.type_value_seperator }), 143 + } 144 + } else { 145 + try rctx.print("{s}", .{cctx.options.type_value_seperator}); 146 + } 147 + 148 + rctx.resetColor(); 149 + } 150 + } 151 + 152 + inline fn formatBool(rctx: RuntimeContext, value: bool) !void { 153 + rctx.setColor(if (value) .bright_green else .bright_red); 154 + try rctx.print("{}", .{value}); 155 + rctx.resetColor(); 156 + } 157 + 158 + inline fn formatValue(rctx: RuntimeContext, value: anytype) !void { 159 + rctx.setColor(.blue); 160 + try rctx.print("{}", .{value}); 161 + rctx.resetColor(); 162 + }