summaryrefslogtreecommitdiff
path: root/src/util.zig
blob: c46dbe775b1e895924e6c259ddca823d2e64b5bb (plain) (blame)
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;
}