1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
const std = @import("std");
const struct_util = @import("util/struct.zig");
const enum_util = @import("util/enum.zig");
pub const enumFieldsSlice = enum_util.enumFieldsSlice;
// Builds a static map of fields in `T` using `F`.
// to be used by our stringify
pub fn JsonMap(
comptime T: type,
comptime F: ?fn (comptime []const u8) []const u8,
) type {
return blk: {
switch (@typeInfo(T)) {
.Enum => {
break :blk struct {
const fields = enum_util.enumFieldsStringSlice(T, F);
pub fn get(field: T) []const u8 {
return fields[@enumToInt(field)];
}
};
},
.Struct => {
break :blk struct {
const map = struct_util.StructFieldsMap(
T,
struct_util.StructFieldsEnum(T, F),
);
pub fn get(field: []const u8) []const u8 {
return @tagName(map.get(field).?);
}
};
},
else => unreachable,
}
};
}
// copy of std.json.stringify to change field names
// using JsonMap when serializing
pub fn jsonStringify(
value: anytype,
options: std.json.StringifyOptions,
out_stream: anytype,
) !void {
const T = @TypeOf(value);
switch (@typeInfo(T)) {
.Enum => {
const member_name = T.json_map.get(value);
try std.json.stringify(member_name, options, out_stream);
},
.Struct => |S| {
try out_stream.writeByte(if (S.is_tuple) '[' else '{');
var field_output = false;
var child_options = options;
child_options.whitespace.indent_level += 1;
inline for (S.fields) |Field| {
// don't include void fields
if (Field.type == void) continue;
var emit_field = true;
// don't include optional fields that are null when emit_null_optional_fields is set to false
if (@typeInfo(Field.type) == .Optional) {
if (options.emit_null_optional_fields == false) {
if (@field(value, Field.name) == null) {
emit_field = false;
}
}
}
if (emit_field) {
if (!field_output) {
field_output = true;
} else {
try out_stream.writeByte(',');
}
try child_options.whitespace.outputIndent(out_stream);
if (!S.is_tuple) {
try std.json.encodeJsonString(
T.json_map.get(Field.name),
options,
out_stream,
);
try out_stream.writeByte(':');
if (child_options.whitespace.separator) {
try out_stream.writeByte(' ');
}
}
try std.json.stringify(@field(value, Field.name), child_options, out_stream);
}
}
if (field_output) {
try options.whitespace.outputIndent(out_stream);
}
try out_stream.writeByte(if (S.is_tuple) ']' else '}');
return;
},
else => unreachable,
}
}
pub fn replaceUnderscores(comptime o: []const u8) []const u8 {
@setEvalBranchQuota(10000);
var name: [o.len]u8 = undefined;
_ = std.mem.replace(u8, o, &[_]u8{'_'}, &[_]u8{'-'}, &name);
return &name;
}
|