Serialize a value, similar to serializeArbitraryDepth.
pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOptions) Error!void
pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
comptime assertCanSerializeType(@TypeOf(val));
switch (@typeInfo(@TypeOf(val))) {
.int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {
self.codePoint(c) catch |err| switch (err) {
error.InvalidCodepoint => unreachable, // Already validated
else => |e| return e,
};
} else {
try self.int(val);
},
.float, .comptime_float => try self.float(val),
.bool, .null => try self.writer.print("{}", .{val}),
.enum_literal => try self.ident(@tagName(val)),
.@"enum" => try self.ident(@tagName(val)),
.pointer => |pointer| {
// Try to serialize as a string
const item: ?type = switch (@typeInfo(pointer.child)) {
.array => |array| array.child,
else => if (pointer.size == .slice) pointer.child else null,
};
if (item == u8 and
(pointer.sentinel() == null or pointer.sentinel() == 0) and
!options.emit_strings_as_containers)
{
return try self.string(val);
}
// Serialize as either a tuple or as the child type
switch (pointer.size) {
.slice => try self.tupleImpl(val, options),
.one => try self.valueArbitraryDepth(val.*, options),
else => comptime unreachable,
}
},
.array => {
try valueArbitraryDepthArray(self, @TypeOf(val), &val, options);
},
.vector => |vector| {
const array: [vector.len]vector.child = val;
try valueArbitraryDepthArray(self, @TypeOf(array), &array, options);
},
.@"struct" => |@"struct"| if (@"struct".is_tuple) {
var container = try self.beginTuple(
.{ .whitespace_style = .{ .fields = @"struct".field_names.len } },
);
inline for (val) |field_value| {
try container.fieldArbitraryDepth(field_value, options);
}
try container.end();
} else {
// Decide which fields to emit
const fields, const skipped: [@"struct".field_names.len]bool = if (options.emit_default_optional_fields) b: {
break :b .{ @"struct".field_names.len, @splat(false) };
} else b: {
var fields = @"struct".field_names.len;
var skipped: [@"struct".field_names.len]bool = @splat(false);
inline for (
@"struct".field_names,
@"struct".field_types,
@"struct".field_attrs,
&skipped,
) |field_name, field_type, field_attrs, *skip| {
if (field_attrs.default_value_ptr) |ptr| {
const default: *const field_type = @ptrCast(@alignCast(ptr));
const field_value = @field(val, field_name);
if (std.meta.eql(field_value, default.*)) {
skip.* = true;
fields -= 1;
}
}
}
break :b .{ fields, skipped };
};
// Emit those fields
var container = try self.beginStruct(
.{ .whitespace_style = .{ .fields = fields } },
);
inline for (@"struct".field_names, skipped) |field_name, skip| {
if (!skip) {
try container.fieldArbitraryDepth(
field_name,
@field(val, field_name),
options,
);
}
}
try container.end();
},
.@"union" => |@"union"| {
comptime assert(@"union".tag_type != null);
switch (val) {
inline else => |pl, tag| if (@TypeOf(pl) == void)
try self.writer.print(".{s}", .{@tagName(tag)})
else {
var container = try self.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
try container.fieldArbitraryDepth(
@tagName(tag),
pl,
options,
);
try container.end();
},
}
},
.optional => if (val) |inner| {
try self.valueArbitraryDepth(inner, options);
} else {
try self.writer.writeAll("null");
},
else => comptime unreachable,
}
}