Radix/stdx/meta.zig

181 lines
5.3 KiB
Zig

const std = @import("std");
const debug = @import("debug.zig");
const compileError = debug.compileError;
pub const FnSignature = @import("signature.zig").FnSignature;
pub fn isStruct(comptime T: type) bool {
return switch (@typeInfo(T)) {
.Struct => true,
else => false,
};
}
pub fn isTuple(comptime T: type) bool {
return switch (@typeInfo(T)) {
.Struct => |info| info.is_tuple,
else => false,
};
}
pub fn isStructOf(comptime T: type, comptime Elem: type) bool {
return switch (@typeInfo(T)) {
.Struct => |info| blk: {
inline for (info.fields) |field| {
if (field.type != Elem) {
break :blk false;
}
}
break :blk true;
},
else => false,
};
}
pub fn isStructOfAny(comptime T: type, comptime f: fn (comptime type) bool) bool {
return switch (@typeInfo(T)) {
.Struct => |info| blk: {
inline for (info.fields) |field| {
if (f(field.type) == false) {
break :blk false;
}
}
break :blk true;
},
else => false,
};
}
pub fn isTupleOf(comptime T: type, comptime Elem: type) bool {
return isTuple(T) and isStructOf(T, Elem);
}
pub fn isTupleOfAny(comptime T: type, comptime f: fn (comptime type) bool) bool {
return isTuple(T) and isStructOfAny(T, f);
}
pub fn isSliceOf(comptime T: type, comptime Elem: type) bool {
return switch (@typeInfo(T)) {
.Pointer => |info| switch (info.size) {
.Slice => info.child == Elem,
.One => switch (@typeInfo(info.child)) {
// As Zig, convert pointer to Array as a slice.
.Array => |arr_info| arr_info.child == Elem,
else => false,
},
else => false,
},
else => false,
};
}
pub fn isInteger(comptime T: type) bool {
return switch (@typeInfo(T)) {
.Int, .ComptimeInt => true,
else => false,
};
}
pub fn isSliceOfAny(comptime T: type, comptime f: fn (comptime type) bool) bool {
return switch (@typeInfo(T)) {
.Pointer => |info| info.size == .Slice and f(info.child),
else => false,
};
}
pub fn DeclEnum(comptime T: type) type {
const field_infos = std.meta.declarations(T);
if (field_infos.len == 0) {
compileError("Struct {} has no declarations", .{T});
}
return std.meta.DeclEnum(UnwrapPtr(T));
}
pub fn UnwrapPtr(comptime T: type) type {
return switch (@typeInfo(T)) {
.Pointer => |info| switch (info.size) {
.One => info.child,
else => T,
},
else => T,
};
}
pub fn asSlice(comptime T: type) type {
const err_msg = "Type " ++ @typeName(T) ++ " can't be interpreted as a slice";
return switch (@typeInfo(T)) {
.Pointer => |info| switch (info.size) {
.Slice => info.child,
.One => switch (@typeInfo(info.child)) {
// As Zig, convert pointer to Array as a slice.
.Array => |arr_info| arr_info.child,
else => @compileError(err_msg),
},
else => @compileError(err_msg),
},
else => @compileError(err_msg),
};
}
pub fn TupleRange(comptime T: type, comptime start: ?usize, comptime end: ?usize) type {
return TupleRangeX(T, start orelse 0, end orelse std.meta.fields(T).len);
}
pub fn TupleRangeX(comptime T: type, comptime start: usize, comptime end: usize) type {
const fields = std.meta.fields(T);
var new_fields: [end - start]std.builtin.Type.StructField = undefined;
inline for (start..end, 0..) |i, j| {
var new_field = fields[i];
var num_buf: [32]u8 = undefined;
new_field.name = blk: {
const s = std.fmt.formatIntBuf(&num_buf, j, 10, .lower, .{});
num_buf[s] = 0;
break :blk num_buf[0..s :0];
};
new_fields[j] = new_field;
}
return @Type(.{
.Struct = .{
.is_tuple = true,
.layout = .auto,
.decls = &.{},
.fields = &new_fields,
},
});
}
pub fn FnParam(comptime func: anytype, comptime n: comptime_int) type {
return @typeInfo(@TypeOf(func)).Fn.params[n].type orelse @compileError("anytype is not supported");
}
pub fn FnArgs(comptime func: anytype) type {
return FnSignature(func, null).ArgsT;
}
pub fn FnResult(comptime func: anytype) type {
return FnSignature(func, null).ReturnT;
}
pub fn Head(Tuple: type) type {
return switch (@typeInfo(Tuple)) {
.Struct => |struct_info| {
if (struct_info.fields.len == 0) @compileError("Can't tail empty tuple");
return struct_info.fields[0].type;
},
else => @compileError("Head works on tuple type"),
};
}
pub fn Tail(Tuple: type) type {
return switch (@typeInfo(Tuple)) {
.Struct => |struct_info| {
if (struct_info.fields.len == 0) @compileError("Can't tail empty tuple");
var types: [struct_info.fields.len - 1]type = undefined;
for (struct_info.fields[1..], 0..) |field, i| types[i] = field.type;
return std.meta.Tuple(&types);
},
else => @compileError("Tail works on tuple type"),
};
}