master
   1//! ZON can be serialized with `serialize`.
   2//!
   3//! The following functions are provided for serializing recursive types:
   4//! * `serializeMaxDepth`
   5//! * `serializeArbitraryDepth`
   6//!
   7//! For additional control over serialization, see `Serializer`.
   8//!
   9//! The following types and any types that contain them may not be serialized:
  10//! * `type`
  11//! * `void`, except as a union payload
  12//! * `noreturn`
  13//! * Error sets/error unions
  14//! * Untagged unions
  15//! * Non-exhaustive enums
  16//! * Many-pointers or C-pointers
  17//! * Opaque types, including `anyopaque`
  18//! * Async frame types, including `anyframe` and `anyframe->T`
  19//! * Functions
  20//!
  21//! All other types are valid. Unsupported types will fail to serialize at compile time. Pointers
  22//! are followed.
  23
  24const std = @import("std");
  25const assert = std.debug.assert;
  26const Writer = std.Io.Writer;
  27const Serializer = std.zon.Serializer;
  28
  29pub const SerializeOptions = struct {
  30    /// If false, whitespace is omitted. Otherwise whitespace is emitted in standard Zig style.
  31    whitespace: bool = true,
  32    /// Determines when to emit Unicode code point literals as opposed to integer literals.
  33    emit_codepoint_literals: Serializer.EmitCodepointLiterals = .never,
  34    /// If true, slices of `u8`s, and pointers to arrays of `u8` are serialized as containers.
  35    /// Otherwise they are serialized as string literals.
  36    emit_strings_as_containers: bool = false,
  37    /// If false, struct fields are not written if they are equal to their default value. Comparison
  38    /// is done by `std.meta.eql`.
  39    emit_default_optional_fields: bool = true,
  40};
  41
  42/// Serialize the given value as ZON.
  43///
  44/// It is asserted at comptime that `@TypeOf(val)` is not a recursive type.
  45pub fn serialize(val: anytype, options: SerializeOptions, writer: *Writer) Writer.Error!void {
  46    var s: Serializer = .{
  47        .writer = writer,
  48        .options = .{ .whitespace = options.whitespace },
  49    };
  50    try s.value(val, .{
  51        .emit_codepoint_literals = options.emit_codepoint_literals,
  52        .emit_strings_as_containers = options.emit_strings_as_containers,
  53        .emit_default_optional_fields = options.emit_default_optional_fields,
  54    });
  55}
  56
  57/// Like `serialize`, but recursive types are allowed.
  58///
  59/// Returns `error.ExceededMaxDepth` if `depth` is exceeded. Every nested value adds one to a
  60/// value's depth.
  61pub fn serializeMaxDepth(
  62    val: anytype,
  63    options: SerializeOptions,
  64    writer: *Writer,
  65    depth: usize,
  66) Serializer.DepthError!void {
  67    var s: Serializer = .{
  68        .writer = writer,
  69        .options = .{ .whitespace = options.whitespace },
  70    };
  71    try s.valueMaxDepth(val, .{
  72        .emit_codepoint_literals = options.emit_codepoint_literals,
  73        .emit_strings_as_containers = options.emit_strings_as_containers,
  74        .emit_default_optional_fields = options.emit_default_optional_fields,
  75    }, depth);
  76}
  77
  78/// Like `serialize`, but recursive types are allowed.
  79///
  80/// It is the caller's responsibility to ensure that `val` does not contain cycles.
  81pub fn serializeArbitraryDepth(
  82    val: anytype,
  83    options: SerializeOptions,
  84    writer: *Writer,
  85) Serializer.Error!void {
  86    var s: Serializer = .{
  87        .writer = writer,
  88        .options = .{ .whitespace = options.whitespace },
  89    };
  90    try s.valueArbitraryDepth(val, .{
  91        .emit_codepoint_literals = options.emit_codepoint_literals,
  92        .emit_strings_as_containers = options.emit_strings_as_containers,
  93        .emit_default_optional_fields = options.emit_default_optional_fields,
  94    });
  95}
  96
  97fn isNestedOptional(T: type) bool {
  98    comptime switch (@typeInfo(T)) {
  99        .optional => |optional| return isNestedOptionalInner(optional.child),
 100        else => return false,
 101    };
 102}
 103
 104fn isNestedOptionalInner(T: type) bool {
 105    switch (@typeInfo(T)) {
 106        .pointer => |pointer| {
 107            if (pointer.size == .one) {
 108                return isNestedOptionalInner(pointer.child);
 109            } else {
 110                return false;
 111            }
 112        },
 113        .optional => return true,
 114        else => return false,
 115    }
 116}
 117
 118fn expectSerializeEqual(
 119    expected: []const u8,
 120    value: anytype,
 121    options: SerializeOptions,
 122) !void {
 123    var aw: Writer.Allocating = .init(std.testing.allocator);
 124    const bw = &aw.writer;
 125    defer aw.deinit();
 126
 127    try serialize(value, options, bw);
 128    try std.testing.expectEqualStrings(expected, aw.written());
 129}
 130
 131test "std.zon stringify whitespace, high level API" {
 132    try expectSerializeEqual(".{}", .{}, .{});
 133    try expectSerializeEqual(".{}", .{}, .{ .whitespace = false });
 134
 135    try expectSerializeEqual(".{1}", .{1}, .{});
 136    try expectSerializeEqual(".{1}", .{1}, .{ .whitespace = false });
 137
 138    try expectSerializeEqual(".{1}", @as([1]u32, .{1}), .{});
 139    try expectSerializeEqual(".{1}", @as([1]u32, .{1}), .{ .whitespace = false });
 140
 141    try expectSerializeEqual(".{1}", @as([]const u32, &.{1}), .{});
 142    try expectSerializeEqual(".{1}", @as([]const u32, &.{1}), .{ .whitespace = false });
 143
 144    try expectSerializeEqual(".{ .x = 1 }", .{ .x = 1 }, .{});
 145    try expectSerializeEqual(".{.x=1}", .{ .x = 1 }, .{ .whitespace = false });
 146
 147    try expectSerializeEqual(".{ 1, 2 }", .{ 1, 2 }, .{});
 148    try expectSerializeEqual(".{1,2}", .{ 1, 2 }, .{ .whitespace = false });
 149
 150    try expectSerializeEqual(".{ 1, 2 }", @as([2]u32, .{ 1, 2 }), .{});
 151    try expectSerializeEqual(".{1,2}", @as([2]u32, .{ 1, 2 }), .{ .whitespace = false });
 152
 153    try expectSerializeEqual(".{ 1, 2 }", @as([]const u32, &.{ 1, 2 }), .{});
 154    try expectSerializeEqual(".{1,2}", @as([]const u32, &.{ 1, 2 }), .{ .whitespace = false });
 155
 156    try expectSerializeEqual(".{ .x = 1, .y = 2 }", .{ .x = 1, .y = 2 }, .{});
 157    try expectSerializeEqual(".{.x=1,.y=2}", .{ .x = 1, .y = 2 }, .{ .whitespace = false });
 158
 159    try expectSerializeEqual(
 160        \\.{
 161        \\    1,
 162        \\    2,
 163        \\    3,
 164        \\}
 165    , .{ 1, 2, 3 }, .{});
 166    try expectSerializeEqual(".{1,2,3}", .{ 1, 2, 3 }, .{ .whitespace = false });
 167
 168    try expectSerializeEqual(
 169        \\.{
 170        \\    1,
 171        \\    2,
 172        \\    3,
 173        \\}
 174    , @as([3]u32, .{ 1, 2, 3 }), .{});
 175    try expectSerializeEqual(".{1,2,3}", @as([3]u32, .{ 1, 2, 3 }), .{ .whitespace = false });
 176
 177    try expectSerializeEqual(
 178        \\.{
 179        \\    1,
 180        \\    2,
 181        \\    3,
 182        \\}
 183    , @as([]const u32, &.{ 1, 2, 3 }), .{});
 184    try expectSerializeEqual(
 185        ".{1,2,3}",
 186        @as([]const u32, &.{ 1, 2, 3 }),
 187        .{ .whitespace = false },
 188    );
 189
 190    try expectSerializeEqual(
 191        \\.{
 192        \\    .x = 1,
 193        \\    .y = 2,
 194        \\    .z = 3,
 195        \\}
 196    , .{ .x = 1, .y = 2, .z = 3 }, .{});
 197    try expectSerializeEqual(
 198        ".{.x=1,.y=2,.z=3}",
 199        .{ .x = 1, .y = 2, .z = 3 },
 200        .{ .whitespace = false },
 201    );
 202
 203    const Union = union(enum) { a: bool, b: i32, c: u8 };
 204
 205    try expectSerializeEqual(".{ .b = 1 }", Union{ .b = 1 }, .{});
 206    try expectSerializeEqual(".{.b=1}", Union{ .b = 1 }, .{ .whitespace = false });
 207
 208    // Nested indentation where outer object doesn't wrap
 209    try expectSerializeEqual(
 210        \\.{ .inner = .{
 211        \\    1,
 212        \\    2,
 213        \\    3,
 214        \\} }
 215    , .{ .inner = .{ 1, 2, 3 } }, .{});
 216
 217    const UnionWithVoid = union(enum) { a, b: void, c: u8 };
 218
 219    try expectSerializeEqual(
 220        \\.a
 221    , UnionWithVoid.a, .{});
 222}
 223
 224test "std.zon stringify whitespace, low level API" {
 225    var aw: Writer.Allocating = .init(std.testing.allocator);
 226    var s: Serializer = .{ .writer = &aw.writer };
 227    defer aw.deinit();
 228
 229    for ([2]bool{ true, false }) |whitespace| {
 230        s.options = .{ .whitespace = whitespace };
 231
 232        // Empty containers
 233        {
 234            var container = try s.beginStruct(.{});
 235            try container.end();
 236            try std.testing.expectEqualStrings(".{}", aw.written());
 237            aw.clearRetainingCapacity();
 238        }
 239
 240        {
 241            var container = try s.beginTuple(.{});
 242            try container.end();
 243            try std.testing.expectEqualStrings(".{}", aw.written());
 244            aw.clearRetainingCapacity();
 245        }
 246
 247        {
 248            var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
 249            try container.end();
 250            try std.testing.expectEqualStrings(".{}", aw.written());
 251            aw.clearRetainingCapacity();
 252        }
 253
 254        {
 255            var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
 256            try container.end();
 257            try std.testing.expectEqualStrings(".{}", aw.written());
 258            aw.clearRetainingCapacity();
 259        }
 260
 261        {
 262            var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 0 } });
 263            try container.end();
 264            try std.testing.expectEqualStrings(".{}", aw.written());
 265            aw.clearRetainingCapacity();
 266        }
 267
 268        {
 269            var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 0 } });
 270            try container.end();
 271            try std.testing.expectEqualStrings(".{}", aw.written());
 272            aw.clearRetainingCapacity();
 273        }
 274
 275        // Size 1
 276        {
 277            var container = try s.beginStruct(.{});
 278            try container.field("a", 1, .{});
 279            try container.end();
 280            if (whitespace) {
 281                try std.testing.expectEqualStrings(
 282                    \\.{
 283                    \\    .a = 1,
 284                    \\}
 285                , aw.written());
 286            } else {
 287                try std.testing.expectEqualStrings(".{.a=1}", aw.written());
 288            }
 289            aw.clearRetainingCapacity();
 290        }
 291
 292        {
 293            var container = try s.beginTuple(.{});
 294            try container.field(1, .{});
 295            try container.end();
 296            if (whitespace) {
 297                try std.testing.expectEqualStrings(
 298                    \\.{
 299                    \\    1,
 300                    \\}
 301                , aw.written());
 302            } else {
 303                try std.testing.expectEqualStrings(".{1}", aw.written());
 304            }
 305            aw.clearRetainingCapacity();
 306        }
 307
 308        {
 309            var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
 310            try container.field("a", 1, .{});
 311            try container.end();
 312            if (whitespace) {
 313                try std.testing.expectEqualStrings(".{ .a = 1 }", aw.written());
 314            } else {
 315                try std.testing.expectEqualStrings(".{.a=1}", aw.written());
 316            }
 317            aw.clearRetainingCapacity();
 318        }
 319
 320        {
 321            // We get extra spaces here, since we didn't know up front that there would only be one
 322            // field.
 323            var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
 324            try container.field(1, .{});
 325            try container.end();
 326            if (whitespace) {
 327                try std.testing.expectEqualStrings(".{ 1 }", aw.written());
 328            } else {
 329                try std.testing.expectEqualStrings(".{1}", aw.written());
 330            }
 331            aw.clearRetainingCapacity();
 332        }
 333
 334        {
 335            var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
 336            try container.field("a", 1, .{});
 337            try container.end();
 338            if (whitespace) {
 339                try std.testing.expectEqualStrings(".{ .a = 1 }", aw.written());
 340            } else {
 341                try std.testing.expectEqualStrings(".{.a=1}", aw.written());
 342            }
 343            aw.clearRetainingCapacity();
 344        }
 345
 346        {
 347            var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 1 } });
 348            try container.field(1, .{});
 349            try container.end();
 350            try std.testing.expectEqualStrings(".{1}", aw.written());
 351            aw.clearRetainingCapacity();
 352        }
 353
 354        // Size 2
 355        {
 356            var container = try s.beginStruct(.{});
 357            try container.field("a", 1, .{});
 358            try container.field("b", 2, .{});
 359            try container.end();
 360            if (whitespace) {
 361                try std.testing.expectEqualStrings(
 362                    \\.{
 363                    \\    .a = 1,
 364                    \\    .b = 2,
 365                    \\}
 366                , aw.written());
 367            } else {
 368                try std.testing.expectEqualStrings(".{.a=1,.b=2}", aw.written());
 369            }
 370            aw.clearRetainingCapacity();
 371        }
 372
 373        {
 374            var container = try s.beginTuple(.{});
 375            try container.field(1, .{});
 376            try container.field(2, .{});
 377            try container.end();
 378            if (whitespace) {
 379                try std.testing.expectEqualStrings(
 380                    \\.{
 381                    \\    1,
 382                    \\    2,
 383                    \\}
 384                , aw.written());
 385            } else {
 386                try std.testing.expectEqualStrings(".{1,2}", aw.written());
 387            }
 388            aw.clearRetainingCapacity();
 389        }
 390
 391        {
 392            var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
 393            try container.field("a", 1, .{});
 394            try container.field("b", 2, .{});
 395            try container.end();
 396            if (whitespace) {
 397                try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", aw.written());
 398            } else {
 399                try std.testing.expectEqualStrings(".{.a=1,.b=2}", aw.written());
 400            }
 401            aw.clearRetainingCapacity();
 402        }
 403
 404        {
 405            var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
 406            try container.field(1, .{});
 407            try container.field(2, .{});
 408            try container.end();
 409            if (whitespace) {
 410                try std.testing.expectEqualStrings(".{ 1, 2 }", aw.written());
 411            } else {
 412                try std.testing.expectEqualStrings(".{1,2}", aw.written());
 413            }
 414            aw.clearRetainingCapacity();
 415        }
 416
 417        {
 418            var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 2 } });
 419            try container.field("a", 1, .{});
 420            try container.field("b", 2, .{});
 421            try container.end();
 422            if (whitespace) {
 423                try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", aw.written());
 424            } else {
 425                try std.testing.expectEqualStrings(".{.a=1,.b=2}", aw.written());
 426            }
 427            aw.clearRetainingCapacity();
 428        }
 429
 430        {
 431            var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 2 } });
 432            try container.field(1, .{});
 433            try container.field(2, .{});
 434            try container.end();
 435            if (whitespace) {
 436                try std.testing.expectEqualStrings(".{ 1, 2 }", aw.written());
 437            } else {
 438                try std.testing.expectEqualStrings(".{1,2}", aw.written());
 439            }
 440            aw.clearRetainingCapacity();
 441        }
 442
 443        // Size 3
 444        {
 445            var container = try s.beginStruct(.{});
 446            try container.field("a", 1, .{});
 447            try container.field("b", 2, .{});
 448            try container.field("c", 3, .{});
 449            try container.end();
 450            if (whitespace) {
 451                try std.testing.expectEqualStrings(
 452                    \\.{
 453                    \\    .a = 1,
 454                    \\    .b = 2,
 455                    \\    .c = 3,
 456                    \\}
 457                , aw.written());
 458            } else {
 459                try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", aw.written());
 460            }
 461            aw.clearRetainingCapacity();
 462        }
 463
 464        {
 465            var container = try s.beginTuple(.{});
 466            try container.field(1, .{});
 467            try container.field(2, .{});
 468            try container.field(3, .{});
 469            try container.end();
 470            if (whitespace) {
 471                try std.testing.expectEqualStrings(
 472                    \\.{
 473                    \\    1,
 474                    \\    2,
 475                    \\    3,
 476                    \\}
 477                , aw.written());
 478            } else {
 479                try std.testing.expectEqualStrings(".{1,2,3}", aw.written());
 480            }
 481            aw.clearRetainingCapacity();
 482        }
 483
 484        {
 485            var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
 486            try container.field("a", 1, .{});
 487            try container.field("b", 2, .{});
 488            try container.field("c", 3, .{});
 489            try container.end();
 490            if (whitespace) {
 491                try std.testing.expectEqualStrings(".{ .a = 1, .b = 2, .c = 3 }", aw.written());
 492            } else {
 493                try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", aw.written());
 494            }
 495            aw.clearRetainingCapacity();
 496        }
 497
 498        {
 499            var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
 500            try container.field(1, .{});
 501            try container.field(2, .{});
 502            try container.field(3, .{});
 503            try container.end();
 504            if (whitespace) {
 505                try std.testing.expectEqualStrings(".{ 1, 2, 3 }", aw.written());
 506            } else {
 507                try std.testing.expectEqualStrings(".{1,2,3}", aw.written());
 508            }
 509            aw.clearRetainingCapacity();
 510        }
 511
 512        {
 513            var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 3 } });
 514            try container.field("a", 1, .{});
 515            try container.field("b", 2, .{});
 516            try container.field("c", 3, .{});
 517            try container.end();
 518            if (whitespace) {
 519                try std.testing.expectEqualStrings(
 520                    \\.{
 521                    \\    .a = 1,
 522                    \\    .b = 2,
 523                    \\    .c = 3,
 524                    \\}
 525                , aw.written());
 526            } else {
 527                try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", aw.written());
 528            }
 529            aw.clearRetainingCapacity();
 530        }
 531
 532        {
 533            var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 3 } });
 534            try container.field(1, .{});
 535            try container.field(2, .{});
 536            try container.field(3, .{});
 537            try container.end();
 538            if (whitespace) {
 539                try std.testing.expectEqualStrings(
 540                    \\.{
 541                    \\    1,
 542                    \\    2,
 543                    \\    3,
 544                    \\}
 545                , aw.written());
 546            } else {
 547                try std.testing.expectEqualStrings(".{1,2,3}", aw.written());
 548            }
 549            aw.clearRetainingCapacity();
 550        }
 551
 552        // Nested objects where the outer container doesn't wrap but the inner containers do
 553        {
 554            var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
 555            try container.field("first", .{ 1, 2, 3 }, .{});
 556            try container.field("second", .{ 4, 5, 6 }, .{});
 557            try container.end();
 558            if (whitespace) {
 559                try std.testing.expectEqualStrings(
 560                    \\.{ .first = .{
 561                    \\    1,
 562                    \\    2,
 563                    \\    3,
 564                    \\}, .second = .{
 565                    \\    4,
 566                    \\    5,
 567                    \\    6,
 568                    \\} }
 569                , aw.written());
 570            } else {
 571                try std.testing.expectEqualStrings(
 572                    ".{.first=.{1,2,3},.second=.{4,5,6}}",
 573                    aw.written(),
 574                );
 575            }
 576            aw.clearRetainingCapacity();
 577        }
 578    }
 579}
 580
 581test "std.zon stringify utf8 codepoints" {
 582    var aw: Writer.Allocating = .init(std.testing.allocator);
 583    var s: Serializer = .{ .writer = &aw.writer };
 584    defer aw.deinit();
 585
 586    // Printable ASCII
 587    try s.int('a');
 588    try std.testing.expectEqualStrings("97", aw.written());
 589    aw.clearRetainingCapacity();
 590
 591    try s.codePoint('a');
 592    try std.testing.expectEqualStrings("'a'", aw.written());
 593    aw.clearRetainingCapacity();
 594
 595    try s.value('a', .{ .emit_codepoint_literals = .always });
 596    try std.testing.expectEqualStrings("'a'", aw.written());
 597    aw.clearRetainingCapacity();
 598
 599    try s.value('a', .{ .emit_codepoint_literals = .printable_ascii });
 600    try std.testing.expectEqualStrings("'a'", aw.written());
 601    aw.clearRetainingCapacity();
 602
 603    try s.value('a', .{ .emit_codepoint_literals = .never });
 604    try std.testing.expectEqualStrings("97", aw.written());
 605    aw.clearRetainingCapacity();
 606
 607    // Short escaped codepoint
 608    try s.int('\n');
 609    try std.testing.expectEqualStrings("10", aw.written());
 610    aw.clearRetainingCapacity();
 611
 612    try s.codePoint('\n');
 613    try std.testing.expectEqualStrings("'\\n'", aw.written());
 614    aw.clearRetainingCapacity();
 615
 616    try s.value('\n', .{ .emit_codepoint_literals = .always });
 617    try std.testing.expectEqualStrings("'\\n'", aw.written());
 618    aw.clearRetainingCapacity();
 619
 620    try s.value('\n', .{ .emit_codepoint_literals = .printable_ascii });
 621    try std.testing.expectEqualStrings("10", aw.written());
 622    aw.clearRetainingCapacity();
 623
 624    try s.value('\n', .{ .emit_codepoint_literals = .never });
 625    try std.testing.expectEqualStrings("10", aw.written());
 626    aw.clearRetainingCapacity();
 627
 628    // Large codepoint
 629    try s.int('âš¡');
 630    try std.testing.expectEqualStrings("9889", aw.written());
 631    aw.clearRetainingCapacity();
 632
 633    try s.codePoint('âš¡');
 634    try std.testing.expectEqualStrings("'\\u{26a1}'", aw.written());
 635    aw.clearRetainingCapacity();
 636
 637    try s.value('âš¡', .{ .emit_codepoint_literals = .always });
 638    try std.testing.expectEqualStrings("'\\u{26a1}'", aw.written());
 639    aw.clearRetainingCapacity();
 640
 641    try s.value('âš¡', .{ .emit_codepoint_literals = .printable_ascii });
 642    try std.testing.expectEqualStrings("9889", aw.written());
 643    aw.clearRetainingCapacity();
 644
 645    try s.value('âš¡', .{ .emit_codepoint_literals = .never });
 646    try std.testing.expectEqualStrings("9889", aw.written());
 647    aw.clearRetainingCapacity();
 648
 649    // Invalid codepoint
 650    try s.codePoint(0x110000 + 1);
 651    try std.testing.expectEqualStrings("'\\u{110001}'", aw.written());
 652    aw.clearRetainingCapacity();
 653
 654    try s.int(0x110000 + 1);
 655    try std.testing.expectEqualStrings("1114113", aw.written());
 656    aw.clearRetainingCapacity();
 657
 658    try s.value(0x110000 + 1, .{ .emit_codepoint_literals = .always });
 659    try std.testing.expectEqualStrings("1114113", aw.written());
 660    aw.clearRetainingCapacity();
 661
 662    try s.value(0x110000 + 1, .{ .emit_codepoint_literals = .printable_ascii });
 663    try std.testing.expectEqualStrings("1114113", aw.written());
 664    aw.clearRetainingCapacity();
 665
 666    try s.value(0x110000 + 1, .{ .emit_codepoint_literals = .never });
 667    try std.testing.expectEqualStrings("1114113", aw.written());
 668    aw.clearRetainingCapacity();
 669
 670    // Valid codepoint, not a codepoint type
 671    try s.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .always });
 672    try std.testing.expectEqualStrings("97", aw.written());
 673    aw.clearRetainingCapacity();
 674
 675    try s.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .printable_ascii });
 676    try std.testing.expectEqualStrings("97", aw.written());
 677    aw.clearRetainingCapacity();
 678
 679    try s.value(@as(i32, 'a'), .{ .emit_codepoint_literals = .never });
 680    try std.testing.expectEqualStrings("97", aw.written());
 681    aw.clearRetainingCapacity();
 682
 683    // Make sure value options are passed to children
 684    try s.value(.{ .c = 'âš¡' }, .{ .emit_codepoint_literals = .always });
 685    try std.testing.expectEqualStrings(".{ .c = '\\u{26a1}' }", aw.written());
 686    aw.clearRetainingCapacity();
 687
 688    try s.value(.{ .c = 'âš¡' }, .{ .emit_codepoint_literals = .never });
 689    try std.testing.expectEqualStrings(".{ .c = 9889 }", aw.written());
 690    aw.clearRetainingCapacity();
 691}
 692
 693test "std.zon stringify strings" {
 694    var aw: Writer.Allocating = .init(std.testing.allocator);
 695    var s: Serializer = .{ .writer = &aw.writer };
 696    defer aw.deinit();
 697
 698    // Minimal case
 699    try s.string("abcâš¡\n");
 700    try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", aw.written());
 701    aw.clearRetainingCapacity();
 702
 703    try s.tuple("abcâš¡\n", .{});
 704    try std.testing.expectEqualStrings(
 705        \\.{
 706        \\    97,
 707        \\    98,
 708        \\    99,
 709        \\    226,
 710        \\    154,
 711        \\    161,
 712        \\    10,
 713        \\}
 714    , aw.written());
 715    aw.clearRetainingCapacity();
 716
 717    try s.value("abcâš¡\n", .{});
 718    try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", aw.written());
 719    aw.clearRetainingCapacity();
 720
 721    try s.value("abcâš¡\n", .{ .emit_strings_as_containers = true });
 722    try std.testing.expectEqualStrings(
 723        \\.{
 724        \\    97,
 725        \\    98,
 726        \\    99,
 727        \\    226,
 728        \\    154,
 729        \\    161,
 730        \\    10,
 731        \\}
 732    , aw.written());
 733    aw.clearRetainingCapacity();
 734
 735    // Value options are inherited by children
 736    try s.value(.{ .str = "abc" }, .{});
 737    try std.testing.expectEqualStrings(".{ .str = \"abc\" }", aw.written());
 738    aw.clearRetainingCapacity();
 739
 740    try s.value(.{ .str = "abc" }, .{ .emit_strings_as_containers = true });
 741    try std.testing.expectEqualStrings(
 742        \\.{ .str = .{
 743        \\    97,
 744        \\    98,
 745        \\    99,
 746        \\} }
 747    , aw.written());
 748    aw.clearRetainingCapacity();
 749
 750    // Arrays (rather than pointers to arrays) of u8s are not considered strings, so that data can
 751    // round trip correctly.
 752    try s.value("abc".*, .{});
 753    try std.testing.expectEqualStrings(
 754        \\.{
 755        \\    97,
 756        \\    98,
 757        \\    99,
 758        \\}
 759    , aw.written());
 760    aw.clearRetainingCapacity();
 761}
 762
 763test "std.zon stringify multiline strings" {
 764    var aw: Writer.Allocating = .init(std.testing.allocator);
 765    var s: Serializer = .{ .writer = &aw.writer };
 766    defer aw.deinit();
 767
 768    inline for (.{ true, false }) |whitespace| {
 769        s.options.whitespace = whitespace;
 770
 771        {
 772            try s.multilineString("", .{ .top_level = true });
 773            try std.testing.expectEqualStrings("\\\\", aw.written());
 774            aw.clearRetainingCapacity();
 775        }
 776
 777        {
 778            try s.multilineString("abcâš¡", .{ .top_level = true });
 779            try std.testing.expectEqualStrings("\\\\abcâš¡", aw.written());
 780            aw.clearRetainingCapacity();
 781        }
 782
 783        {
 784            try s.multilineString("abcâš¡\ndef", .{ .top_level = true });
 785            try std.testing.expectEqualStrings("\\\\abcâš¡\n\\\\def", aw.written());
 786            aw.clearRetainingCapacity();
 787        }
 788
 789        {
 790            try s.multilineString("abcâš¡\r\ndef", .{ .top_level = true });
 791            try std.testing.expectEqualStrings("\\\\abcâš¡\n\\\\def", aw.written());
 792            aw.clearRetainingCapacity();
 793        }
 794
 795        {
 796            try s.multilineString("\nabcâš¡", .{ .top_level = true });
 797            try std.testing.expectEqualStrings("\\\\\n\\\\abcâš¡", aw.written());
 798            aw.clearRetainingCapacity();
 799        }
 800
 801        {
 802            try s.multilineString("\r\nabcâš¡", .{ .top_level = true });
 803            try std.testing.expectEqualStrings("\\\\\n\\\\abcâš¡", aw.written());
 804            aw.clearRetainingCapacity();
 805        }
 806
 807        {
 808            try s.multilineString("abc\ndef", .{});
 809            if (whitespace) {
 810                try std.testing.expectEqualStrings("\n\\\\abc\n\\\\def\n", aw.written());
 811            } else {
 812                try std.testing.expectEqualStrings("\\\\abc\n\\\\def\n", aw.written());
 813            }
 814            aw.clearRetainingCapacity();
 815        }
 816
 817        {
 818            const str: []const u8 = &.{ 'a', '\r', 'c' };
 819            try s.string(str);
 820            try std.testing.expectEqualStrings("\"a\\rc\"", aw.written());
 821            aw.clearRetainingCapacity();
 822        }
 823
 824        {
 825            try std.testing.expectError(
 826                error.InnerCarriageReturn,
 827                s.multilineString(@as([]const u8, &.{ 'a', '\r', 'c' }), .{}),
 828            );
 829            try std.testing.expectError(
 830                error.InnerCarriageReturn,
 831                s.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\n' }), .{}),
 832            );
 833            try std.testing.expectError(
 834                error.InnerCarriageReturn,
 835                s.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\r', '\n' }), .{}),
 836            );
 837            try std.testing.expectEqualStrings("", aw.written());
 838            aw.clearRetainingCapacity();
 839        }
 840    }
 841}
 842
 843test "std.zon stringify skip default fields" {
 844    const Struct = struct {
 845        x: i32 = 2,
 846        y: i8,
 847        z: u32 = 4,
 848        inner1: struct { a: u8 = 'z', b: u8 = 'y', c: u8 } = .{
 849            .a = '1',
 850            .b = '2',
 851            .c = '3',
 852        },
 853        inner2: struct { u8, u8, u8 } = .{
 854            'a',
 855            'b',
 856            'c',
 857        },
 858        inner3: struct { u8, u8, u8 } = .{
 859            'a',
 860            'b',
 861            'c',
 862        },
 863    };
 864
 865    // Not skipping if not set
 866    try expectSerializeEqual(
 867        \\.{
 868        \\    .x = 2,
 869        \\    .y = 3,
 870        \\    .z = 4,
 871        \\    .inner1 = .{
 872        \\        .a = '1',
 873        \\        .b = '2',
 874        \\        .c = '3',
 875        \\    },
 876        \\    .inner2 = .{
 877        \\        'a',
 878        \\        'b',
 879        \\        'c',
 880        \\    },
 881        \\    .inner3 = .{
 882        \\        'a',
 883        \\        'b',
 884        \\        'd',
 885        \\    },
 886        \\}
 887    ,
 888        Struct{
 889            .y = 3,
 890            .z = 4,
 891            .inner1 = .{
 892                .a = '1',
 893                .b = '2',
 894                .c = '3',
 895            },
 896            .inner3 = .{
 897                'a',
 898                'b',
 899                'd',
 900            },
 901        },
 902        .{ .emit_codepoint_literals = .always },
 903    );
 904
 905    // Top level defaults
 906    try expectSerializeEqual(
 907        \\.{ .y = 3, .inner3 = .{
 908        \\    'a',
 909        \\    'b',
 910        \\    'd',
 911        \\} }
 912    ,
 913        Struct{
 914            .y = 3,
 915            .z = 4,
 916            .inner1 = .{
 917                .a = '1',
 918                .b = '2',
 919                .c = '3',
 920            },
 921            .inner3 = .{
 922                'a',
 923                'b',
 924                'd',
 925            },
 926        },
 927        .{
 928            .emit_default_optional_fields = false,
 929            .emit_codepoint_literals = .always,
 930        },
 931    );
 932
 933    // Inner types having defaults, and defaults changing the number of fields affecting the
 934    // formatting
 935    try expectSerializeEqual(
 936        \\.{
 937        \\    .y = 3,
 938        \\    .inner1 = .{ .b = '2', .c = '3' },
 939        \\    .inner3 = .{
 940        \\        'a',
 941        \\        'b',
 942        \\        'd',
 943        \\    },
 944        \\}
 945    ,
 946        Struct{
 947            .y = 3,
 948            .z = 4,
 949            .inner1 = .{
 950                .a = 'z',
 951                .b = '2',
 952                .c = '3',
 953            },
 954            .inner3 = .{
 955                'a',
 956                'b',
 957                'd',
 958            },
 959        },
 960        .{
 961            .emit_default_optional_fields = false,
 962            .emit_codepoint_literals = .always,
 963        },
 964    );
 965
 966    const DefaultStrings = struct {
 967        foo: []const u8 = "abc",
 968    };
 969    try expectSerializeEqual(
 970        \\.{}
 971    ,
 972        DefaultStrings{ .foo = "abc" },
 973        .{ .emit_default_optional_fields = false },
 974    );
 975    try expectSerializeEqual(
 976        \\.{ .foo = "abcd" }
 977    ,
 978        DefaultStrings{ .foo = "abcd" },
 979        .{ .emit_default_optional_fields = false },
 980    );
 981}
 982
 983test "std.zon depth limits" {
 984    var aw: Writer.Allocating = .init(std.testing.allocator);
 985    const bw = &aw.writer;
 986    defer aw.deinit();
 987
 988    const Recurse = struct { r: []const @This() };
 989
 990    // Normal operation
 991    try serializeMaxDepth(.{ 1, .{ 2, 3 } }, .{}, bw, 16);
 992    try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", aw.written());
 993    aw.clearRetainingCapacity();
 994
 995    try serializeArbitraryDepth(.{ 1, .{ 2, 3 } }, .{}, bw);
 996    try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", aw.written());
 997    aw.clearRetainingCapacity();
 998
 999    // Max depth failing on non recursive type
1000    try std.testing.expectError(
1001        error.ExceededMaxDepth,
1002        serializeMaxDepth(.{ 1, .{ 2, .{ 3, 4 } } }, .{}, bw, 3),
1003    );
1004    try std.testing.expectEqualStrings("", aw.written());
1005    aw.clearRetainingCapacity();
1006
1007    // Max depth passing on recursive type
1008    {
1009        const maybe_recurse = Recurse{ .r = &.{} };
1010        try serializeMaxDepth(maybe_recurse, .{}, bw, 2);
1011        try std.testing.expectEqualStrings(".{ .r = .{} }", aw.written());
1012        aw.clearRetainingCapacity();
1013    }
1014
1015    // Unchecked passing on recursive type
1016    {
1017        const maybe_recurse = Recurse{ .r = &.{} };
1018        try serializeArbitraryDepth(maybe_recurse, .{}, bw);
1019        try std.testing.expectEqualStrings(".{ .r = .{} }", aw.written());
1020        aw.clearRetainingCapacity();
1021    }
1022
1023    // Max depth failing on recursive type due to depth
1024    {
1025        var maybe_recurse = Recurse{ .r = &.{} };
1026        maybe_recurse.r = &.{.{ .r = &.{} }};
1027        try std.testing.expectError(
1028            error.ExceededMaxDepth,
1029            serializeMaxDepth(maybe_recurse, .{}, bw, 2),
1030        );
1031        try std.testing.expectEqualStrings("", aw.written());
1032        aw.clearRetainingCapacity();
1033    }
1034
1035    // Same but for a slice
1036    {
1037        var temp: [1]Recurse = .{.{ .r = &.{} }};
1038        const maybe_recurse: []const Recurse = &temp;
1039
1040        try std.testing.expectError(
1041            error.ExceededMaxDepth,
1042            serializeMaxDepth(maybe_recurse, .{}, bw, 2),
1043        );
1044        try std.testing.expectEqualStrings("", aw.written());
1045        aw.clearRetainingCapacity();
1046
1047        var s: Serializer = .{ .writer = bw };
1048
1049        try std.testing.expectError(
1050            error.ExceededMaxDepth,
1051            s.tupleMaxDepth(maybe_recurse, .{}, 2),
1052        );
1053        try std.testing.expectEqualStrings("", aw.written());
1054        aw.clearRetainingCapacity();
1055
1056        try s.tupleArbitraryDepth(maybe_recurse, .{});
1057        try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.written());
1058        aw.clearRetainingCapacity();
1059    }
1060
1061    // A slice succeeding
1062    {
1063        var temp: [1]Recurse = .{.{ .r = &.{} }};
1064        const maybe_recurse: []const Recurse = &temp;
1065
1066        try serializeMaxDepth(maybe_recurse, .{}, bw, 3);
1067        try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.written());
1068        aw.clearRetainingCapacity();
1069
1070        var s: Serializer = .{ .writer = bw };
1071
1072        try s.tupleMaxDepth(maybe_recurse, .{}, 3);
1073        try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.written());
1074        aw.clearRetainingCapacity();
1075
1076        try s.tupleArbitraryDepth(maybe_recurse, .{});
1077        try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.written());
1078        aw.clearRetainingCapacity();
1079    }
1080
1081    // Max depth failing on recursive type due to recursion
1082    {
1083        var temp: [1]Recurse = .{.{ .r = &.{} }};
1084        temp[0].r = &temp;
1085        const maybe_recurse: []const Recurse = &temp;
1086
1087        try std.testing.expectError(
1088            error.ExceededMaxDepth,
1089            serializeMaxDepth(maybe_recurse, .{}, bw, 128),
1090        );
1091        try std.testing.expectEqualStrings("", aw.written());
1092        aw.clearRetainingCapacity();
1093
1094        var s: Serializer = .{ .writer = bw };
1095        try std.testing.expectError(
1096            error.ExceededMaxDepth,
1097            s.tupleMaxDepth(maybe_recurse, .{}, 128),
1098        );
1099        try std.testing.expectEqualStrings("", aw.written());
1100        aw.clearRetainingCapacity();
1101    }
1102
1103    // Max depth on other parts of the lower level API
1104    {
1105        var s: Serializer = .{ .writer = bw };
1106
1107        const maybe_recurse: []const Recurse = &.{};
1108
1109        try std.testing.expectError(error.ExceededMaxDepth, s.valueMaxDepth(1, .{}, 0));
1110        try s.valueMaxDepth(2, .{}, 1);
1111        try s.value(3, .{});
1112        try s.valueArbitraryDepth(maybe_recurse, .{});
1113
1114        var wip_struct = try s.beginStruct(.{});
1115        try std.testing.expectError(error.ExceededMaxDepth, wip_struct.fieldMaxDepth("a", 1, .{}, 0));
1116        try wip_struct.fieldMaxDepth("b", 4, .{}, 1);
1117        try wip_struct.field("c", 5, .{});
1118        try wip_struct.fieldArbitraryDepth("d", maybe_recurse, .{});
1119        try wip_struct.end();
1120
1121        var t = try s.beginTuple(.{});
1122        try std.testing.expectError(error.ExceededMaxDepth, t.fieldMaxDepth(1, .{}, 0));
1123        try t.fieldMaxDepth(6, .{}, 1);
1124        try t.field(7, .{});
1125        try t.fieldArbitraryDepth(maybe_recurse, .{});
1126        try t.end();
1127
1128        var a = try s.beginTuple(.{});
1129        try std.testing.expectError(error.ExceededMaxDepth, a.fieldMaxDepth(1, .{}, 0));
1130        try a.fieldMaxDepth(8, .{}, 1);
1131        try a.field(9, .{});
1132        try a.fieldArbitraryDepth(maybe_recurse, .{});
1133        try a.end();
1134
1135        try std.testing.expectEqualStrings(
1136            \\23.{}.{
1137            \\    .b = 4,
1138            \\    .c = 5,
1139            \\    .d = .{},
1140            \\}.{
1141            \\    6,
1142            \\    7,
1143            \\    .{},
1144            \\}.{
1145            \\    8,
1146            \\    9,
1147            \\    .{},
1148            \\}
1149        , aw.written());
1150    }
1151}
1152
1153test "std.zon stringify primitives" {
1154    // Issue: https://github.com/ziglang/zig/issues/20880
1155    if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest;
1156
1157    try expectSerializeEqual(
1158        \\.{
1159        \\    .a = 1.5,
1160        \\    .b = 0.3333333333333333333333333333333333,
1161        \\    .c = 3.1415926535897932384626433832795028,
1162        \\    .d = 0,
1163        \\    .e = 0,
1164        \\    .f = -0.0,
1165        \\    .g = inf,
1166        \\    .h = -inf,
1167        \\    .i = nan,
1168        \\}
1169    ,
1170        .{
1171            .a = @as(f128, 1.5), // Make sure explicit f128s work
1172            .b = 1.0 / 3.0,
1173            .c = std.math.pi,
1174            .d = 0.0,
1175            .e = -0.0,
1176            .f = @as(f128, -0.0),
1177            .g = std.math.inf(f32),
1178            .h = -std.math.inf(f32),
1179            .i = std.math.nan(f32),
1180        },
1181        .{},
1182    );
1183
1184    try expectSerializeEqual(
1185        \\.{
1186        \\    .a = 18446744073709551616,
1187        \\    .b = -18446744073709551616,
1188        \\    .c = 680564733841876926926749214863536422912,
1189        \\    .d = -680564733841876926926749214863536422912,
1190        \\    .e = 0,
1191        \\}
1192    ,
1193        .{
1194            .a = 18446744073709551616,
1195            .b = -18446744073709551616,
1196            .c = 680564733841876926926749214863536422912,
1197            .d = -680564733841876926926749214863536422912,
1198            .e = 0,
1199        },
1200        .{},
1201    );
1202
1203    try expectSerializeEqual(
1204        \\.{
1205        \\    .a = true,
1206        \\    .b = false,
1207        \\    .c = .foo,
1208        \\    .e = null,
1209        \\}
1210    ,
1211        .{
1212            .a = true,
1213            .b = false,
1214            .c = .foo,
1215            .e = null,
1216        },
1217        .{},
1218    );
1219
1220    const Struct = struct { x: f32, y: f32 };
1221    try expectSerializeEqual(
1222        ".{ .a = .{ .x = 1, .y = 2 }, .b = null }",
1223        .{
1224            .a = @as(?Struct, .{ .x = 1, .y = 2 }),
1225            .b = @as(?Struct, null),
1226        },
1227        .{},
1228    );
1229
1230    const E = enum(u8) {
1231        foo,
1232        bar,
1233    };
1234    try expectSerializeEqual(
1235        ".{ .a = .foo, .b = .foo }",
1236        .{
1237            .a = .foo,
1238            .b = E.foo,
1239        },
1240        .{},
1241    );
1242}
1243
1244test "std.zon stringify ident" {
1245    var aw: Writer.Allocating = .init(std.testing.allocator);
1246    var s: Serializer = .{ .writer = &aw.writer };
1247    defer aw.deinit();
1248
1249    try expectSerializeEqual(".{ .a = 0 }", .{ .a = 0 }, .{});
1250    try s.ident("a");
1251    try std.testing.expectEqualStrings(".a", aw.written());
1252    aw.clearRetainingCapacity();
1253
1254    try s.ident("foo_1");
1255    try std.testing.expectEqualStrings(".foo_1", aw.written());
1256    aw.clearRetainingCapacity();
1257
1258    try s.ident("_foo_1");
1259    try std.testing.expectEqualStrings("._foo_1", aw.written());
1260    aw.clearRetainingCapacity();
1261
1262    try s.ident("foo bar");
1263    try std.testing.expectEqualStrings(".@\"foo bar\"", aw.written());
1264    aw.clearRetainingCapacity();
1265
1266    try s.ident("1foo");
1267    try std.testing.expectEqualStrings(".@\"1foo\"", aw.written());
1268    aw.clearRetainingCapacity();
1269
1270    try s.ident("var");
1271    try std.testing.expectEqualStrings(".@\"var\"", aw.written());
1272    aw.clearRetainingCapacity();
1273
1274    try s.ident("true");
1275    try std.testing.expectEqualStrings(".true", aw.written());
1276    aw.clearRetainingCapacity();
1277
1278    try s.ident("_");
1279    try std.testing.expectEqualStrings("._", aw.written());
1280    aw.clearRetainingCapacity();
1281
1282    const Enum = enum {
1283        @"foo bar",
1284    };
1285    try expectSerializeEqual(".{ .@\"var\" = .@\"foo bar\", .@\"1\" = .@\"foo bar\" }", .{
1286        .@"var" = .@"foo bar",
1287        .@"1" = Enum.@"foo bar",
1288    }, .{});
1289}
1290
1291test "std.zon stringify as tuple" {
1292    var aw: Writer.Allocating = .init(std.testing.allocator);
1293    var s: Serializer = .{ .writer = &aw.writer };
1294    defer aw.deinit();
1295
1296    // Tuples
1297    try s.tuple(.{ 1, 2 }, .{});
1298    try std.testing.expectEqualStrings(".{ 1, 2 }", aw.written());
1299    aw.clearRetainingCapacity();
1300
1301    // Slice
1302    try s.tuple(@as([]const u8, &.{ 1, 2 }), .{});
1303    try std.testing.expectEqualStrings(".{ 1, 2 }", aw.written());
1304    aw.clearRetainingCapacity();
1305
1306    // Array
1307    try s.tuple([2]u8{ 1, 2 }, .{});
1308    try std.testing.expectEqualStrings(".{ 1, 2 }", aw.written());
1309    aw.clearRetainingCapacity();
1310}
1311
1312test "std.zon stringify as float" {
1313    var aw: Writer.Allocating = .init(std.testing.allocator);
1314    var s: Serializer = .{ .writer = &aw.writer };
1315    defer aw.deinit();
1316
1317    // Comptime float
1318    try s.float(2.5);
1319    try std.testing.expectEqualStrings("2.5", aw.written());
1320    aw.clearRetainingCapacity();
1321
1322    // Sized float
1323    try s.float(@as(f32, 2.5));
1324    try std.testing.expectEqualStrings("2.5", aw.written());
1325    aw.clearRetainingCapacity();
1326}
1327
1328test "std.zon stringify vector" {
1329    try expectSerializeEqual(
1330        \\.{
1331        \\    .{},
1332        \\    .{
1333        \\        true,
1334        \\        false,
1335        \\        true,
1336        \\    },
1337        \\    .{},
1338        \\    .{
1339        \\        1.5,
1340        \\        2.5,
1341        \\        3.5,
1342        \\    },
1343        \\    .{},
1344        \\    .{
1345        \\        2,
1346        \\        4,
1347        \\        6,
1348        \\    },
1349        \\    .{ 1, 2 },
1350        \\    .{
1351        \\        3,
1352        \\        4,
1353        \\        null,
1354        \\    },
1355        \\}
1356    ,
1357        .{
1358            @Vector(0, bool){},
1359            @Vector(3, bool){ true, false, true },
1360            @Vector(0, f32){},
1361            @Vector(3, f32){ 1.5, 2.5, 3.5 },
1362            @Vector(0, u8){},
1363            @Vector(3, u8){ 2, 4, 6 },
1364            @Vector(2, *const u8){ &1, &2 },
1365            @Vector(3, ?*const u8){ &3, &4, null },
1366        },
1367        .{},
1368    );
1369}
1370
1371test "std.zon pointers" {
1372    // Primitive with varying levels of pointers
1373    try expectSerializeEqual("10", &@as(u32, 10), .{});
1374    try expectSerializeEqual("10", &&@as(u32, 10), .{});
1375    try expectSerializeEqual("10", &&&@as(u32, 10), .{});
1376
1377    // Primitive optional with varying levels of pointers
1378    try expectSerializeEqual("10", @as(?*const u32, &10), .{});
1379    try expectSerializeEqual("null", @as(?*const u32, null), .{});
1380    try expectSerializeEqual("10", @as(?*const u32, &10), .{});
1381    try expectSerializeEqual("null", @as(*const ?u32, &null), .{});
1382
1383    try expectSerializeEqual("10", @as(?*const *const u32, &&10), .{});
1384    try expectSerializeEqual("null", @as(?*const *const u32, null), .{});
1385    try expectSerializeEqual("10", @as(*const ?*const u32, &&10), .{});
1386    try expectSerializeEqual("null", @as(*const ?*const u32, &null), .{});
1387    try expectSerializeEqual("10", @as(*const *const ?u32, &&10), .{});
1388    try expectSerializeEqual("null", @as(*const *const ?u32, &&null), .{});
1389
1390    try expectSerializeEqual(".{ 1, 2 }", &[2]u32{ 1, 2 }, .{});
1391
1392    // A complicated type with nested internal pointers and string allocations
1393    {
1394        const Inner = struct {
1395            f1: *const ?*const []const u8,
1396            f2: *const ?*const []const u8,
1397        };
1398        const Outer = struct {
1399            f1: *const ?*const Inner,
1400            f2: *const ?*const Inner,
1401        };
1402        const val: ?*const Outer = &.{
1403            .f1 = &&.{
1404                .f1 = &null,
1405                .f2 = &&"foo",
1406            },
1407            .f2 = &null,
1408        };
1409
1410        try expectSerializeEqual(
1411            \\.{ .f1 = .{ .f1 = null, .f2 = "foo" }, .f2 = null }
1412        , val, .{});
1413    }
1414}
1415
1416test "std.zon tuple/struct field" {
1417    var aw: Writer.Allocating = .init(std.testing.allocator);
1418    var s: Serializer = .{ .writer = &aw.writer };
1419    defer aw.deinit();
1420
1421    // Test on structs
1422    {
1423        var root = try s.beginStruct(.{});
1424        {
1425            var tuple = try root.beginTupleField("foo", .{});
1426            try tuple.field(0, .{});
1427            try tuple.field(1, .{});
1428            try tuple.end();
1429        }
1430        {
1431            var strct = try root.beginStructField("bar", .{});
1432            try strct.field("a", 0, .{});
1433            try strct.field("b", 1, .{});
1434            try strct.end();
1435        }
1436        try root.end();
1437
1438        try std.testing.expectEqualStrings(
1439            \\.{
1440            \\    .foo = .{
1441            \\        0,
1442            \\        1,
1443            \\    },
1444            \\    .bar = .{
1445            \\        .a = 0,
1446            \\        .b = 1,
1447            \\    },
1448            \\}
1449        , aw.written());
1450        aw.clearRetainingCapacity();
1451    }
1452
1453    // Test on tuples
1454    {
1455        var root = try s.beginTuple(.{});
1456        {
1457            var tuple = try root.beginTupleField(.{});
1458            try tuple.field(0, .{});
1459            try tuple.field(1, .{});
1460            try tuple.end();
1461        }
1462        {
1463            var strct = try root.beginStructField(.{});
1464            try strct.field("a", 0, .{});
1465            try strct.field("b", 1, .{});
1466            try strct.end();
1467        }
1468        try root.end();
1469
1470        try std.testing.expectEqualStrings(
1471            \\.{
1472            \\    .{
1473            \\        0,
1474            \\        1,
1475            \\    },
1476            \\    .{
1477            \\        .a = 0,
1478            \\        .b = 1,
1479            \\    },
1480            \\}
1481        , aw.written());
1482        aw.clearRetainingCapacity();
1483    }
1484}