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}