authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-19 17:33:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-19 18:27:09-07:00
log0fb7a0a94bf6f9e329008d8b5b819a8d1d7124b0
tree13825170aa0c520a49f006a1e0f35990ee9af6bc
parent737b13357ed04a33bfea17f58674deba8f17f51e

std.zon: better namespace for Serializer


7 files changed, 957 insertions(+), 981 deletions(-)

lib/std/zon.zig+1
...@@ -38,6 +38,7 @@...@@ -38,6 +38,7 @@
3838
39pub const parse = @import("zon/parse.zig");39pub const parse = @import("zon/parse.zig");
40pub const stringify = @import("zon/stringify.zig");40pub const stringify = @import("zon/stringify.zig");
41pub const Serializer = @import("zon/Serializer.zig");
4142
42test {43test {
43 _ = parse;44 _ = parse;
lib/std/zon/Serializer.zig created+929
...@@ -0,0 +1,929 @@
1//! Lower level control over serialization, you can create a new instance with `serializer`.
2//!
3//! Useful when you want control over which fields are serialized, how they're represented,
4//! or want to write a ZON object that does not exist in memory.
5//!
6//! You can serialize values with `value`. To serialize recursive types, the following are provided:
7//! * `valueMaxDepth`
8//! * `valueArbitraryDepth`
9//!
10//! You can also serialize values using specific notations:
11//! * `int`
12//! * `float`
13//! * `codePoint`
14//! * `tuple`
15//! * `tupleMaxDepth`
16//! * `tupleArbitraryDepth`
17//! * `string`
18//! * `multilineString`
19//!
20//! For manual serialization of containers, see:
21//! * `beginStruct`
22//! * `beginTuple`
23
24options: Options = .{},
25indent_level: u8 = 0,
26writer: *Writer,
27
28const Serializer = @This();
29const std = @import("std");
30const assert = std.debug.assert;
31const Writer = std.Io.Writer;
32
33pub const Error = Writer.Error;
34pub const DepthError = Error || error{ExceededMaxDepth};
35
36pub const Options = struct {
37 /// If false, only syntactically necessary whitespace is emitted.
38 whitespace: bool = true,
39};
40
41/// Options for manual serialization of container types.
42pub const ContainerOptions = struct {
43 /// The whitespace style that should be used for this container. Ignored if whitespace is off.
44 whitespace_style: union(enum) {
45 /// If true, wrap every field. If false do not.
46 wrap: bool,
47 /// Automatically decide whether to wrap or not based on the number of fields. Following
48 /// the standard rule of thumb, containers with more than two fields are wrapped.
49 fields: usize,
50 } = .{ .wrap = true },
51
52 fn shouldWrap(self: ContainerOptions) bool {
53 return switch (self.whitespace_style) {
54 .wrap => |wrap| wrap,
55 .fields => |fields| fields > 2,
56 };
57 }
58};
59
60/// Options for serialization of an individual value.
61///
62/// See `SerializeOptions` for more information on these options.
63pub const ValueOptions = struct {
64 emit_codepoint_literals: EmitCodepointLiterals = .never,
65 emit_strings_as_containers: bool = false,
66 emit_default_optional_fields: bool = true,
67};
68
69/// Determines when to emit Unicode code point literals as opposed to integer literals.
70pub const EmitCodepointLiterals = enum {
71 /// Never emit Unicode code point literals.
72 never,
73 /// Emit Unicode code point literals for any `u8` in the printable ASCII range.
74 printable_ascii,
75 /// Emit Unicode code point literals for any unsigned integer with 21 bits or fewer
76 /// whose value is a valid non-surrogate code point.
77 always,
78
79 /// If the value should be emitted as a Unicode codepoint, return it as a u21.
80 fn emitAsCodepoint(self: @This(), val: anytype) ?u21 {
81 // Rule out incompatible integer types
82 switch (@typeInfo(@TypeOf(val))) {
83 .int => |int_info| if (int_info.signedness == .signed or int_info.bits > 21) {
84 return null;
85 },
86 .comptime_int => {},
87 else => comptime unreachable,
88 }
89
90 // Return null if the value shouldn't be printed as a Unicode codepoint, or the value casted
91 // to a u21 if it should.
92 switch (self) {
93 .always => {
94 const c = std.math.cast(u21, val) orelse return null;
95 if (!std.unicode.utf8ValidCodepoint(c)) return null;
96 return c;
97 },
98 .printable_ascii => {
99 const c = std.math.cast(u8, val) orelse return null;
100 if (!std.ascii.isPrint(c)) return null;
101 return c;
102 },
103 .never => {
104 return null;
105 },
106 }
107 }
108};
109
110/// Serialize a value, similar to `serialize`.
111pub fn value(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
112 comptime assert(!typeIsRecursive(@TypeOf(val)));
113 return self.valueArbitraryDepth(val, options);
114}
115
116/// Serialize a value, similar to `serializeMaxDepth`.
117/// Can return `error.ExceededMaxDepth`.
118pub fn valueMaxDepth(self: *Serializer, val: anytype, options: ValueOptions, depth: usize) DepthError!void {
119 try checkValueDepth(val, depth);
120 return self.valueArbitraryDepth(val, options);
121}
122
123/// Serialize a value, similar to `serializeArbitraryDepth`.
124pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
125 comptime assert(canSerializeType(@TypeOf(val)));
126 switch (@typeInfo(@TypeOf(val))) {
127 .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {
128 self.codePoint(c) catch |err| switch (err) {
129 error.InvalidCodepoint => unreachable, // Already validated
130 else => |e| return e,
131 };
132 } else {
133 try self.int(val);
134 },
135 .float, .comptime_float => try self.float(val),
136 .bool, .null => try self.writer.print("{}", .{val}),
137 .enum_literal => try self.ident(@tagName(val)),
138 .@"enum" => try self.ident(@tagName(val)),
139 .pointer => |pointer| {
140 // Try to serialize as a string
141 const item: ?type = switch (@typeInfo(pointer.child)) {
142 .array => |array| array.child,
143 else => if (pointer.size == .slice) pointer.child else null,
144 };
145 if (item == u8 and
146 (pointer.sentinel() == null or pointer.sentinel() == 0) and
147 !options.emit_strings_as_containers)
148 {
149 return try self.string(val);
150 }
151
152 // Serialize as either a tuple or as the child type
153 switch (pointer.size) {
154 .slice => try self.tupleImpl(val, options),
155 .one => try self.valueArbitraryDepth(val.*, options),
156 else => comptime unreachable,
157 }
158 },
159 .array => {
160 var container = try self.beginTuple(
161 .{ .whitespace_style = .{ .fields = val.len } },
162 );
163 for (val) |item_val| {
164 try container.fieldArbitraryDepth(item_val, options);
165 }
166 try container.end();
167 },
168 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
169 var container = try self.beginTuple(
170 .{ .whitespace_style = .{ .fields = @"struct".fields.len } },
171 );
172 inline for (val) |field_value| {
173 try container.fieldArbitraryDepth(field_value, options);
174 }
175 try container.end();
176 } else {
177 // Decide which fields to emit
178 const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: {
179 break :b .{ @"struct".fields.len, @splat(false) };
180 } else b: {
181 var fields = @"struct".fields.len;
182 var skipped: [@"struct".fields.len]bool = @splat(false);
183 inline for (@"struct".fields, &skipped) |field_info, *skip| {
184 if (field_info.default_value_ptr) |ptr| {
185 const default: *const field_info.type = @ptrCast(@alignCast(ptr));
186 const field_value = @field(val, field_info.name);
187 if (std.meta.eql(field_value, default.*)) {
188 skip.* = true;
189 fields -= 1;
190 }
191 }
192 }
193 break :b .{ fields, skipped };
194 };
195
196 // Emit those fields
197 var container = try self.beginStruct(
198 .{ .whitespace_style = .{ .fields = fields } },
199 );
200 inline for (@"struct".fields, skipped) |field_info, skip| {
201 if (!skip) {
202 try container.fieldArbitraryDepth(
203 field_info.name,
204 @field(val, field_info.name),
205 options,
206 );
207 }
208 }
209 try container.end();
210 },
211 .@"union" => |@"union"| {
212 comptime assert(@"union".tag_type != null);
213 switch (val) {
214 inline else => |pl, tag| if (@TypeOf(pl) == void)
215 try self.writer.print(".{s}", .{@tagName(tag)})
216 else {
217 var container = try self.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
218
219 try container.fieldArbitraryDepth(
220 @tagName(tag),
221 pl,
222 options,
223 );
224
225 try container.end();
226 },
227 }
228 },
229 .optional => if (val) |inner| {
230 try self.valueArbitraryDepth(inner, options);
231 } else {
232 try self.writer.writeAll("null");
233 },
234 .vector => |vector| {
235 var container = try self.beginTuple(
236 .{ .whitespace_style = .{ .fields = vector.len } },
237 );
238 for (0..vector.len) |i| {
239 try container.fieldArbitraryDepth(val[i], options);
240 }
241 try container.end();
242 },
243
244 else => comptime unreachable,
245 }
246}
247
248/// Serialize an integer.
249pub fn int(self: *Serializer, val: anytype) Error!void {
250 try self.writer.printInt(val, 10, .lower, .{});
251}
252
253/// Serialize a float.
254pub fn float(self: *Serializer, val: anytype) Error!void {
255 switch (@typeInfo(@TypeOf(val))) {
256 .float => if (std.math.isNan(val)) {
257 return self.writer.writeAll("nan");
258 } else if (std.math.isPositiveInf(val)) {
259 return self.writer.writeAll("inf");
260 } else if (std.math.isNegativeInf(val)) {
261 return self.writer.writeAll("-inf");
262 } else if (std.math.isNegativeZero(val)) {
263 return self.writer.writeAll("-0.0");
264 } else {
265 try self.writer.print("{d}", .{val});
266 },
267 .comptime_float => if (val == 0) {
268 return self.writer.writeAll("0");
269 } else {
270 try self.writer.print("{d}", .{val});
271 },
272 else => comptime unreachable,
273 }
274}
275
276/// Serialize `name` as an identifier prefixed with `.`.
277///
278/// Escapes the identifier if necessary.
279pub fn ident(self: *Serializer, name: []const u8) Error!void {
280 try self.writer.print(".{f}", .{std.zig.fmtIdPU(name)});
281}
282
283pub const CodePointError = Error || error{InvalidCodepoint};
284
285/// Serialize `val` as a Unicode codepoint.
286///
287/// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint.
288pub fn codePoint(self: *Serializer, val: u21) CodePointError!void {
289 try self.writer.print("'{f}'", .{std.zig.fmtChar(val)});
290}
291
292/// Like `value`, but always serializes `val` as a tuple.
293///
294/// Will fail at comptime if `val` is not a tuple, array, pointer to an array, or slice.
295pub fn tuple(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
296 comptime assert(!typeIsRecursive(@TypeOf(val)));
297 try self.tupleArbitraryDepth(val, options);
298}
299
300/// Like `tuple`, but recursive types are allowed.
301///
302/// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
303pub fn tupleMaxDepth(
304 self: *Serializer,
305 val: anytype,
306 options: ValueOptions,
307 depth: usize,
308) DepthError!void {
309 try checkValueDepth(val, depth);
310 try self.tupleArbitraryDepth(val, options);
311}
312
313/// Like `tuple`, but recursive types are allowed.
314///
315/// It is the caller's responsibility to ensure that `val` does not contain cycles.
316pub fn tupleArbitraryDepth(
317 self: *Serializer,
318 val: anytype,
319 options: ValueOptions,
320) Error!void {
321 try self.tupleImpl(val, options);
322}
323
324fn tupleImpl(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
325 comptime assert(canSerializeType(@TypeOf(val)));
326 switch (@typeInfo(@TypeOf(val))) {
327 .@"struct" => {
328 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
329 inline for (val) |item_val| {
330 try container.fieldArbitraryDepth(item_val, options);
331 }
332 try container.end();
333 },
334 .pointer, .array => {
335 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
336 for (val) |item_val| {
337 try container.fieldArbitraryDepth(item_val, options);
338 }
339 try container.end();
340 },
341 else => comptime unreachable,
342 }
343}
344
345/// Like `value`, but always serializes `val` as a string.
346pub fn string(self: *Serializer, val: []const u8) Error!void {
347 try self.writer.print("\"{f}\"", .{std.zig.fmtString(val)});
348}
349
350/// Options for formatting multiline strings.
351pub const MultilineStringOptions = struct {
352 /// If top level is true, whitespace before and after the multiline string is elided.
353 /// If it is true, a newline is printed, then the value, followed by a newline, and if
354 /// whitespace is true any necessary indentation follows.
355 top_level: bool = false,
356};
357
358pub const MultilineStringError = Error || error{InnerCarriageReturn};
359
360/// Like `value`, but always serializes to a multiline string literal.
361///
362/// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline,
363/// since multiline strings cannot represent CR without a following newline.
364pub fn multilineString(
365 self: *Serializer,
366 val: []const u8,
367 options: MultilineStringOptions,
368) MultilineStringError!void {
369 // Make sure the string does not contain any carriage returns not followed by a newline
370 var i: usize = 0;
371 while (i < val.len) : (i += 1) {
372 if (val[i] == '\r') {
373 if (i + 1 < val.len) {
374 if (val[i + 1] == '\n') {
375 i += 1;
376 continue;
377 }
378 }
379 return error.InnerCarriageReturn;
380 }
381 }
382
383 if (!options.top_level) {
384 try self.newline();
385 try self.indent();
386 }
387
388 try self.writer.writeAll("\\\\");
389 for (val) |c| {
390 if (c != '\r') {
391 try self.writer.writeByte(c); // We write newlines here even if whitespace off
392 if (c == '\n') {
393 try self.indent();
394 try self.writer.writeAll("\\\\");
395 }
396 }
397 }
398
399 if (!options.top_level) {
400 try self.writer.writeByte('\n'); // Even if whitespace off
401 try self.indent();
402 }
403}
404
405/// Create a `Struct` for writing ZON structs field by field.
406pub fn beginStruct(self: *Serializer, options: ContainerOptions) Error!Struct {
407 return Struct.begin(self, options);
408}
409
410/// Creates a `Tuple` for writing ZON tuples field by field.
411pub fn beginTuple(self: *Serializer, options: ContainerOptions) Error!Tuple {
412 return Tuple.begin(self, options);
413}
414
415fn indent(self: *Serializer) Error!void {
416 if (self.options.whitespace) {
417 try self.writer.splatByteAll(' ', 4 * self.indent_level);
418 }
419}
420
421fn newline(self: *Serializer) Error!void {
422 if (self.options.whitespace) {
423 try self.writer.writeByte('\n');
424 }
425}
426
427fn newlineOrSpace(self: *Serializer, len: usize) Error!void {
428 if (self.containerShouldWrap(len)) {
429 try self.newline();
430 } else {
431 try self.space();
432 }
433}
434
435fn space(self: *Serializer) Error!void {
436 if (self.options.whitespace) {
437 try self.writer.writeByte(' ');
438 }
439}
440
441/// Writes ZON tuples field by field.
442pub const Tuple = struct {
443 container: Container,
444
445 fn begin(parent: *Serializer, options: ContainerOptions) Error!Tuple {
446 return .{
447 .container = try Container.begin(parent, .anon, options),
448 };
449 }
450
451 /// Finishes serializing the tuple.
452 ///
453 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
454 pub fn end(self: *Tuple) Error!void {
455 try self.container.end();
456 self.* = undefined;
457 }
458
459 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
460 pub fn field(
461 self: *Tuple,
462 val: anytype,
463 options: ValueOptions,
464 ) Error!void {
465 try self.container.field(null, val, options);
466 }
467
468 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
469 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
470 pub fn fieldMaxDepth(
471 self: *Tuple,
472 val: anytype,
473 options: ValueOptions,
474 depth: usize,
475 ) DepthError!void {
476 try self.container.fieldMaxDepth(null, val, options, depth);
477 }
478
479 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
480 /// `valueArbitraryDepth`.
481 pub fn fieldArbitraryDepth(
482 self: *Tuple,
483 val: anytype,
484 options: ValueOptions,
485 ) Error!void {
486 try self.container.fieldArbitraryDepth(null, val, options);
487 }
488
489 /// Starts a field with a struct as a value. Returns the struct.
490 pub fn beginStructField(
491 self: *Tuple,
492 options: ContainerOptions,
493 ) Error!Struct {
494 try self.fieldPrefix();
495 return self.container.serializer.beginStruct(options);
496 }
497
498 /// Starts a field with a tuple as a value. Returns the tuple.
499 pub fn beginTupleField(
500 self: *Tuple,
501 options: ContainerOptions,
502 ) Error!Tuple {
503 try self.fieldPrefix();
504 return self.container.serializer.beginTuple(options);
505 }
506
507 /// Print a field prefix. This prints any necessary commas, and whitespace as
508 /// configured. Useful if you want to serialize the field value yourself.
509 pub fn fieldPrefix(self: *Tuple) Error!void {
510 try self.container.fieldPrefix(null);
511 }
512};
513
514/// Writes ZON structs field by field.
515pub const Struct = struct {
516 container: Container,
517
518 fn begin(parent: *Serializer, options: ContainerOptions) Error!Struct {
519 return .{
520 .container = try Container.begin(parent, .named, options),
521 };
522 }
523
524 /// Finishes serializing the struct.
525 ///
526 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
527 pub fn end(self: *Struct) Error!void {
528 try self.container.end();
529 self.* = undefined;
530 }
531
532 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
533 pub fn field(
534 self: *Struct,
535 name: []const u8,
536 val: anytype,
537 options: ValueOptions,
538 ) Error!void {
539 try self.container.field(name, val, options);
540 }
541
542 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
543 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
544 pub fn fieldMaxDepth(
545 self: *Struct,
546 name: []const u8,
547 val: anytype,
548 options: ValueOptions,
549 depth: usize,
550 ) DepthError!void {
551 try self.container.fieldMaxDepth(name, val, options, depth);
552 }
553
554 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
555 /// `valueArbitraryDepth`.
556 pub fn fieldArbitraryDepth(
557 self: *Struct,
558 name: []const u8,
559 val: anytype,
560 options: ValueOptions,
561 ) Error!void {
562 try self.container.fieldArbitraryDepth(name, val, options);
563 }
564
565 /// Starts a field with a struct as a value. Returns the struct.
566 pub fn beginStructField(
567 self: *Struct,
568 name: []const u8,
569 options: ContainerOptions,
570 ) Error!Struct {
571 try self.fieldPrefix(name);
572 return self.container.serializer.beginStruct(options);
573 }
574
575 /// Starts a field with a tuple as a value. Returns the tuple.
576 pub fn beginTupleField(
577 self: *Struct,
578 name: []const u8,
579 options: ContainerOptions,
580 ) Error!Tuple {
581 try self.fieldPrefix(name);
582 return self.container.serializer.beginTuple(options);
583 }
584
585 /// Print a field prefix. This prints any necessary commas, the field name (escaped if
586 /// necessary) and whitespace as configured. Useful if you want to serialize the field
587 /// value yourself.
588 pub fn fieldPrefix(self: *Struct, name: []const u8) Error!void {
589 try self.container.fieldPrefix(name);
590 }
591};
592
593const Container = struct {
594 const FieldStyle = enum { named, anon };
595
596 serializer: *Serializer,
597 field_style: FieldStyle,
598 options: ContainerOptions,
599 empty: bool,
600
601 fn begin(
602 sz: *Serializer,
603 field_style: FieldStyle,
604 options: ContainerOptions,
605 ) Error!Container {
606 if (options.shouldWrap()) sz.indent_level +|= 1;
607 try sz.writer.writeAll(".{");
608 return .{
609 .serializer = sz,
610 .field_style = field_style,
611 .options = options,
612 .empty = true,
613 };
614 }
615
616 fn end(self: *Container) Error!void {
617 if (self.options.shouldWrap()) self.serializer.indent_level -|= 1;
618 if (!self.empty) {
619 if (self.options.shouldWrap()) {
620 if (self.serializer.options.whitespace) {
621 try self.serializer.writer.writeByte(',');
622 }
623 try self.serializer.newline();
624 try self.serializer.indent();
625 } else if (!self.shouldElideSpaces()) {
626 try self.serializer.space();
627 }
628 }
629 try self.serializer.writer.writeByte('}');
630 self.* = undefined;
631 }
632
633 fn fieldPrefix(self: *Container, name: ?[]const u8) Error!void {
634 if (!self.empty) {
635 try self.serializer.writer.writeByte(',');
636 }
637 self.empty = false;
638 if (self.options.shouldWrap()) {
639 try self.serializer.newline();
640 } else if (!self.shouldElideSpaces()) {
641 try self.serializer.space();
642 }
643 if (self.options.shouldWrap()) try self.serializer.indent();
644 if (name) |n| {
645 try self.serializer.ident(n);
646 try self.serializer.space();
647 try self.serializer.writer.writeByte('=');
648 try self.serializer.space();
649 }
650 }
651
652 fn field(
653 self: *Container,
654 name: ?[]const u8,
655 val: anytype,
656 options: ValueOptions,
657 ) Error!void {
658 comptime assert(!typeIsRecursive(@TypeOf(val)));
659 try self.fieldArbitraryDepth(name, val, options);
660 }
661
662 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
663 fn fieldMaxDepth(
664 self: *Container,
665 name: ?[]const u8,
666 val: anytype,
667 options: ValueOptions,
668 depth: usize,
669 ) DepthError!void {
670 try checkValueDepth(val, depth);
671 try self.fieldArbitraryDepth(name, val, options);
672 }
673
674 fn fieldArbitraryDepth(
675 self: *Container,
676 name: ?[]const u8,
677 val: anytype,
678 options: ValueOptions,
679 ) Error!void {
680 try self.fieldPrefix(name);
681 try self.serializer.valueArbitraryDepth(val, options);
682 }
683
684 fn shouldElideSpaces(self: *const Container) bool {
685 return switch (self.options.whitespace_style) {
686 .fields => |fields| self.field_style != .named and fields == 1,
687 else => false,
688 };
689 }
690};
691
692test Serializer {
693 var discarding: Writer.Discarding = .init(&.{});
694 var s: Serializer = .{ .writer = &discarding.writer };
695 var vec2 = try s.beginStruct(.{});
696 try vec2.field("x", 1.5, .{});
697 try vec2.fieldPrefix("prefix");
698 try s.value(2.5, .{});
699 try vec2.end();
700}
701
702inline fn typeIsRecursive(comptime T: type) bool {
703 return comptime typeIsRecursiveInner(T, &.{});
704}
705
706fn typeIsRecursiveInner(comptime T: type, comptime prev_visited: []const type) bool {
707 for (prev_visited) |V| {
708 if (V == T) return true;
709 }
710 const visited = prev_visited ++ .{T};
711
712 return switch (@typeInfo(T)) {
713 .pointer => |pointer| typeIsRecursiveInner(pointer.child, visited),
714 .optional => |optional| typeIsRecursiveInner(optional.child, visited),
715 .array => |array| typeIsRecursiveInner(array.child, visited),
716 .vector => |vector| typeIsRecursiveInner(vector.child, visited),
717 .@"struct" => |@"struct"| for (@"struct".fields) |field| {
718 if (typeIsRecursiveInner(field.type, visited)) break true;
719 } else false,
720 .@"union" => |@"union"| inline for (@"union".fields) |field| {
721 if (typeIsRecursiveInner(field.type, visited)) break true;
722 } else false,
723 else => false,
724 };
725}
726
727test typeIsRecursive {
728 try std.testing.expect(!typeIsRecursive(bool));
729 try std.testing.expect(!typeIsRecursive(struct { x: i32, y: i32 }));
730 try std.testing.expect(!typeIsRecursive(struct { i32, i32 }));
731 try std.testing.expect(typeIsRecursive(struct { x: i32, y: i32, z: *@This() }));
732 try std.testing.expect(typeIsRecursive(struct {
733 a: struct {
734 const A = @This();
735 b: struct {
736 c: *struct {
737 a: ?A,
738 },
739 },
740 },
741 }));
742 try std.testing.expect(typeIsRecursive(struct {
743 a: [3]*@This(),
744 }));
745 try std.testing.expect(typeIsRecursive(struct {
746 a: union { a: i32, b: *@This() },
747 }));
748}
749
750fn checkValueDepth(val: anytype, depth: usize) error{ExceededMaxDepth}!void {
751 if (depth == 0) return error.ExceededMaxDepth;
752 const child_depth = depth - 1;
753
754 switch (@typeInfo(@TypeOf(val))) {
755 .pointer => |pointer| switch (pointer.size) {
756 .one => try checkValueDepth(val.*, child_depth),
757 .slice => for (val) |item| {
758 try checkValueDepth(item, child_depth);
759 },
760 .c, .many => {},
761 },
762 .array => for (val) |item| {
763 try checkValueDepth(item, child_depth);
764 },
765 .@"struct" => |@"struct"| inline for (@"struct".fields) |field_info| {
766 try checkValueDepth(@field(val, field_info.name), child_depth);
767 },
768 .@"union" => |@"union"| if (@"union".tag_type == null) {
769 return;
770 } else switch (val) {
771 inline else => |payload| {
772 return checkValueDepth(payload, child_depth);
773 },
774 },
775 .optional => if (val) |inner| try checkValueDepth(inner, child_depth),
776 else => {},
777 }
778}
779
780fn expectValueDepthEquals(expected: usize, v: anytype) !void {
781 try checkValueDepth(v, expected);
782 try std.testing.expectError(error.ExceededMaxDepth, checkValueDepth(v, expected - 1));
783}
784
785test checkValueDepth {
786 try expectValueDepthEquals(1, 10);
787 try expectValueDepthEquals(2, .{ .x = 1, .y = 2 });
788 try expectValueDepthEquals(2, .{ 1, 2 });
789 try expectValueDepthEquals(3, .{ 1, .{ 2, 3 } });
790 try expectValueDepthEquals(3, .{ .{ 1, 2 }, 3 });
791 try expectValueDepthEquals(3, .{ .x = 0, .y = 1, .z = .{ .x = 3 } });
792 try expectValueDepthEquals(3, .{ .x = 0, .y = .{ .x = 1 }, .z = 2 });
793 try expectValueDepthEquals(3, .{ .x = .{ .x = 0 }, .y = 1, .z = 2 });
794 try expectValueDepthEquals(2, @as(?u32, 1));
795 try expectValueDepthEquals(1, @as(?u32, null));
796 try expectValueDepthEquals(1, null);
797 try expectValueDepthEquals(2, &1);
798 try expectValueDepthEquals(3, &@as(?u32, 1));
799
800 const Union = union(enum) {
801 x: u32,
802 y: struct { x: u32 },
803 };
804 try expectValueDepthEquals(2, Union{ .x = 1 });
805 try expectValueDepthEquals(3, Union{ .y = .{ .x = 1 } });
806
807 const Recurse = struct { r: ?*const @This() };
808 try expectValueDepthEquals(2, Recurse{ .r = null });
809 try expectValueDepthEquals(5, Recurse{ .r = &Recurse{ .r = null } });
810 try expectValueDepthEquals(8, Recurse{ .r = &Recurse{ .r = &Recurse{ .r = null } } });
811
812 try expectValueDepthEquals(2, @as([]const u8, &.{ 1, 2, 3 }));
813 try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{ 1, 2, 3 }}));
814}
815
816inline fn canSerializeType(T: type) bool {
817 comptime return canSerializeTypeInner(T, &.{}, false);
818}
819
820fn canSerializeTypeInner(
821 T: type,
822 /// Visited structs and unions, to avoid infinite recursion.
823 /// Tracking more types is unnecessary, and a little complex due to optional nesting.
824 visited: []const type,
825 parent_is_optional: bool,
826) bool {
827 return switch (@typeInfo(T)) {
828 .bool,
829 .int,
830 .float,
831 .comptime_float,
832 .comptime_int,
833 .null,
834 .enum_literal,
835 => true,
836
837 .noreturn,
838 .void,
839 .type,
840 .undefined,
841 .error_union,
842 .error_set,
843 .@"fn",
844 .frame,
845 .@"anyframe",
846 .@"opaque",
847 => false,
848
849 .@"enum" => |@"enum"| @"enum".is_exhaustive,
850
851 .pointer => |pointer| switch (pointer.size) {
852 .one => canSerializeTypeInner(pointer.child, visited, parent_is_optional),
853 .slice => canSerializeTypeInner(pointer.child, visited, false),
854 .many, .c => false,
855 },
856
857 .optional => |optional| if (parent_is_optional)
858 false
859 else
860 canSerializeTypeInner(optional.child, visited, true),
861
862 .array => |array| canSerializeTypeInner(array.child, visited, false),
863 .vector => |vector| canSerializeTypeInner(vector.child, visited, false),
864
865 .@"struct" => |@"struct"| {
866 for (visited) |V| if (T == V) return true;
867 const new_visited = visited ++ .{T};
868 for (@"struct".fields) |field| {
869 if (!canSerializeTypeInner(field.type, new_visited, false)) return false;
870 }
871 return true;
872 },
873 .@"union" => |@"union"| {
874 for (visited) |V| if (T == V) return true;
875 const new_visited = visited ++ .{T};
876 if (@"union".tag_type == null) return false;
877 for (@"union".fields) |field| {
878 if (field.type != void and !canSerializeTypeInner(field.type, new_visited, false)) {
879 return false;
880 }
881 }
882 return true;
883 },
884 };
885}
886
887test canSerializeType {
888 try std.testing.expect(!comptime canSerializeType(void));
889 try std.testing.expect(!comptime canSerializeType(struct { f: [*]u8 }));
890 try std.testing.expect(!comptime canSerializeType(struct { error{foo} }));
891 try std.testing.expect(!comptime canSerializeType(union(enum) { a: void, f: [*c]u8 }));
892 try std.testing.expect(!comptime canSerializeType(@Vector(0, [*c]u8)));
893 try std.testing.expect(!comptime canSerializeType(*?[*c]u8));
894 try std.testing.expect(!comptime canSerializeType(enum(u8) { _ }));
895 try std.testing.expect(!comptime canSerializeType(union { foo: void }));
896 try std.testing.expect(comptime canSerializeType(union(enum) { foo: void }));
897 try std.testing.expect(comptime canSerializeType(comptime_float));
898 try std.testing.expect(comptime canSerializeType(comptime_int));
899 try std.testing.expect(!comptime canSerializeType(struct { comptime foo: ??u8 = null }));
900 try std.testing.expect(comptime canSerializeType(@TypeOf(.foo)));
901 try std.testing.expect(comptime canSerializeType(?u8));
902 try std.testing.expect(comptime canSerializeType(*?*u8));
903 try std.testing.expect(comptime canSerializeType(?struct {
904 foo: ?struct {
905 ?union(enum) {
906 a: ?@Vector(0, ?*u8),
907 },
908 ?struct {
909 f: ?[]?u8,
910 },
911 },
912 }));
913 try std.testing.expect(!comptime canSerializeType(??u8));
914 try std.testing.expect(!comptime canSerializeType(?*?u8));
915 try std.testing.expect(!comptime canSerializeType(*?*?*u8));
916 try std.testing.expect(comptime canSerializeType(struct { x: comptime_int = 2 }));
917 try std.testing.expect(comptime canSerializeType(struct { x: comptime_float = 2 }));
918 try std.testing.expect(comptime canSerializeType(struct { comptime_int }));
919 try std.testing.expect(comptime canSerializeType(struct { comptime x: @TypeOf(.foo) = .foo }));
920 const Recursive = struct { foo: ?*@This() };
921 try std.testing.expect(comptime canSerializeType(Recursive));
922
923 // Make sure we validate nested optional before we early out due to already having seen
924 // a type recursion!
925 try std.testing.expect(!comptime canSerializeType(struct {
926 add_to_visited: ?u8,
927 retrieve_from_visited: ??u8,
928 }));
929}
lib/std/zon/stringify.zig+2-934
...@@ -23,13 +23,13 @@...@@ -23,13 +23,13 @@
23const std = @import("std");23const std = @import("std");
24const assert = std.debug.assert;24const assert = std.debug.assert;
25const Writer = std.Io.Writer;25const Writer = std.Io.Writer;
26const Serializer = std.zon.Serializer;
2627
27/// Options for `serialize`.
28pub const SerializeOptions = struct {28pub const SerializeOptions = struct {
29 /// If false, whitespace is omitted. Otherwise whitespace is emitted in standard Zig style.29 /// If false, whitespace is omitted. Otherwise whitespace is emitted in standard Zig style.
30 whitespace: bool = true,30 whitespace: bool = true,
31 /// Determines when to emit Unicode code point literals as opposed to integer literals.31 /// Determines when to emit Unicode code point literals as opposed to integer literals.
32 emit_codepoint_literals: EmitCodepointLiterals = .never,32 emit_codepoint_literals: Serializer.EmitCodepointLiterals = .never,
33 /// If true, slices of `u8`s, and pointers to arrays of `u8` are serialized as containers.33 /// If true, slices of `u8`s, and pointers to arrays of `u8` are serialized as containers.
34 /// Otherwise they are serialized as string literals.34 /// Otherwise they are serialized as string literals.
35 emit_strings_as_containers: bool = false,35 emit_strings_as_containers: bool = false,
...@@ -93,102 +93,6 @@ pub fn serializeArbitraryDepth(...@@ -93,102 +93,6 @@ pub fn serializeArbitraryDepth(
93 });93 });
94}94}
9595
96inline fn typeIsRecursive(comptime T: type) bool {
97 return comptime typeIsRecursiveInner(T, &.{});
98}
99
100fn typeIsRecursiveInner(comptime T: type, comptime prev_visited: []const type) bool {
101 for (prev_visited) |V| {
102 if (V == T) return true;
103 }
104 const visited = prev_visited ++ .{T};
105
106 return switch (@typeInfo(T)) {
107 .pointer => |pointer| typeIsRecursiveInner(pointer.child, visited),
108 .optional => |optional| typeIsRecursiveInner(optional.child, visited),
109 .array => |array| typeIsRecursiveInner(array.child, visited),
110 .vector => |vector| typeIsRecursiveInner(vector.child, visited),
111 .@"struct" => |@"struct"| for (@"struct".fields) |field| {
112 if (typeIsRecursiveInner(field.type, visited)) break true;
113 } else false,
114 .@"union" => |@"union"| inline for (@"union".fields) |field| {
115 if (typeIsRecursiveInner(field.type, visited)) break true;
116 } else false,
117 else => false,
118 };
119}
120
121inline fn canSerializeType(T: type) bool {
122 comptime return canSerializeTypeInner(T, &.{}, false);
123}
124
125fn canSerializeTypeInner(
126 T: type,
127 /// Visited structs and unions, to avoid infinite recursion.
128 /// Tracking more types is unnecessary, and a little complex due to optional nesting.
129 visited: []const type,
130 parent_is_optional: bool,
131) bool {
132 return switch (@typeInfo(T)) {
133 .bool,
134 .int,
135 .float,
136 .comptime_float,
137 .comptime_int,
138 .null,
139 .enum_literal,
140 => true,
141
142 .noreturn,
143 .void,
144 .type,
145 .undefined,
146 .error_union,
147 .error_set,
148 .@"fn",
149 .frame,
150 .@"anyframe",
151 .@"opaque",
152 => false,
153
154 .@"enum" => |@"enum"| @"enum".is_exhaustive,
155
156 .pointer => |pointer| switch (pointer.size) {
157 .one => canSerializeTypeInner(pointer.child, visited, parent_is_optional),
158 .slice => canSerializeTypeInner(pointer.child, visited, false),
159 .many, .c => false,
160 },
161
162 .optional => |optional| if (parent_is_optional)
163 false
164 else
165 canSerializeTypeInner(optional.child, visited, true),
166
167 .array => |array| canSerializeTypeInner(array.child, visited, false),
168 .vector => |vector| canSerializeTypeInner(vector.child, visited, false),
169
170 .@"struct" => |@"struct"| {
171 for (visited) |V| if (T == V) return true;
172 const new_visited = visited ++ .{T};
173 for (@"struct".fields) |field| {
174 if (!canSerializeTypeInner(field.type, new_visited, false)) return false;
175 }
176 return true;
177 },
178 .@"union" => |@"union"| {
179 for (visited) |V| if (T == V) return true;
180 const new_visited = visited ++ .{T};
181 if (@"union".tag_type == null) return false;
182 for (@"union".fields) |field| {
183 if (field.type != void and !canSerializeTypeInner(field.type, new_visited, false)) {
184 return false;
185 }
186 }
187 return true;
188 },
189 };
190}
191
192fn isNestedOptional(T: type) bool {96fn isNestedOptional(T: type) bool {
193 comptime switch (@typeInfo(T)) {97 comptime switch (@typeInfo(T)) {
194 .optional => |optional| return isNestedOptionalInner(optional.child),98 .optional => |optional| return isNestedOptionalInner(optional.child),
...@@ -210,842 +114,6 @@ fn isNestedOptionalInner(T: type) bool {...@@ -210,842 +114,6 @@ fn isNestedOptionalInner(T: type) bool {
210 }114 }
211}115}
212116
213test "std.zon stringify canSerializeType" {
214 try std.testing.expect(!comptime canSerializeType(void));
215 try std.testing.expect(!comptime canSerializeType(struct { f: [*]u8 }));
216 try std.testing.expect(!comptime canSerializeType(struct { error{foo} }));
217 try std.testing.expect(!comptime canSerializeType(union(enum) { a: void, f: [*c]u8 }));
218 try std.testing.expect(!comptime canSerializeType(@Vector(0, [*c]u8)));
219 try std.testing.expect(!comptime canSerializeType(*?[*c]u8));
220 try std.testing.expect(!comptime canSerializeType(enum(u8) { _ }));
221 try std.testing.expect(!comptime canSerializeType(union { foo: void }));
222 try std.testing.expect(comptime canSerializeType(union(enum) { foo: void }));
223 try std.testing.expect(comptime canSerializeType(comptime_float));
224 try std.testing.expect(comptime canSerializeType(comptime_int));
225 try std.testing.expect(!comptime canSerializeType(struct { comptime foo: ??u8 = null }));
226 try std.testing.expect(comptime canSerializeType(@TypeOf(.foo)));
227 try std.testing.expect(comptime canSerializeType(?u8));
228 try std.testing.expect(comptime canSerializeType(*?*u8));
229 try std.testing.expect(comptime canSerializeType(?struct {
230 foo: ?struct {
231 ?union(enum) {
232 a: ?@Vector(0, ?*u8),
233 },
234 ?struct {
235 f: ?[]?u8,
236 },
237 },
238 }));
239 try std.testing.expect(!comptime canSerializeType(??u8));
240 try std.testing.expect(!comptime canSerializeType(?*?u8));
241 try std.testing.expect(!comptime canSerializeType(*?*?*u8));
242 try std.testing.expect(comptime canSerializeType(struct { x: comptime_int = 2 }));
243 try std.testing.expect(comptime canSerializeType(struct { x: comptime_float = 2 }));
244 try std.testing.expect(comptime canSerializeType(struct { comptime_int }));
245 try std.testing.expect(comptime canSerializeType(struct { comptime x: @TypeOf(.foo) = .foo }));
246 const Recursive = struct { foo: ?*@This() };
247 try std.testing.expect(comptime canSerializeType(Recursive));
248
249 // Make sure we validate nested optional before we early out due to already having seen
250 // a type recursion!
251 try std.testing.expect(!comptime canSerializeType(struct {
252 add_to_visited: ?u8,
253 retrieve_from_visited: ??u8,
254 }));
255}
256
257test "std.zon typeIsRecursive" {
258 try std.testing.expect(!typeIsRecursive(bool));
259 try std.testing.expect(!typeIsRecursive(struct { x: i32, y: i32 }));
260 try std.testing.expect(!typeIsRecursive(struct { i32, i32 }));
261 try std.testing.expect(typeIsRecursive(struct { x: i32, y: i32, z: *@This() }));
262 try std.testing.expect(typeIsRecursive(struct {
263 a: struct {
264 const A = @This();
265 b: struct {
266 c: *struct {
267 a: ?A,
268 },
269 },
270 },
271 }));
272 try std.testing.expect(typeIsRecursive(struct {
273 a: [3]*@This(),
274 }));
275 try std.testing.expect(typeIsRecursive(struct {
276 a: union { a: i32, b: *@This() },
277 }));
278}
279
280fn checkValueDepth(val: anytype, depth: usize) error{ExceededMaxDepth}!void {
281 if (depth == 0) return error.ExceededMaxDepth;
282 const child_depth = depth - 1;
283
284 switch (@typeInfo(@TypeOf(val))) {
285 .pointer => |pointer| switch (pointer.size) {
286 .one => try checkValueDepth(val.*, child_depth),
287 .slice => for (val) |item| {
288 try checkValueDepth(item, child_depth);
289 },
290 .c, .many => {},
291 },
292 .array => for (val) |item| {
293 try checkValueDepth(item, child_depth);
294 },
295 .@"struct" => |@"struct"| inline for (@"struct".fields) |field_info| {
296 try checkValueDepth(@field(val, field_info.name), child_depth);
297 },
298 .@"union" => |@"union"| if (@"union".tag_type == null) {
299 return;
300 } else switch (val) {
301 inline else => |payload| {
302 return checkValueDepth(payload, child_depth);
303 },
304 },
305 .optional => if (val) |inner| try checkValueDepth(inner, child_depth),
306 else => {},
307 }
308}
309
310fn expectValueDepthEquals(expected: usize, value: anytype) !void {
311 try checkValueDepth(value, expected);
312 try std.testing.expectError(error.ExceededMaxDepth, checkValueDepth(value, expected - 1));
313}
314
315test "std.zon checkValueDepth" {
316 try expectValueDepthEquals(1, 10);
317 try expectValueDepthEquals(2, .{ .x = 1, .y = 2 });
318 try expectValueDepthEquals(2, .{ 1, 2 });
319 try expectValueDepthEquals(3, .{ 1, .{ 2, 3 } });
320 try expectValueDepthEquals(3, .{ .{ 1, 2 }, 3 });
321 try expectValueDepthEquals(3, .{ .x = 0, .y = 1, .z = .{ .x = 3 } });
322 try expectValueDepthEquals(3, .{ .x = 0, .y = .{ .x = 1 }, .z = 2 });
323 try expectValueDepthEquals(3, .{ .x = .{ .x = 0 }, .y = 1, .z = 2 });
324 try expectValueDepthEquals(2, @as(?u32, 1));
325 try expectValueDepthEquals(1, @as(?u32, null));
326 try expectValueDepthEquals(1, null);
327 try expectValueDepthEquals(2, &1);
328 try expectValueDepthEquals(3, &@as(?u32, 1));
329
330 const Union = union(enum) {
331 x: u32,
332 y: struct { x: u32 },
333 };
334 try expectValueDepthEquals(2, Union{ .x = 1 });
335 try expectValueDepthEquals(3, Union{ .y = .{ .x = 1 } });
336
337 const Recurse = struct { r: ?*const @This() };
338 try expectValueDepthEquals(2, Recurse{ .r = null });
339 try expectValueDepthEquals(5, Recurse{ .r = &Recurse{ .r = null } });
340 try expectValueDepthEquals(8, Recurse{ .r = &Recurse{ .r = &Recurse{ .r = null } } });
341
342 try expectValueDepthEquals(2, @as([]const u8, &.{ 1, 2, 3 }));
343 try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{ 1, 2, 3 }}));
344}
345
346/// Determines when to emit Unicode code point literals as opposed to integer literals.
347pub const EmitCodepointLiterals = enum {
348 /// Never emit Unicode code point literals.
349 never,
350 /// Emit Unicode code point literals for any `u8` in the printable ASCII range.
351 printable_ascii,
352 /// Emit Unicode code point literals for any unsigned integer with 21 bits or fewer
353 /// whose value is a valid non-surrogate code point.
354 always,
355
356 /// If the value should be emitted as a Unicode codepoint, return it as a u21.
357 fn emitAsCodepoint(self: @This(), val: anytype) ?u21 {
358 // Rule out incompatible integer types
359 switch (@typeInfo(@TypeOf(val))) {
360 .int => |int_info| if (int_info.signedness == .signed or int_info.bits > 21) {
361 return null;
362 },
363 .comptime_int => {},
364 else => comptime unreachable,
365 }
366
367 // Return null if the value shouldn't be printed as a Unicode codepoint, or the value casted
368 // to a u21 if it should.
369 switch (self) {
370 .always => {
371 const c = std.math.cast(u21, val) orelse return null;
372 if (!std.unicode.utf8ValidCodepoint(c)) return null;
373 return c;
374 },
375 .printable_ascii => {
376 const c = std.math.cast(u8, val) orelse return null;
377 if (!std.ascii.isPrint(c)) return null;
378 return c;
379 },
380 .never => {
381 return null;
382 },
383 }
384 }
385};
386
387/// Options for serialization of an individual value.
388///
389/// See `SerializeOptions` for more information on these options.
390pub const ValueOptions = struct {
391 emit_codepoint_literals: EmitCodepointLiterals = .never,
392 emit_strings_as_containers: bool = false,
393 emit_default_optional_fields: bool = true,
394};
395
396/// Options for manual serialization of container types.
397pub const SerializeContainerOptions = struct {
398 /// The whitespace style that should be used for this container. Ignored if whitespace is off.
399 whitespace_style: union(enum) {
400 /// If true, wrap every field. If false do not.
401 wrap: bool,
402 /// Automatically decide whether to wrap or not based on the number of fields. Following
403 /// the standard rule of thumb, containers with more than two fields are wrapped.
404 fields: usize,
405 } = .{ .wrap = true },
406
407 fn shouldWrap(self: SerializeContainerOptions) bool {
408 return switch (self.whitespace_style) {
409 .wrap => |wrap| wrap,
410 .fields => |fields| fields > 2,
411 };
412 }
413};
414
415/// Lower level control over serialization, you can create a new instance with `serializer`.
416///
417/// Useful when you want control over which fields are serialized, how they're represented,
418/// or want to write a ZON object that does not exist in memory.
419///
420/// You can serialize values with `value`. To serialize recursive types, the following are provided:
421/// * `valueMaxDepth`
422/// * `valueArbitraryDepth`
423///
424/// You can also serialize values using specific notations:
425/// * `int`
426/// * `float`
427/// * `codePoint`
428/// * `tuple`
429/// * `tupleMaxDepth`
430/// * `tupleArbitraryDepth`
431/// * `string`
432/// * `multilineString`
433///
434/// For manual serialization of containers, see:
435/// * `beginStruct`
436/// * `beginTuple`
437pub const Serializer = struct {
438 options: Options = .{},
439 indent_level: u8 = 0,
440 writer: *Writer,
441
442 pub const Error = Writer.Error;
443 pub const DepthError = Error || error{ExceededMaxDepth};
444
445 pub const Options = struct {
446 /// If false, only syntactically necessary whitespace is emitted.
447 whitespace: bool = true,
448 };
449
450 /// Serialize a value, similar to `serialize`.
451 pub fn value(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
452 comptime assert(!typeIsRecursive(@TypeOf(val)));
453 return self.valueArbitraryDepth(val, options);
454 }
455
456 /// Serialize a value, similar to `serializeMaxDepth`.
457 /// Can return `error.ExceededMaxDepth`.
458 pub fn valueMaxDepth(self: *Serializer, val: anytype, options: ValueOptions, depth: usize) DepthError!void {
459 try checkValueDepth(val, depth);
460 return self.valueArbitraryDepth(val, options);
461 }
462
463 /// Serialize a value, similar to `serializeArbitraryDepth`.
464 pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
465 comptime assert(canSerializeType(@TypeOf(val)));
466 switch (@typeInfo(@TypeOf(val))) {
467 .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {
468 self.codePoint(c) catch |err| switch (err) {
469 error.InvalidCodepoint => unreachable, // Already validated
470 else => |e| return e,
471 };
472 } else {
473 try self.int(val);
474 },
475 .float, .comptime_float => try self.float(val),
476 .bool, .null => try self.writer.print("{}", .{val}),
477 .enum_literal => try self.ident(@tagName(val)),
478 .@"enum" => try self.ident(@tagName(val)),
479 .pointer => |pointer| {
480 // Try to serialize as a string
481 const item: ?type = switch (@typeInfo(pointer.child)) {
482 .array => |array| array.child,
483 else => if (pointer.size == .slice) pointer.child else null,
484 };
485 if (item == u8 and
486 (pointer.sentinel() == null or pointer.sentinel() == 0) and
487 !options.emit_strings_as_containers)
488 {
489 return try self.string(val);
490 }
491
492 // Serialize as either a tuple or as the child type
493 switch (pointer.size) {
494 .slice => try self.tupleImpl(val, options),
495 .one => try self.valueArbitraryDepth(val.*, options),
496 else => comptime unreachable,
497 }
498 },
499 .array => {
500 var container = try self.beginTuple(
501 .{ .whitespace_style = .{ .fields = val.len } },
502 );
503 for (val) |item_val| {
504 try container.fieldArbitraryDepth(item_val, options);
505 }
506 try container.end();
507 },
508 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
509 var container = try self.beginTuple(
510 .{ .whitespace_style = .{ .fields = @"struct".fields.len } },
511 );
512 inline for (val) |field_value| {
513 try container.fieldArbitraryDepth(field_value, options);
514 }
515 try container.end();
516 } else {
517 // Decide which fields to emit
518 const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: {
519 break :b .{ @"struct".fields.len, @splat(false) };
520 } else b: {
521 var fields = @"struct".fields.len;
522 var skipped: [@"struct".fields.len]bool = @splat(false);
523 inline for (@"struct".fields, &skipped) |field_info, *skip| {
524 if (field_info.default_value_ptr) |ptr| {
525 const default: *const field_info.type = @ptrCast(@alignCast(ptr));
526 const field_value = @field(val, field_info.name);
527 if (std.meta.eql(field_value, default.*)) {
528 skip.* = true;
529 fields -= 1;
530 }
531 }
532 }
533 break :b .{ fields, skipped };
534 };
535
536 // Emit those fields
537 var container = try self.beginStruct(
538 .{ .whitespace_style = .{ .fields = fields } },
539 );
540 inline for (@"struct".fields, skipped) |field_info, skip| {
541 if (!skip) {
542 try container.fieldArbitraryDepth(
543 field_info.name,
544 @field(val, field_info.name),
545 options,
546 );
547 }
548 }
549 try container.end();
550 },
551 .@"union" => |@"union"| {
552 comptime assert(@"union".tag_type != null);
553 switch (val) {
554 inline else => |pl, tag| if (@TypeOf(pl) == void)
555 try self.writer.print(".{s}", .{@tagName(tag)})
556 else {
557 var container = try self.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
558
559 try container.fieldArbitraryDepth(
560 @tagName(tag),
561 pl,
562 options,
563 );
564
565 try container.end();
566 },
567 }
568 },
569 .optional => if (val) |inner| {
570 try self.valueArbitraryDepth(inner, options);
571 } else {
572 try self.writer.writeAll("null");
573 },
574 .vector => |vector| {
575 var container = try self.beginTuple(
576 .{ .whitespace_style = .{ .fields = vector.len } },
577 );
578 for (0..vector.len) |i| {
579 try container.fieldArbitraryDepth(val[i], options);
580 }
581 try container.end();
582 },
583
584 else => comptime unreachable,
585 }
586 }
587
588 /// Serialize an integer.
589 pub fn int(self: *Serializer, val: anytype) Error!void {
590 try self.writer.printInt(val, 10, .lower, .{});
591 }
592
593 /// Serialize a float.
594 pub fn float(self: *Serializer, val: anytype) Error!void {
595 switch (@typeInfo(@TypeOf(val))) {
596 .float => if (std.math.isNan(val)) {
597 return self.writer.writeAll("nan");
598 } else if (std.math.isPositiveInf(val)) {
599 return self.writer.writeAll("inf");
600 } else if (std.math.isNegativeInf(val)) {
601 return self.writer.writeAll("-inf");
602 } else if (std.math.isNegativeZero(val)) {
603 return self.writer.writeAll("-0.0");
604 } else {
605 try self.writer.print("{d}", .{val});
606 },
607 .comptime_float => if (val == 0) {
608 return self.writer.writeAll("0");
609 } else {
610 try self.writer.print("{d}", .{val});
611 },
612 else => comptime unreachable,
613 }
614 }
615
616 /// Serialize `name` as an identifier prefixed with `.`.
617 ///
618 /// Escapes the identifier if necessary.
619 pub fn ident(self: *Serializer, name: []const u8) Error!void {
620 try self.writer.print(".{f}", .{std.zig.fmtIdPU(name)});
621 }
622
623 pub const CodePointError = Error || error{InvalidCodepoint};
624
625 /// Serialize `val` as a Unicode codepoint.
626 ///
627 /// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint.
628 pub fn codePoint(self: *Serializer, val: u21) CodePointError!void {
629 try self.writer.print("'{f}'", .{std.zig.fmtChar(val)});
630 }
631
632 /// Like `value`, but always serializes `val` as a tuple.
633 ///
634 /// Will fail at comptime if `val` is not a tuple, array, pointer to an array, or slice.
635 pub fn tuple(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
636 comptime assert(!typeIsRecursive(@TypeOf(val)));
637 try self.tupleArbitraryDepth(val, options);
638 }
639
640 /// Like `tuple`, but recursive types are allowed.
641 ///
642 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
643 pub fn tupleMaxDepth(
644 self: *Serializer,
645 val: anytype,
646 options: ValueOptions,
647 depth: usize,
648 ) DepthError!void {
649 try checkValueDepth(val, depth);
650 try self.tupleArbitraryDepth(val, options);
651 }
652
653 /// Like `tuple`, but recursive types are allowed.
654 ///
655 /// It is the caller's responsibility to ensure that `val` does not contain cycles.
656 pub fn tupleArbitraryDepth(
657 self: *Serializer,
658 val: anytype,
659 options: ValueOptions,
660 ) Error!void {
661 try self.tupleImpl(val, options);
662 }
663
664 fn tupleImpl(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
665 comptime assert(canSerializeType(@TypeOf(val)));
666 switch (@typeInfo(@TypeOf(val))) {
667 .@"struct" => {
668 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
669 inline for (val) |item_val| {
670 try container.fieldArbitraryDepth(item_val, options);
671 }
672 try container.end();
673 },
674 .pointer, .array => {
675 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
676 for (val) |item_val| {
677 try container.fieldArbitraryDepth(item_val, options);
678 }
679 try container.end();
680 },
681 else => comptime unreachable,
682 }
683 }
684
685 /// Like `value`, but always serializes `val` as a string.
686 pub fn string(self: *Serializer, val: []const u8) Error!void {
687 try self.writer.print("\"{f}\"", .{std.zig.fmtString(val)});
688 }
689
690 /// Options for formatting multiline strings.
691 pub const MultilineStringOptions = struct {
692 /// If top level is true, whitespace before and after the multiline string is elided.
693 /// If it is true, a newline is printed, then the value, followed by a newline, and if
694 /// whitespace is true any necessary indentation follows.
695 top_level: bool = false,
696 };
697
698 pub const MultilineStringError = Error || error{InnerCarriageReturn};
699
700 /// Like `value`, but always serializes to a multiline string literal.
701 ///
702 /// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline,
703 /// since multiline strings cannot represent CR without a following newline.
704 pub fn multilineString(
705 self: *Serializer,
706 val: []const u8,
707 options: MultilineStringOptions,
708 ) MultilineStringError!void {
709 // Make sure the string does not contain any carriage returns not followed by a newline
710 var i: usize = 0;
711 while (i < val.len) : (i += 1) {
712 if (val[i] == '\r') {
713 if (i + 1 < val.len) {
714 if (val[i + 1] == '\n') {
715 i += 1;
716 continue;
717 }
718 }
719 return error.InnerCarriageReturn;
720 }
721 }
722
723 if (!options.top_level) {
724 try self.newline();
725 try self.indent();
726 }
727
728 try self.writer.writeAll("\\\\");
729 for (val) |c| {
730 if (c != '\r') {
731 try self.writer.writeByte(c); // We write newlines here even if whitespace off
732 if (c == '\n') {
733 try self.indent();
734 try self.writer.writeAll("\\\\");
735 }
736 }
737 }
738
739 if (!options.top_level) {
740 try self.writer.writeByte('\n'); // Even if whitespace off
741 try self.indent();
742 }
743 }
744
745 /// Create a `Struct` for writing ZON structs field by field.
746 pub fn beginStruct(
747 self: *Serializer,
748 options: SerializeContainerOptions,
749 ) Error!Struct {
750 return Struct.begin(self, options);
751 }
752
753 /// Creates a `Tuple` for writing ZON tuples field by field.
754 pub fn beginTuple(
755 self: *Serializer,
756 options: SerializeContainerOptions,
757 ) Error!Tuple {
758 return Tuple.begin(self, options);
759 }
760
761 fn indent(self: *Serializer) Error!void {
762 if (self.options.whitespace) {
763 try self.writer.splatByteAll(' ', 4 * self.indent_level);
764 }
765 }
766
767 fn newline(self: *Serializer) Error!void {
768 if (self.options.whitespace) {
769 try self.writer.writeByte('\n');
770 }
771 }
772
773 fn newlineOrSpace(self: *Serializer, len: usize) Error!void {
774 if (self.containerShouldWrap(len)) {
775 try self.newline();
776 } else {
777 try self.space();
778 }
779 }
780
781 fn space(self: *Serializer) Error!void {
782 if (self.options.whitespace) {
783 try self.writer.writeByte(' ');
784 }
785 }
786
787 /// Writes ZON tuples field by field.
788 pub const Tuple = struct {
789 container: Container,
790
791 fn begin(parent: *Serializer, options: SerializeContainerOptions) Error!Tuple {
792 return .{
793 .container = try Container.begin(parent, .anon, options),
794 };
795 }
796
797 /// Finishes serializing the tuple.
798 ///
799 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
800 pub fn end(self: *Tuple) Error!void {
801 try self.container.end();
802 self.* = undefined;
803 }
804
805 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
806 pub fn field(
807 self: *Tuple,
808 val: anytype,
809 options: ValueOptions,
810 ) Error!void {
811 try self.container.field(null, val, options);
812 }
813
814 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
815 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
816 pub fn fieldMaxDepth(
817 self: *Tuple,
818 val: anytype,
819 options: ValueOptions,
820 depth: usize,
821 ) DepthError!void {
822 try self.container.fieldMaxDepth(null, val, options, depth);
823 }
824
825 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
826 /// `valueArbitraryDepth`.
827 pub fn fieldArbitraryDepth(
828 self: *Tuple,
829 val: anytype,
830 options: ValueOptions,
831 ) Error!void {
832 try self.container.fieldArbitraryDepth(null, val, options);
833 }
834
835 /// Starts a field with a struct as a value. Returns the struct.
836 pub fn beginStructField(
837 self: *Tuple,
838 options: SerializeContainerOptions,
839 ) Error!Struct {
840 try self.fieldPrefix();
841 return self.container.serializer.beginStruct(options);
842 }
843
844 /// Starts a field with a tuple as a value. Returns the tuple.
845 pub fn beginTupleField(
846 self: *Tuple,
847 options: SerializeContainerOptions,
848 ) Error!Tuple {
849 try self.fieldPrefix();
850 return self.container.serializer.beginTuple(options);
851 }
852
853 /// Print a field prefix. This prints any necessary commas, and whitespace as
854 /// configured. Useful if you want to serialize the field value yourself.
855 pub fn fieldPrefix(self: *Tuple) Error!void {
856 try self.container.fieldPrefix(null);
857 }
858 };
859
860 /// Writes ZON structs field by field.
861 pub const Struct = struct {
862 container: Container,
863
864 fn begin(parent: *Serializer, options: SerializeContainerOptions) Error!Struct {
865 return .{
866 .container = try Container.begin(parent, .named, options),
867 };
868 }
869
870 /// Finishes serializing the struct.
871 ///
872 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
873 pub fn end(self: *Struct) Error!void {
874 try self.container.end();
875 self.* = undefined;
876 }
877
878 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
879 pub fn field(
880 self: *Struct,
881 name: []const u8,
882 val: anytype,
883 options: ValueOptions,
884 ) Error!void {
885 try self.container.field(name, val, options);
886 }
887
888 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
889 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
890 pub fn fieldMaxDepth(
891 self: *Struct,
892 name: []const u8,
893 val: anytype,
894 options: ValueOptions,
895 depth: usize,
896 ) DepthError!void {
897 try self.container.fieldMaxDepth(name, val, options, depth);
898 }
899
900 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
901 /// `valueArbitraryDepth`.
902 pub fn fieldArbitraryDepth(
903 self: *Struct,
904 name: []const u8,
905 val: anytype,
906 options: ValueOptions,
907 ) Error!void {
908 try self.container.fieldArbitraryDepth(name, val, options);
909 }
910
911 /// Starts a field with a struct as a value. Returns the struct.
912 pub fn beginStructField(
913 self: *Struct,
914 name: []const u8,
915 options: SerializeContainerOptions,
916 ) Error!Struct {
917 try self.fieldPrefix(name);
918 return self.container.serializer.beginStruct(options);
919 }
920
921 /// Starts a field with a tuple as a value. Returns the tuple.
922 pub fn beginTupleField(
923 self: *Struct,
924 name: []const u8,
925 options: SerializeContainerOptions,
926 ) Error!Tuple {
927 try self.fieldPrefix(name);
928 return self.container.serializer.beginTuple(options);
929 }
930
931 /// Print a field prefix. This prints any necessary commas, the field name (escaped if
932 /// necessary) and whitespace as configured. Useful if you want to serialize the field
933 /// value yourself.
934 pub fn fieldPrefix(self: *Struct, name: []const u8) Error!void {
935 try self.container.fieldPrefix(name);
936 }
937 };
938
939 const Container = struct {
940 const FieldStyle = enum { named, anon };
941
942 serializer: *Serializer,
943 field_style: FieldStyle,
944 options: SerializeContainerOptions,
945 empty: bool,
946
947 fn begin(
948 sz: *Serializer,
949 field_style: FieldStyle,
950 options: SerializeContainerOptions,
951 ) Error!Container {
952 if (options.shouldWrap()) sz.indent_level +|= 1;
953 try sz.writer.writeAll(".{");
954 return .{
955 .serializer = sz,
956 .field_style = field_style,
957 .options = options,
958 .empty = true,
959 };
960 }
961
962 fn end(self: *Container) Error!void {
963 if (self.options.shouldWrap()) self.serializer.indent_level -|= 1;
964 if (!self.empty) {
965 if (self.options.shouldWrap()) {
966 if (self.serializer.options.whitespace) {
967 try self.serializer.writer.writeByte(',');
968 }
969 try self.serializer.newline();
970 try self.serializer.indent();
971 } else if (!self.shouldElideSpaces()) {
972 try self.serializer.space();
973 }
974 }
975 try self.serializer.writer.writeByte('}');
976 self.* = undefined;
977 }
978
979 fn fieldPrefix(self: *Container, name: ?[]const u8) Error!void {
980 if (!self.empty) {
981 try self.serializer.writer.writeByte(',');
982 }
983 self.empty = false;
984 if (self.options.shouldWrap()) {
985 try self.serializer.newline();
986 } else if (!self.shouldElideSpaces()) {
987 try self.serializer.space();
988 }
989 if (self.options.shouldWrap()) try self.serializer.indent();
990 if (name) |n| {
991 try self.serializer.ident(n);
992 try self.serializer.space();
993 try self.serializer.writer.writeByte('=');
994 try self.serializer.space();
995 }
996 }
997
998 fn field(
999 self: *Container,
1000 name: ?[]const u8,
1001 val: anytype,
1002 options: ValueOptions,
1003 ) Error!void {
1004 comptime assert(!typeIsRecursive(@TypeOf(val)));
1005 try self.fieldArbitraryDepth(name, val, options);
1006 }
1007
1008 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
1009 fn fieldMaxDepth(
1010 self: *Container,
1011 name: ?[]const u8,
1012 val: anytype,
1013 options: ValueOptions,
1014 depth: usize,
1015 ) DepthError!void {
1016 try checkValueDepth(val, depth);
1017 try self.fieldArbitraryDepth(name, val, options);
1018 }
1019
1020 fn fieldArbitraryDepth(
1021 self: *Container,
1022 name: ?[]const u8,
1023 val: anytype,
1024 options: ValueOptions,
1025 ) Error!void {
1026 try self.fieldPrefix(name);
1027 try self.serializer.valueArbitraryDepth(val, options);
1028 }
1029
1030 fn shouldElideSpaces(self: *const Container) bool {
1031 return switch (self.options.whitespace_style) {
1032 .fields => |fields| self.field_style != .named and fields == 1,
1033 else => false,
1034 };
1035 }
1036 };
1037};
1038
1039test Serializer {
1040 var discarding: Writer.Discarding = .init(&.{});
1041 var s: Serializer = .{ .writer = &discarding.writer };
1042 var vec2 = try s.beginStruct(.{});
1043 try vec2.field("x", 1.5, .{});
1044 try vec2.fieldPrefix("prefix");
1045 try s.value(2.5, .{});
1046 try vec2.end();
1047}
1048
1049fn expectSerializeEqual(117fn expectSerializeEqual(
1050 expected: []const u8,118 expected: []const u8,
1051 value: anytype,119 value: anytype,
src/main.zig+6-3
...@@ -344,8 +344,9 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -344,8 +344,9 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
344 } else if (mem.eql(u8, cmd, "targets")) {344 } else if (mem.eql(u8, cmd, "targets")) {
345 dev.check(.targets_command);345 dev.check(.targets_command);
346 const host = std.zig.resolveTargetQueryOrFatal(.{});346 const host = std.zig.resolveTargetQueryOrFatal(.{});
347 const stdout = fs.File.stdout().deprecatedWriter();347 var stdout_writer = fs.File.stdout().writer(&stdio_buffer);
348 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, &host);348 try @import("print_targets.zig").cmdTargets(arena, cmd_args, &stdout_writer.interface, &host);
349 return stdout_writer.interface.flush();
349 } else if (mem.eql(u8, cmd, "version")) {350 } else if (mem.eql(u8, cmd, "version")) {
350 dev.check(.version_command);351 dev.check(.version_command);
351 try fs.File.stdout().writeAll(build_options.version ++ "\n");352 try fs.File.stdout().writeAll(build_options.version ++ "\n");
...@@ -356,7 +357,9 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -356,7 +357,9 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
356 } else if (mem.eql(u8, cmd, "env")) {357 } else if (mem.eql(u8, cmd, "env")) {
357 dev.check(.env_command);358 dev.check(.env_command);
358 verifyLibcxxCorrectlyLinked();359 verifyLibcxxCorrectlyLinked();
359 return @import("print_env.zig").cmdEnv(arena, cmd_args);360 var stdout_writer = fs.File.stdout().writer(&stdio_buffer);
361 try @import("print_env.zig").cmdEnv(arena, &stdout_writer.interface);
362 return stdout_writer.interface.flush();
360 } else if (mem.eql(u8, cmd, "reduce")) {363 } else if (mem.eql(u8, cmd, "reduce")) {
361 return jitCmd(gpa, arena, cmd_args, .{364 return jitCmd(gpa, arena, cmd_args, .{
362 .cmd_name = "reduce",365 .cmd_name = "reduce",
src/print_env.zig+14-35
...@@ -4,8 +4,7 @@ const introspect = @import("introspect.zig");...@@ -4,8 +4,7 @@ const introspect = @import("introspect.zig");
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
5const fatal = std.process.fatal;5const fatal = std.process.fatal;
66
7pub fn cmdEnv(arena: Allocator, args: []const []const u8) !void {7pub fn cmdEnv(arena: Allocator, out: *std.Io.Writer) !void {
8 _ = args;
9 const cwd_path = try introspect.getResolvedCwd(arena);8 const cwd_path = try introspect.getResolvedCwd(arena);
10 const self_exe_path = try std.fs.selfExePathAlloc(arena);9 const self_exe_path = try std.fs.selfExePathAlloc(arena);
1110
...@@ -21,41 +20,21 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8) !void {...@@ -21,41 +20,21 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8) !void {
21 const host = try std.zig.system.resolveTargetQuery(.{});20 const host = try std.zig.system.resolveTargetQuery(.{});
22 const triple = try host.zigTriple(arena);21 const triple = try host.zigTriple(arena);
2322
24 var buffer: [1024]u8 = undefined;23 var serializer: std.zon.Serializer = .{ .writer = out };
25 var stdout_writer = std.fs.File.stdout().writer(&buffer);24 var root = try serializer.beginStruct(.{});
2625
27 var jws: std.json.Stringify = .{ .writer = &stdout_writer.interface, .options = .{ .whitespace = .indent_1 } };26 try root.field("zig_exe", self_exe_path, .{});
2827 try root.field("lib_dir", zig_lib_directory.path.?, .{});
29 try jws.beginObject();28 try root.field("std_dir", zig_std_dir, .{});
3029 try root.field("global_cache_dir", global_cache_dir, .{});
31 try jws.objectField("zig_exe");30 try root.field("version", build_options.version, .{});
32 try jws.write(self_exe_path);31 try root.field("target", triple, .{});
3332 var env = try root.beginStructField("env", .{});
34 try jws.objectField("lib_dir");
35 try jws.write(zig_lib_directory.path.?);
36
37 try jws.objectField("std_dir");
38 try jws.write(zig_std_dir);
39
40 try jws.objectField("global_cache_dir");
41 try jws.write(global_cache_dir);
42
43 try jws.objectField("version");
44 try jws.write(build_options.version);
45
46 try jws.objectField("target");
47 try jws.write(triple);
48
49 try jws.objectField("env");
50 try jws.beginObject();
51 inline for (@typeInfo(std.zig.EnvVar).@"enum".fields) |field| {33 inline for (@typeInfo(std.zig.EnvVar).@"enum".fields) |field| {
52 try jws.objectField(field.name);34 try env.field(field.name, try @field(std.zig.EnvVar, field.name).get(arena), .{});
53 try jws.write(try @field(std.zig.EnvVar, field.name).get(arena));
54 }35 }
55 try jws.endObject();36 try env.end();
5637 try root.end();
57 try jws.endObject();
5838
59 try stdout_writer.interface.writeByte('\n');39 try out.writeByte('\n');
60 try stdout_writer.interface.flush();
61}40}
src/print_targets.zig+4-8
...@@ -14,8 +14,7 @@ const introspect = @import("introspect.zig");...@@ -14,8 +14,7 @@ const introspect = @import("introspect.zig");
14pub fn cmdTargets(14pub fn cmdTargets(
15 allocator: Allocator,15 allocator: Allocator,
16 args: []const []const u8,16 args: []const []const u8,
17 /// Output stream17 out: *std.Io.Writer,
18 stdout: anytype,
19 native_target: *const Target,18 native_target: *const Target,
20) !void {19) !void {
21 _ = args;20 _ = args;
...@@ -38,12 +37,10 @@ pub fn cmdTargets(...@@ -38,12 +37,10 @@ pub fn cmdTargets(
38 const glibc_abi = try glibc.loadMetaData(allocator, abilists_contents);37 const glibc_abi = try glibc.loadMetaData(allocator, abilists_contents);
39 defer glibc_abi.destroy(allocator);38 defer glibc_abi.destroy(allocator);
4039
41 var bw = io.bufferedWriter(stdout);40 var serializer: std.zon.Serializer = .{ .writer = out };
42 const w = bw.writer();
43 var sz = std.zon.stringify.serializer(w, .{});
4441
45 {42 {
46 var root_obj = try sz.beginStruct(.{});43 var root_obj = try serializer.beginStruct(.{});
4744
48 try root_obj.field("arch", meta.fieldNames(Target.Cpu.Arch), .{});45 try root_obj.field("arch", meta.fieldNames(Target.Cpu.Arch), .{});
49 try root_obj.field("os", meta.fieldNames(Target.Os.Tag), .{});46 try root_obj.field("os", meta.fieldNames(Target.Os.Tag), .{});
...@@ -136,6 +133,5 @@ pub fn cmdTargets(...@@ -136,6 +133,5 @@ pub fn cmdTargets(
136 try root_obj.end();133 try root_obj.end();
137 }134 }
138135
139 try w.writeByte('\n');136 try out.writeByte('\n');
140 return bw.flush();
141}137}
src/translate_c.zig+1-1
...@@ -3338,7 +3338,7 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined...@@ -3338,7 +3338,7 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined
33383338
3339fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node {3339fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node {
3340 return Tag.char_literal.create(c.arena, if (narrow)3340 return Tag.char_literal.create(c.arena, if (narrow)
3341 try std.fmt.allocPrint(c.arena, "'{f}'", .{std.zig.fmtChar(&.{@as(u8, @intCast(val))})})3341 try std.fmt.allocPrint(c.arena, "'{f}'", .{std.zig.fmtChar(@intCast(val))})
3342 else3342 else
3343 try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val}));3343 try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val}));
3344}3344}