A SpaceTraders Agent

create custom pretty printer

altagos.dev 0a2b039f 960c034f

verified
+465
+9
build.zig
··· 7 7 const options = b.addOptions(); 8 8 9 9 const known_folders = b.dependency("known_folders", .{}).module("known-folders"); 10 + 11 + const meta = b.createModule(.{ 12 + .root_source_file = b.path("src/meta/root.zig"), 13 + .target = target, 14 + .optimize = optimize, 15 + }); 16 + 10 17 const st = b.addModule("st", .{ 11 18 .root_source_file = b.path("src/st/root.zig"), 12 19 .target = target, ··· 31 38 .optimize = optimize, 32 39 .imports = &.{ 33 40 .{ .name = "known-folders", .module = known_folders }, 41 + // Modules 34 42 .{ .name = "st", .module = st }, 35 43 .{ .name = "agent", .module = agent }, 44 + .{ .name = "meta", .module = meta }, 36 45 }, 37 46 }); 38 47
+453
src/meta/fmt.zig
··· 1 + const std = @import("std"); 2 + const Io = std.Io; 3 + const Type = std.builtin.Type; 4 + 5 + pub fn pretty(value: anytype) Pretty(@TypeOf(value)) { 6 + return Pretty(@TypeOf(value)).init(value); 7 + } 8 + 9 + fn Context(comptime runtime: bool) type { 10 + if (runtime) { 11 + return struct { 12 + indent_width: u8 = 2, 13 + indent_level: usize = 0, 14 + }; 15 + } else { 16 + return struct { 17 + indent_width: comptime_int = 2, 18 + indent_level: comptime_int = 0, 19 + }; 20 + } 21 + } 22 + 23 + const Options = struct { 24 + type_name: bool = true, 25 + last_layer_type_name: bool = true, 26 + 27 + follow_pointers: bool = true, 28 + }; 29 + 30 + pub fn Pretty(comptime T: type) type { 31 + const global = struct { 32 + var conf: ?std.Io.tty.Config = null; 33 + }; 34 + return struct { 35 + value: T, 36 + pub fn init(val: T) @This() { 37 + return .{ .value = val }; 38 + } 39 + pub fn format(this: *const @This(), w: *std.Io.Writer) error{WriteFailed}!void { 40 + if (global.conf == null) { 41 + global.conf = .detect(.stderr()); 42 + } 43 + return innerFmt(w, global.conf.?, T, this.value, false, .{}, .{}); 44 + } 45 + }; 46 + } 47 + 48 + const Color = enum { 49 + dim, 50 + reset, 51 + text, 52 + field, 53 + value, 54 + }; 55 + 56 + fn setColor( 57 + w: *Io.Writer, 58 + tty: Io.tty.Config, 59 + color: Color, 60 + ) void { 61 + switch (color) { 62 + .dim => { 63 + tty.setColor(w, .dim) catch {}; 64 + }, 65 + .reset => { 66 + tty.setColor(w, .reset) catch {}; 67 + }, 68 + .text => { 69 + tty.setColor(w, .italic) catch {}; 70 + tty.setColor(w, .dim) catch {}; 71 + tty.setColor(w, .blue) catch {}; 72 + }, 73 + .field => { 74 + tty.setColor(w, .green) catch {}; 75 + }, 76 + .value => { 77 + tty.setColor(w, .italic) catch {}; 78 + tty.setColor(w, .blue) catch {}; 79 + }, 80 + } 81 + } 82 + 83 + fn innerFmt( 84 + w: *std.Io.Writer, 85 + tty: std.Io.tty.Config, 86 + comptime T: type, 87 + value: T, 88 + comptime runtime: bool, 89 + ctx: Context(runtime), 90 + opts: Options, 91 + ) error{WriteFailed}!void { 92 + const info = @typeInfo(T); 93 + 94 + switch (info) { 95 + .bool => try printBool(w, tty, value, opts), 96 + .int => |int| try printInt(w, tty, int, value, opts), 97 + .float => |float| try printFloat(w, tty, float, value, opts), 98 + .@"enum", .enum_literal => try printEnum(w, tty, value, opts), 99 + .optional => |optional| try printOptional(w, tty, optional, value, runtime, ctx, opts), 100 + .pointer => |ptr| { 101 + if (opts.follow_pointers) { 102 + try printPointer(w, tty, ptr, value, runtime, ctx, opts); 103 + } else {} 104 + }, 105 + .array => |array| try printArray(w, tty, array, value, runtime, ctx, opts), 106 + .@"struct" => |str| try printStruct(T, w, tty, str, value, runtime, ctx, opts), 107 + else => { 108 + tty.setColor(w, .red) catch {}; 109 + try w.print("Unimplemented! ({})", .{info}); 110 + setColor(w, tty, .reset); 111 + }, 112 + } 113 + } 114 + 115 + fn printBool( 116 + w: *Io.Writer, 117 + tty: Io.tty.Config, 118 + value: bool, 119 + opts: Options, 120 + ) !void { 121 + if (opts.type_name) { 122 + setColor(w, tty, .dim); 123 + try w.print("bool => ", .{}); 124 + setColor(w, tty, .reset); 125 + } else { 126 + tty.setColor(w, .dim) catch {}; 127 + try w.print("=> ", .{}); 128 + setColor(w, tty, .reset); 129 + } 130 + setColor(w, tty, .value); 131 + if (value) { 132 + tty.setColor(w, .bright_green) catch {}; 133 + } else { 134 + tty.setColor(w, .bright_red) catch {}; 135 + } 136 + try w.print("{}", .{value}); 137 + setColor(w, tty, .reset); 138 + } 139 + 140 + fn printInt( 141 + w: *Io.Writer, 142 + tty: Io.tty.Config, 143 + int: Type.Int, 144 + value: anytype, 145 + opts: Options, 146 + ) !void { 147 + if (opts.type_name) { 148 + tty.setColor(w, .dim) catch {}; 149 + try w.print("{s}{} => ", .{ if (int.signedness == .signed) "i" else "u", int.bits }); 150 + setColor(w, tty, .reset); 151 + } else { 152 + tty.setColor(w, .dim) catch {}; 153 + try w.print("=> ", .{}); 154 + setColor(w, tty, .reset); 155 + } 156 + setColor(w, tty, .value); 157 + try w.print("{}", .{value}); 158 + setColor(w, tty, .reset); 159 + } 160 + 161 + fn printFloat( 162 + w: *Io.Writer, 163 + tty: Io.tty.Config, 164 + float: Type.Float, 165 + value: anytype, 166 + opts: Options, 167 + ) !void { 168 + if (opts.type_name) { 169 + tty.setColor(w, .dim) catch {}; 170 + try w.print("f{} => ", .{float.bits}); 171 + setColor(w, tty, .reset); 172 + } else { 173 + tty.setColor(w, .dim) catch {}; 174 + try w.print("=> ", .{}); 175 + setColor(w, tty, .reset); 176 + } 177 + setColor(w, tty, .value); 178 + try w.print("{}", .{value}); 179 + setColor(w, tty, .reset); 180 + } 181 + 182 + fn printEnum( 183 + w: *Io.Writer, 184 + tty: Io.tty.Config, 185 + value: anytype, 186 + opts: Options, 187 + ) !void { 188 + if (opts.type_name) { 189 + tty.setColor(w, .dim) catch {}; 190 + try w.print("{s} => ", .{@typeName(@TypeOf(value))}); 191 + setColor(w, tty, .reset); 192 + } else { 193 + tty.setColor(w, .dim) catch {}; 194 + try w.print("=> ", .{}); 195 + setColor(w, tty, .reset); 196 + } 197 + setColor(w, tty, .value); 198 + try w.print("{}", .{value}); 199 + setColor(w, tty, .reset); 200 + } 201 + 202 + fn printOptional( 203 + w: *Io.Writer, 204 + tty: Io.tty.Config, 205 + optional: Type.Optional, 206 + value: anytype, 207 + comptime runtime: bool, 208 + ctx: Context(runtime), 209 + opts: Options, 210 + ) !void { 211 + var next_opts = opts; 212 + next_opts.type_name = if (!opts.type_name and !opts.last_layer_type_name) false else true; 213 + next_opts.last_layer_type_name = opts.type_name; 214 + 215 + if (opts.type_name) { 216 + tty.setColor(w, .dim) catch {}; 217 + try w.print("?", .{}); 218 + setColor(w, tty, .reset); 219 + } 220 + 221 + if (value) |val| { 222 + try innerFmt(w, tty, optional.child, val, runtime, ctx, next_opts); 223 + } else { 224 + if (opts.type_name) { 225 + tty.setColor(w, .dim) catch {}; 226 + try w.print("{s} => ", .{@typeName(optional.child)}); 227 + setColor(w, tty, .reset); 228 + } else { 229 + tty.setColor(w, .dim) catch {}; 230 + try w.print("=> ", .{}); 231 + setColor(w, tty, .reset); 232 + } 233 + 234 + tty.setColor(w, .blue) catch {}; 235 + try w.writeAll("null"); 236 + setColor(w, tty, .reset); 237 + } 238 + } 239 + 240 + fn printArray( 241 + w: *Io.Writer, 242 + tty: Io.tty.Config, 243 + array: Type.Array, 244 + value: anytype, 245 + comptime runtime: bool, 246 + ctx: Context(runtime), 247 + opts: Options, 248 + ) !void { 249 + comptime var next_ctx = ctx; 250 + 251 + var next_opts = opts; 252 + next_opts.type_name = if (!opts.type_name and !opts.last_layer_type_name) false else true; 253 + next_opts.last_layer_type_name = opts.type_name; 254 + 255 + if (opts.type_name) { 256 + tty.setColor(w, .dim) catch {}; 257 + try w.print("[{}]{s} =>", .{ array.len, @typeName(array.child) }); 258 + setColor(w, tty, .reset); 259 + } else { 260 + tty.setColor(w, .dim) catch {}; 261 + try w.print("=> ", .{}); 262 + setColor(w, tty, .reset); 263 + } 264 + 265 + inline for (value, 0..) |item, idx| { 266 + try indent(w, runtime, ctx); 267 + 268 + setColor(w, tty, .field); 269 + try w.print("[{}] ", .{idx}); 270 + setColor(w, tty, .reset); 271 + 272 + next_ctx.indent_level = ctx.indent_level + 1; 273 + next_opts.type_name = false; 274 + 275 + try innerFmt(w, tty, @TypeOf(item), item, runtime, next_ctx, next_opts); 276 + } 277 + } 278 + 279 + fn printPointer( 280 + w: *Io.Writer, 281 + tty: Io.tty.Config, 282 + ptr: Type.Pointer, 283 + value: anytype, 284 + comptime runtime: bool, 285 + ctx: Context(runtime), 286 + opts: Options, 287 + ) !void { 288 + var next_opts = opts; 289 + next_opts.type_name = if (!opts.type_name and !opts.last_layer_type_name) false else true; 290 + next_opts.last_layer_type_name = opts.type_name; 291 + 292 + switch (ptr.size) { 293 + .one => { 294 + if (opts.type_name) { 295 + tty.setColor(w, .dim) catch {}; 296 + try w.print("*{s}", .{if (ptr.is_const) "const " else ""}); 297 + setColor(w, tty, .reset); 298 + } 299 + 300 + if (ptr.child == anyopaque or @typeInfo(ptr.child) == .@"fn") 301 + return; 302 + 303 + try innerFmt(w, tty, ptr.child, value, runtime, ctx, next_opts); 304 + }, 305 + .slice => { 306 + if (opts.type_name) { 307 + setColor(w, tty, .dim); 308 + try w.print("[]{s}", .{if (ptr.is_const) "const " else ""}); 309 + setColor(w, tty, .reset); 310 + } 311 + 312 + if (ptr.child == u8) { 313 + tty.setColor(w, .dim) catch {}; 314 + try w.print("{s}=>", .{if (opts.type_name) "u8 " else ""}); 315 + setColor(w, tty, .reset); 316 + 317 + try indent(w, runtime, ctx); 318 + 319 + setColor(w, tty, .text); 320 + try w.print("\"{s}\"", .{value}); 321 + setColor(w, tty, .reset); 322 + return; 323 + } 324 + 325 + setColor(w, tty, .dim); 326 + try w.print("{s}{s}", .{ 327 + if (opts.type_name) @typeName(ptr.child) else "", 328 + if (opts.type_name) " =>" else "=>", 329 + }); 330 + setColor(w, tty, .reset); 331 + 332 + const run_ctx: Context(true) = .{ .indent_width = ctx.indent_width, .indent_level = ctx.indent_level }; 333 + var next_ctx: Context(true) = .{ .indent_width = ctx.indent_width, .indent_level = ctx.indent_level }; 334 + 335 + var count: usize = 0; 336 + for (value, 0..) |item, idx| { 337 + try indent(w, true, run_ctx); 338 + 339 + setColor(w, tty, .field); 340 + try w.print("[{}] ", .{idx}); 341 + setColor(w, tty, .reset); 342 + 343 + next_ctx.indent_level = ctx.indent_level + 1; 344 + next_opts.type_name = false; 345 + 346 + try innerFmt(w, tty, @TypeOf(item), item, true, next_ctx, next_opts); 347 + count += 1; 348 + } 349 + 350 + if (count == 0) { 351 + setColor(w, tty, .dim); 352 + try w.writeAll(" empty"); 353 + setColor(w, tty, .reset); 354 + } 355 + }, 356 + else => { 357 + try w.print("unimplemented {}", .{ptr}); 358 + }, 359 + } 360 + } 361 + 362 + fn printStruct( 363 + comptime T: type, 364 + w: *Io.Writer, 365 + tty: Io.tty.Config, 366 + str: Type.Struct, 367 + value: anytype, 368 + comptime runtime: bool, 369 + ctx: Context(runtime), 370 + opts: Options, 371 + ) !void { 372 + var next_opts = opts; 373 + next_opts.type_name = if (!opts.type_name and !opts.last_layer_type_name) false else true; 374 + next_opts.last_layer_type_name = opts.type_name; 375 + 376 + setColor(w, tty, .dim); 377 + if (opts.type_name) { 378 + try w.print("{s} =>", .{@typeName(T)}); 379 + } else { 380 + try w.print("=>", .{}); 381 + } 382 + setColor(w, tty, .reset); 383 + 384 + if (runtime) { 385 + var next_ctx: Context(true) = ctx; 386 + 387 + inline for (str.fields) |field| { 388 + try indent(w, runtime, ctx); 389 + 390 + setColor(w, tty, .field); 391 + try w.print(".{s}", .{field.name}); 392 + setColor(w, tty, .reset); 393 + 394 + setColor(w, tty, .dim); 395 + if (opts.type_name) { 396 + try w.writeAll(": "); 397 + } else try w.writeByte(' '); 398 + setColor(w, tty, .reset); 399 + 400 + next_ctx.indent_level = ctx.indent_level + 1; 401 + 402 + try innerFmt( 403 + w, 404 + tty, 405 + field.type, 406 + @field(value, field.name), 407 + runtime, 408 + next_ctx, 409 + next_opts, 410 + ); 411 + } 412 + } else { 413 + comptime var next_ctx: Context(false) = ctx; 414 + 415 + inline for (str.fields) |field| { 416 + try indent(w, runtime, ctx); 417 + 418 + setColor(w, tty, .field); 419 + try w.print(".{s}", .{field.name}); 420 + setColor(w, tty, .reset); 421 + 422 + setColor(w, tty, .dim); 423 + if (opts.type_name) { 424 + try w.writeAll(": "); 425 + } else try w.writeByte(' '); 426 + setColor(w, tty, .reset); 427 + 428 + next_ctx.indent_level = ctx.indent_level + 1; 429 + 430 + try innerFmt( 431 + w, 432 + tty, 433 + field.type, 434 + @field(value, field.name), 435 + runtime, 436 + next_ctx, 437 + next_opts, 438 + ); 439 + } 440 + } 441 + } 442 + 443 + fn indent(w: *Io.Writer, comptime runtime: bool, ctx: Context(runtime)) !void { 444 + try w.writeByte('\n'); 445 + if (runtime) { 446 + for (0..((ctx.indent_level + 1) * ctx.indent_width)) |_| { 447 + try w.writeByte(' '); 448 + } 449 + } else { 450 + const text: [(ctx.indent_level + 1) * ctx.indent_width]u8 = @splat(' '); 451 + try w.writeAll(&text); 452 + } 453 + }
+3
src/meta/root.zig
··· 1 + pub const fmt = @import("fmt.zig"); 2 + 3 + pub const pretty = fmt.pretty;