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