| ... | ... | @@ -69,6 +69,7 @@ pub const FormatOptions = struct { |
| 69 | 69 | /// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. |
| 70 | 70 | /// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max. |
| 71 | 71 | /// - `*`: output the address of the value instead of the value itself. |
| 72 | /// - `any`: output a value of any type using its default format |
| 72 | 73 | /// |
| 73 | 74 | /// If a formatted user type contains a function of the type |
| 74 | 75 | /// ``` |
| ... | ... | @@ -387,17 +388,32 @@ pub fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @T |
| 387 | 388 | return; |
| 388 | 389 | } |
| 389 | 390 | }, |
| 390 | | .Array => |info| { |
| 391 | | try writer.writeAll(@typeName(info.child) ++ "@"); |
| 392 | | try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); |
| 393 | | return; |
| 394 | | }, |
| 395 | 391 | else => {}, |
| 396 | 392 | } |
| 397 | 393 | |
| 398 | 394 | @compileError("Cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier"); |
| 399 | 395 | } |
| 400 | 396 | |
| 397 | // This ANY const is a workaround for: https://github.com/ziglang/zig/issues/7948 |
| 398 | const ANY = "any"; |
| 399 | |
| 400 | fn defaultSpec(comptime T: type) [:0]const u8 { |
| 401 | switch (@typeInfo(T)) { |
| 402 | .Array => |_| return ANY, |
| 403 | .Pointer => |ptr_info| switch (ptr_info.size) { |
| 404 | .One => switch (@typeInfo(ptr_info.child)) { |
| 405 | .Array => |_| return "*", |
| 406 | else => {}, |
| 407 | }, |
| 408 | .Many, .C => return "*", |
| 409 | .Slice => return ANY, |
| 410 | }, |
| 411 | .Optional => |info| return defaultSpec(info.child), |
| 412 | else => {}, |
| 413 | } |
| 414 | return ""; |
| 415 | } |
| 416 | |
| 401 | 417 | pub fn formatType( |
| 402 | 418 | value: anytype, |
| 403 | 419 | comptime fmt: []const u8, |
| ... | ... | @@ -405,18 +421,19 @@ pub fn formatType( |
| 405 | 421 | writer: anytype, |
| 406 | 422 | max_depth: usize, |
| 407 | 423 | ) @TypeOf(writer).Error!void { |
| 408 | | if (comptime std.mem.eql(u8, fmt, "*")) { |
| 424 | const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY)) defaultSpec(@TypeOf(value)) else fmt; |
| 425 | if (comptime std.mem.eql(u8, actual_fmt, "*")) { |
| 409 | 426 | return formatAddress(value, options, writer); |
| 410 | 427 | } |
| 411 | 428 | |
| 412 | 429 | const T = @TypeOf(value); |
| 413 | 430 | if (comptime std.meta.trait.hasFn("format")(T)) { |
| 414 | | return try value.format(fmt, options, writer); |
| 431 | return try value.format(actual_fmt, options, writer); |
| 415 | 432 | } |
| 416 | 433 | |
| 417 | 434 | switch (@typeInfo(T)) { |
| 418 | 435 | .ComptimeInt, .Int, .ComptimeFloat, .Float => { |
| 419 | | return formatValue(value, fmt, options, writer); |
| 436 | return formatValue(value, actual_fmt, options, writer); |
| 420 | 437 | }, |
| 421 | 438 | .Void => { |
| 422 | 439 | return formatBuf("void", options, writer); |
| ... | ... | @@ -426,16 +443,16 @@ pub fn formatType( |
| 426 | 443 | }, |
| 427 | 444 | .Optional => { |
| 428 | 445 | if (value) |payload| { |
| 429 | | return formatType(payload, fmt, options, writer, max_depth); |
| 446 | return formatType(payload, actual_fmt, options, writer, max_depth); |
| 430 | 447 | } else { |
| 431 | 448 | return formatBuf("null", options, writer); |
| 432 | 449 | } |
| 433 | 450 | }, |
| 434 | 451 | .ErrorUnion => { |
| 435 | 452 | if (value) |payload| { |
| 436 | | return formatType(payload, fmt, options, writer, max_depth); |
| 453 | return formatType(payload, actual_fmt, options, writer, max_depth); |
| 437 | 454 | } else |err| { |
| 438 | | return formatType(err, fmt, options, writer, max_depth); |
| 455 | return formatType(err, actual_fmt, options, writer, max_depth); |
| 439 | 456 | } |
| 440 | 457 | }, |
| 441 | 458 | .ErrorSet => { |
| ... | ... | @@ -461,7 +478,7 @@ pub fn formatType( |
| 461 | 478 | } |
| 462 | 479 | |
| 463 | 480 | try writer.writeAll("("); |
| 464 | | try formatType(@enumToInt(value), fmt, options, writer, max_depth); |
| 481 | try formatType(@enumToInt(value), actual_fmt, options, writer, max_depth); |
| 465 | 482 | try writer.writeAll(")"); |
| 466 | 483 | }, |
| 467 | 484 | .Union => |info| { |
| ... | ... | @@ -475,7 +492,7 @@ pub fn formatType( |
| 475 | 492 | try writer.writeAll(" = "); |
| 476 | 493 | inline for (info.fields) |u_field| { |
| 477 | 494 | if (value == @field(UnionTagType, u_field.name)) { |
| 478 | | try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1); |
| 495 | try formatType(@field(value, u_field.name), ANY, options, writer, max_depth - 1); |
| 479 | 496 | } |
| 480 | 497 | } |
| 481 | 498 | try writer.writeAll(" }"); |
| ... | ... | @@ -497,48 +514,54 @@ pub fn formatType( |
| 497 | 514 | } |
| 498 | 515 | try writer.writeAll(f.name); |
| 499 | 516 | try writer.writeAll(" = "); |
| 500 | | try formatType(@field(value, f.name), fmt, options, writer, max_depth - 1); |
| 517 | try formatType(@field(value, f.name), ANY, options, writer, max_depth - 1); |
| 501 | 518 | } |
| 502 | 519 | try writer.writeAll(" }"); |
| 503 | 520 | }, |
| 504 | 521 | .Pointer => |ptr_info| switch (ptr_info.size) { |
| 505 | 522 | .One => switch (@typeInfo(ptr_info.child)) { |
| 506 | 523 | .Array => |info| { |
| 524 | if (actual_fmt.len == 0) |
| 525 | @compileError("cannot format array ref without a specifier (i.e. {s} or {*})"); |
| 507 | 526 | if (info.child == u8) { |
| 508 | | if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { |
| 509 | | return formatText(value, fmt, options, writer); |
| 527 | if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { |
| 528 | return formatText(value, actual_fmt, options, writer); |
| 510 | 529 | } |
| 511 | 530 | } |
| 512 | | return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); |
| 531 | @compileError("Unknown format string: '" ++ actual_fmt ++ "'"); |
| 513 | 532 | }, |
| 514 | 533 | .Enum, .Union, .Struct => { |
| 515 | | return formatType(value.*, fmt, options, writer, max_depth); |
| 534 | return formatType(value.*, actual_fmt, options, writer, max_depth); |
| 516 | 535 | }, |
| 517 | 536 | else => return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }), |
| 518 | 537 | }, |
| 519 | 538 | .Many, .C => { |
| 539 | if (actual_fmt.len == 0) |
| 540 | @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); |
| 520 | 541 | if (ptr_info.sentinel) |sentinel| { |
| 521 | | return formatType(mem.span(value), fmt, options, writer, max_depth); |
| 542 | return formatType(mem.span(value), actual_fmt, options, writer, max_depth); |
| 522 | 543 | } |
| 523 | 544 | if (ptr_info.child == u8) { |
| 524 | | if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { |
| 525 | | return formatText(mem.span(value), fmt, options, writer); |
| 545 | if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { |
| 546 | return formatText(mem.span(value), actual_fmt, options, writer); |
| 526 | 547 | } |
| 527 | 548 | } |
| 528 | | return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); |
| 549 | @compileError("Unknown format string: '" ++ actual_fmt ++ "'"); |
| 529 | 550 | }, |
| 530 | 551 | .Slice => { |
| 552 | if (actual_fmt.len == 0) |
| 553 | @compileError("cannot format slice without a specifier (i.e. {s} or {any})"); |
| 531 | 554 | if (max_depth == 0) { |
| 532 | 555 | return writer.writeAll("{ ... }"); |
| 533 | 556 | } |
| 534 | 557 | if (ptr_info.child == u8) { |
| 535 | | if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { |
| 536 | | return formatText(value, fmt, options, writer); |
| 558 | if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { |
| 559 | return formatText(value, actual_fmt, options, writer); |
| 537 | 560 | } |
| 538 | 561 | } |
| 539 | 562 | try writer.writeAll("{ "); |
| 540 | 563 | for (value) |elem, i| { |
| 541 | | try formatType(elem, fmt, options, writer, max_depth - 1); |
| 564 | try formatType(elem, actual_fmt, options, writer, max_depth - 1); |
| 542 | 565 | if (i != value.len - 1) { |
| 543 | 566 | try writer.writeAll(", "); |
| 544 | 567 | } |
| ... | ... | @@ -547,17 +570,19 @@ pub fn formatType( |
| 547 | 570 | }, |
| 548 | 571 | }, |
| 549 | 572 | .Array => |info| { |
| 573 | if (actual_fmt.len == 0) |
| 574 | @compileError("cannot format array without a specifier (i.e. {s} or {any})"); |
| 550 | 575 | if (max_depth == 0) { |
| 551 | 576 | return writer.writeAll("{ ... }"); |
| 552 | 577 | } |
| 553 | 578 | if (info.child == u8) { |
| 554 | | if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { |
| 555 | | return formatText(&value, fmt, options, writer); |
| 579 | if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { |
| 580 | return formatText(&value, actual_fmt, options, writer); |
| 556 | 581 | } |
| 557 | 582 | } |
| 558 | 583 | try writer.writeAll("{ "); |
| 559 | 584 | for (value) |elem, i| { |
| 560 | | try formatType(elem, fmt, options, writer, max_depth - 1); |
| 585 | try formatType(elem, actual_fmt, options, writer, max_depth - 1); |
| 561 | 586 | if (i < value.len - 1) { |
| 562 | 587 | try writer.writeAll(", "); |
| 563 | 588 | } |
| ... | ... | @@ -568,7 +593,7 @@ pub fn formatType( |
| 568 | 593 | try writer.writeAll("{ "); |
| 569 | 594 | var i: usize = 0; |
| 570 | 595 | while (i < info.len) : (i += 1) { |
| 571 | | try formatValue(value[i], fmt, options, writer); |
| 596 | try formatValue(value[i], actual_fmt, options, writer); |
| 572 | 597 | if (i < info.len - 1) { |
| 573 | 598 | try writer.writeAll(", "); |
| 574 | 599 | } |
| ... | ... | @@ -1668,7 +1693,7 @@ test "slice" { |
| 1668 | 1693 | { |
| 1669 | 1694 | var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; |
| 1670 | 1695 | var runtime_zero: usize = 0; |
| 1671 | | try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {}", .{int_slice[runtime_zero..]}); |
| 1696 | try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{int_slice[runtime_zero..]}); |
| 1672 | 1697 | try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); |
| 1673 | 1698 | try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); |
| 1674 | 1699 | try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); |