add Merged() util

This commit is contained in:
falsycat 2025-04-09 00:18:06 +09:00
parent ca3d26a18d
commit 8d53a185a4
2 changed files with 74 additions and 0 deletions

72
src/hncore/Merged.zig Normal file
View File

@ -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);
}

View File

@ -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());
}