From db5d7914d82d69021cf303e0ab02b46e0730bf48 Mon Sep 17 00:00:00 2001 From: Christian Segundo Date: Thu, 15 Jun 2023 19:54:16 +0200 Subject: one step closer --- src/util.zig | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 17 deletions(-) (limited to 'src/util.zig') diff --git a/src/util.zig b/src/util.zig index 41c3584..c46dbe7 100644 --- a/src/util.zig +++ b/src/util.zig @@ -1,23 +1,108 @@ const std = @import("std"); -/// Returns an slice of all the fields in the given type replacing all '_' with '-'. -pub fn enumFieldsToStringSlice(comptime E: type) []const []const u8 { - @setEvalBranchQuota(10000); - var names: []const []const u8 = &[_][]const u8{}; - for (std.meta.fields(E)) |field| { - var name: [field.name.len]u8 = undefined; - _ = std.mem.replace(u8, field.name, &[_]u8{'_'}, &[_]u8{'-'}, &name); - names = names ++ &[_][]const u8{&name}; - } - return names; +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, + } + }; } -pub fn enumFieldsToSlice(comptime T: type) []const T { - var fields: []const T = &[_]T{}; - inline for (@typeInfo(T).Enum.fields) |enumField| { - fields = fields ++ &[_]T{ - @field(T, enumField.name), - }; +// 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, } - return fields; +} + +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; } -- cgit v1.2.3