High-level wrapper around Flecs, a powerful ECS (Entity Component System) library, written in Zig language
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
6.3 KiB

//! Since the `std.meta.trait` module was removed from Zig's standard
//! library, we're re-introducing some of the functions we needed.
const std = @import("std");
/// Returns if the provided value is either a `null`
/// constant or an optional type with a `null` value.
pub fn isNull(value: anytype) bool {
return switch (@typeInfo(@TypeOf(value))) {
.Null => true,
.Optional => value == null,
else => false,
};
}
/// Returns if the provided type is a tuple.
pub fn isTuple(comptime T: type) bool {
return @typeInfo(T) == .Struct and @typeInfo(T).Struct.is_tuple;
}
/// Returns true if the passed type will coerce to []const u8.
/// Any of the following are considered strings:
/// ```
/// []const u8, [:S]const u8, *const [N]u8, *const [N:S]u8,
/// []u8, [:S]u8, *[:S]u8, *[N:S]u8.
/// ```
/// These types are not considered strings:
/// ```
/// u8, [N]u8, [*]const u8, [*:0]const u8,
/// [*]const [N]u8, []const u16, []const i8,
/// *const u8, ?[]const u8, ?*const [N]u8.
/// ```
pub fn isZigString(comptime T: type) bool {
return comptime blk: {
// Only pointer types can be strings, no optionals
const info = @typeInfo(T);
if (info != .Pointer) break :blk false;
const ptr = &info.Pointer;
// Check for CV qualifiers that would prevent coerction to []const u8
if (ptr.is_volatile or ptr.is_allowzero) break :blk false;
// If it's already a slice, simple check.
if (ptr.size == .Slice) {
break :blk ptr.child == u8;
}
// Otherwise check if it's an array type that coerces to slice.
if (ptr.size == .One) {
const child = @typeInfo(ptr.child);
if (child == .Array) {
const arr = &child.Array;
break :blk arr.child == u8;
}
}
break :blk false;
};
}
/// Returns the type of the specified expression, which must be either
/// just a `type`, or a tuple in the form of `.{ TRelation, TTarget }`,
/// representing a relationship pair in Flecs.
///
/// If the expression is a pair, the type is determined like this:
/// - If `TRelation` is a non-zero-sized type, it is returned.
/// - If `TTarget` is a non-zero-sized type, it is returned.
/// - Otherwise, a compile error is raised.
pub fn AnyToType(comptime expr: anytype) type {
switch (@typeInfo(@TypeOf(expr))) {
.Type => return expr,
.Struct => |s| {
if (!s.is_tuple or s.fields.len != 2)
@compileError("Expression must be a type or a tuple of two types");
const TRelation = expr[0];
const TTarget = expr[1];
if (@TypeOf(TRelation) != type) @compileError("TRelation must be a type, but is " ++ @typeName(@TypeOf(TRelation)));
if (@TypeOf(TTarget) != type) @compileError("TTarget must be a type, but is " ++ @typeName(@TypeOf(TTarget)));
if (@sizeOf(TRelation) > 0) return TRelation;
if (@sizeOf(TTarget) > 0) return TTarget;
@compileError("Either TRelation or TTarget must be a non-zero-sized type");
},
else => @compileError("Expression must be a type or a tuple of two types"),
}
}
/// Gets a Flecs-compatible symbol name for the specified type.
/// Supports structs (except anonymous or generic), as well as
/// single-item pointers to supported structs.
///
/// If the struct defines a public accessible `name`, it is used.
pub fn flecsTypeName(comptime T: type) [:0]const u8 {
const allowed_characters =
"abcdefghijklmnopqrstuvwxyz" ++
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++
"0123456789_";
switch (@typeInfo(T)) {
.Struct => {
comptime var name: [:0]const u8 = undefined;
comptime if (@hasDecl(T, "name")) {
name = @field(T, "name");
} else {
name = @typeName(T);
if (std.mem.indexOfScalar(u8, name, '{') != null)
@compileError("Expected type with simple name, got '" ++ @typeName(T) ++ "' (anonymous type?)");
// If name includes fully qualified namespace, strip it.
if (std.mem.lastIndexOfScalar(u8, name, '.')) |i|
name = name[(i + 1)..];
if (std.mem.indexOfScalar(u8, name, '(') != null)
@compileError("Expected type with simple name, got '" ++ @typeName(T) ++ "' (generic type?)");
if (std.mem.indexOf(u8, name, "__") != null)
@compileError("Expected type with simple name, got '" ++ @typeName(T) ++ "' (anonymous type?)");
if (std.mem.indexOfNone(u8, name, allowed_characters) != null)
@compileError("Expected type with simple name, got '" ++ @typeName(T) ++ "' (invalid characters?)");
};
return name;
},
.Pointer => |p| {
if (p.size != .One)
@compileError("Expected struct or single-item pointer, got '" ++ @typeName(T) ++ "'");
return flecsTypeName(p.child);
},
else => @compileError("Expected struct or single-item pointer, got '" ++ @typeName(T) ++ "'"),
}
}
const expect = @import("./test/expect.zig");
test flecsTypeName {
const Foo = struct {};
try expect.equal("Foo", flecsTypeName(Foo));
try expect.equal("Foo", flecsTypeName(*Foo));
const Bar = struct {
const Baz = struct {
const Quux = struct {};
};
};
try expect.equal("Quux", flecsTypeName(Bar.Baz.Quux));
try expect.equal("Anon", flecsTypeName((struct {
const Anon = struct {};
}).Anon));
try expect.equal("Random", flecsTypeName(std.Random));
// error: Expected struct or single-item pointer, got 'i32'
// _ = flecsTypeName(i32);
// error: Expected type with simple name, got 'array_list.ArrayListAligned(i32,null)' (generic type?)
// _ flecsTypeName(std.ArrayList(i32));
// error: Expected type with simple name, got 'meta.decltest.flecsTypeName__struct_...' (anonymous type?)
// _ = flecsTypeName(struct {});
// error: Expected type with simple name, got 'struct{comptime foo: comptime_int = 0}' (anonymous type?)
// _ = flecsTypeName(@TypeOf(.{ .foo = 0 }));
// error: Expected type with simple name, got 'meta.decltest.flecsTypeName.%&!*' (invalid characters?)
// const @"%&!*" = struct {};
// _ = flecsTypeName(@"%&!*");
}