getty-zig / getty

A (de)serialization framework for Zig
https://getty.so
MIT License
183 stars 14 forks source link

(De)Ser maps into tuple slices #152

Closed Cloudef closed 7 months ago

Cloudef commented 7 months ago

Problem

As far as I see currently deser of maps requires a std type.

Consider type: const Headers = []const std.meta.Tuple(&.{ []const u8, []const u8 });

With getty-json, this serializes to array of arrays: [["a","b"],["c", "d"]]

Proposal

It would be nice you could control the behavior so that the tuple is used as key: value instead. [{"a":"b"},{"c":"d"}]

Is there currently way to do this?

Additional Context

No response

Cloudef commented 7 months ago

Okay so you can do it like this:

pub const HeaderKv = std.meta.Tuple(&.{ []const u8, []const u8 });
pub const Headers = []const HeaderKv;

const HeadersBlock = struct {
    pub fn is(comptime T: type) bool {
        return T == Headers;
    }

    pub fn serialize(_: ?std.mem.Allocator, value: anytype, serializer: anytype) @TypeOf(serializer).Err!@TypeOf(serializer).Ok {
        var m = try serializer.serializeMap(value.len);
        const map = m.map();
        for (value[0..]) |tuple| {
            try map.serializeEntry(tuple.@"0", tuple.@"1");
        }
        return try map.end();
    }

    pub fn deserialize(ally: ?std.mem.Allocator, comptime T: type, deserializer: anytype, visitor: anytype) @TypeOf(deserializer).Err!@TypeOf(visitor).Value {
        _ = T;
        return try deserializer.deserializeMap(ally.?, visitor);
    }

    pub fn Visitor(comptime Value: type) type {
        return struct {
            pub usingnamespace getty.de.Visitor(
                @This(),
                Value,
                .{ .visitMap = visitMap },
            );

            pub fn visitMap(_: @This(), ally: std.mem.Allocator, comptime Deserializer: type, map: anytype) Deserializer.Err!Value {
                const allocated = 32;
                var headers = try ally.alloc(HeaderKv, allocated);
                errdefer ally.free(headers);

                var len: usize = 0;
                while (try map.nextKey(ally, []const u8)) |k| {
                    if (len >= allocated) return error.InvalidLength;
                    const v = try map.nextValue(ally, []const u8);
                    headers[len] = .{ k, v };
                    len += 1;
                }

                return headers[0..len];
            }
        };
    }
};

And then you pass this block to getty-json's toSliceWith and fromSliceWith methods. Leaving this here as a reference.