Radix/stdx/signature.zig

84 lines
2.9 KiB
Zig
Raw Normal View History

const std = @import("std");
const compileError = @import("debug.zig").compileError;
pub fn ArgsTuple(comptime funcT: anytype, comptime ArgsT: ?type) type {
const params = @typeInfo(funcT).Fn.params;
if (params.len == 0) {
return @TypeOf(.{});
}
if (@typeInfo(funcT).Fn.is_generic == false) {
return std.meta.ArgsTuple(funcT);
}
const args = std.meta.fields(ArgsT orelse compileError("generic function requires an explicit ArgsTuple", .{}));
var tuple_fields: [params.len]std.builtin.Type.StructField = undefined;
if (params.len != args.len) {
compileError("function {} expected {} args, got {}", .{ funcT, params.len, args.len });
}
inline for (params, args, 0..) |param, arg, i| {
if (param.type == null) {
tuple_fields[i] = arg;
continue;
}
const T = param.type.?;
var num_buf: [32]u8 = undefined;
tuple_fields[i] = .{
.name = blk: {
const s = std.fmt.formatIntBuf(&num_buf, i, 10, .lower, .{});
num_buf[s] = 0;
break :blk num_buf[0..s :0];
},
.type = T,
.default_value = null,
.is_comptime = false,
.alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
};
}
return @Type(.{
.Struct = .{
.is_tuple = true,
.layout = .auto,
.decls = &.{},
.fields = &tuple_fields,
},
});
}
pub fn FnSignature(comptime func: anytype, comptime ArgsT: ?type) type {
const n_params = switch (@typeInfo(@TypeOf(func))) {
.Fn => |fn_info| fn_info.params.len,
else => compileError("FnSignature expects a function as first argument got: {}", .{@TypeOf(func)}),
};
if (ArgsT != null) {
const n_args = switch (@typeInfo(ArgsT.?)) {
.Struct => |struct_info| struct_info.fields.len,
else => compileError("function {} need to be called with a tuple of args", .{@TypeOf(func)}),
};
if (n_params != n_args) {
compileError("function {} expected {} args, got {}", .{ @TypeOf(func), n_params, n_args });
}
}
return FnSignatureX(func, ArgsTuple(@TypeOf(func), ArgsT));
}
// TODO: I think this should return a struct instead of returing at type
// this gives a better error stacktrace because here the error is delayed to when the fields are read.
fn FnSignatureX(comptime func: anytype, comptime ArgsT_: type) type {
return struct {
pub const FuncT = @TypeOf(func);
pub const ArgsT = ArgsT_;
pub const ReturnT = @TypeOf(@call(.auto, func, @as(ArgsT_, undefined)));
pub const ReturnPayloadT = switch (@typeInfo(ReturnT)) {
.ErrorUnion => |u| u.payload,
else => ReturnT,
};
pub const ReturnErrorSet: ?type = switch (@typeInfo(ReturnT)) {
.ErrorUnion => |u| u.error_set,
else => null,
};
};
}