From 8d53a185a487d76dec9a07dcde854d04921933db Mon Sep 17 00:00:00 2001 From: falsycat Date: Wed, 9 Apr 2025 00:18:06 +0900 Subject: [PATCH] add Merged() util --- src/hncore/Merged.zig | 72 +++++++++++++++++++++++++++++++++++++++++++ src/hncore/root.zig | 2 ++ 2 files changed, 74 insertions(+) create mode 100644 src/hncore/Merged.zig diff --git a/src/hncore/Merged.zig b/src/hncore/Merged.zig new file mode 100644 index 0000000..724a6a9 --- /dev/null +++ b/src/hncore/Merged.zig @@ -0,0 +1,72 @@ +const std = @import("std"); + +/// Returns a type having fields that type A and type B have. +/// Fields from B are more preferred than from A in case that field names are duplicated. +pub fn Merged(comptime A: type, comptime B: type) type { + const at = @typeInfo(A).@"struct"; + const bt = @typeInfo(B).@"struct"; + + return @Type(.{ + .@"struct" = .{ + .layout = .auto, + .fields = mergeFields(at.fields, bt.fields), + .is_tuple = false, + .decls = &.{}, + }, + }); +} +fn mergeFields( + comptime a: []const std.builtin.Type.StructField, + comptime b: []const std.builtin.Type.StructField) []std.builtin.Type.StructField { + var ret: [a.len+b.len]std.builtin.Type.StructField = undefined; + var len: usize = 0; + + inline for (b) |f| { + ret[len] = f; + ret[len].is_comptime = false; + len += 1; + } + inline for (a) |f| { + inline for (b) |fb| { + if (std.mem.eql(u8, f.name, fb.name)) { + break; + } + } else { + ret[len] = f; + ret[len].is_comptime = false; + len += 1; + } + } + return ret[0..len]; +} + +/// Same to `Merged(@TypeOf(a), @TypeOf(b))` but returns a merged value, not type. +pub fn merge(a: anytype, b: anytype) Merged(@TypeOf(a), @TypeOf(b)) { + var ret: Merged(@TypeOf(a), @TypeOf(b)) = undefined; + + const af = @typeInfo(@TypeOf(a)).@"struct".fields; + const bf = @typeInfo(@TypeOf(b)).@"struct".fields; + + inline for (bf) |f| { + @field(ret, f.name) = @field(b, f.name); + } + inline for (af) |f| { + inline for (bf) |fb| { + if (std.mem.eql(u8, f.name, fb.name)) { + break; + } + } else { + @field(ret, f.name) = @field(a, f.name); + } + } + return ret; + +} + +test "merging 2 structs" { + const merged = merge(.{.a = 0, .b = 1, .c = 2,}, .{.b = 44, .d = 53,}); + try std.testing.expect(merged.a == 0); + try std.testing.expect(merged.b == 44); + try std.testing.expect(merged.c == 2); + try std.testing.expect(merged.d == 53); +} diff --git a/src/hncore/root.zig b/src/hncore/root.zig index 1a3d019..e58974e 100644 --- a/src/hncore/root.zig +++ b/src/hncore/root.zig @@ -2,6 +2,8 @@ pub const Mindmap = @import("./Mindmap.zig"); pub const Node = @import("./Node.zig"); pub const Project = @import("./Project.zig"); +pub const merge = @import("./Merged.zig").merge; + test { @import("std").testing.refAllDecls(@This()); }