A SpaceTraders Agent
at main 12 kB view raw
1const std = @import("std"); 2const Io = std.Io; 3const Type = std.builtin.Type; 4 5pub fn pretty(value: anytype) Pretty(@TypeOf(value)) { 6 return Pretty(@TypeOf(value)).init(value); 7} 8 9fn 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 23const Options = struct { 24 type_name: bool = true, 25 last_layer_type_name: bool = true, 26 27 follow_pointers: bool = true, 28}; 29 30pub 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 48const Color = enum { 49 dim, 50 reset, 51 text, 52 field, 53 value, 54}; 55 56fn 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 83fn 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 115fn 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 140fn 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 161fn 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 182fn 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 202fn 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 240fn 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 279fn 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 362fn 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 443fn 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}