const c = @import("./c.zig"); pub const IdError = error{ /// Id is `0`. IsNone, /// Id is or contains wildcards. IsWildcard, /// Id is not valid. IsInvalid, }; /// `Id`s are the things that can be added to an `Entity`. /// It can be an `Entity` or `Pair`, and can have optional id flags. pub fn Id(comptime ctx: anytype) type { return struct { const Self = @This(); const Context = @import("./context.zig").Context(ctx); const World = Context.World; const Entity = Context.Entity; const Pair = Context.Pair; world: *World, raw: c.ecs_id_t, pub fn fromRaw(world: *World, raw: c.ecs_id_t) Self { return .{ .world = world, .raw = raw }; } /// Ensures this `Id` is valid. /// That is, it can be added to an entity. /// /// An `Id` is valid if .. /// - .. it is not `0`. /// - .. it does not contain wildcards. /// - .. it does not contain invalid entities. pub fn ensureValid(self: Self) !void { if (self.raw == 0) return IdError.IsNone; if (c.ecs_id_is_wildcard(self.raw)) return IdError.IsWildcard; if (self.asPairUnsafe()) |p| try p.ensureValid() else if (!self.isEntity()) if (!c.ecs_is_valid(self.raw & c.ECS_COMPONENT_MASK)) return IdError.IsInvalid; } pub fn isEntity(self: Self) bool { return (self.raw & c.ECS_ID_FLAGS_MASK) != 0; } /// Attempts to return this `Id` as an `Entity`, or an error if it's /// not an entity, not a valid entity or not alive in the world. pub fn asEntityAlive(self: Self) !Entity { const result = self.asEntityUnsafe(); try result.ensureAlive(); return result; } /// Returns this `Id` as an `Entity`, or `null` otherwise. /// This assumes the `Id` is valid, or an invalid result could be returned. pub fn asEntityUnsafe(self: Self) ?Entity { return if (isEntity(self)) .{ .world = self.world, .raw = self.raw } else null; } pub fn isPair(self: Self) bool { return c.ecs_id_is_pair(self.raw); } /// Attempts to return this `Id` as a `Pair`, or an error if it's not /// a pair, or either of its elements are not valid or not alive in /// the world. pub fn asPairAlive(self: Self) !Pair { const result = Pair{ .world = self.world, .raw = self.raw }; try result.ensureAlive(); return result; } /// Attempts to return this `Id` as a `Pair`, or an error if it's not /// a pair, or either of its elements are not valid. pub fn asPairValid(self: Self) !Pair { const result = Pair{ .world = self.world, .raw = self.raw }; try result.ensureValid(); return result; } /// Returns this `Id` as a `Pair`, or `null` otherwise. /// This assumes the `Id` is valid, or an invalid result could be returned. pub fn asPairUnsafe(self: Self) ?Pair { return if (isPair(self)) .{ .world = self.world, .raw = self.raw } else null; } pub fn isWildcard(self: Self) bool { return c.ecs_id_is_wildcard(self.raw); } /// Returns whether this `Id` a tag. /// That is, a component without data / size. /// /// An `Id` is a tag when it .. /// - .. is an entity without the `Component` component. /// - .. has an `Component` with size member set to 0. /// - .. is a `Pair` where both elements are a tag. /// - .. is a `Pair` where `.relation` has the `Tag` tag. pub fn isTag(self: Self) bool { return c.ecs_id_is_tag(self.world.raw, self.raw); } /// Returns whether this `Id` represents a union. /// Only `Pair`s can be unions. /// /// An `Id` represents a union when .. /// - .. the `.relation` of the pair is `Union`. /// - .. the `.relation` of the pair has `Union`. pub fn isUnion(self: Self) bool { return c.ecs_id_is_union(self.world.raw, self.raw); } /// Returns whether this `Id` is in use in the world. /// That is, if it has been added to one or more tables. /// /// Note that empty tables may reference this `Id`, in which case /// this function can return `true`. Consider using `count()` instead. pub fn isInUse(self: Self) bool { return c.ecs_id_in_use(self.world.raw, self.raw); } /// Gets the number of entities in the world that have this `Id`. pub fn count(self: Self) usize { return @intCast(c.ecs_count_id(self.world.raw, self.raw)); } // Get the type for an id. // This function returns the type information for an id. The specified id can be // any valid id. For the rules on how type information is determined based on // id, see ecs_get_typeid. // TODO: const ecs_type_info_t* ecs_get_type_info(const ecs_world_t *world, ecs_id_t id); /// Returns the entity representing the component type for this `Id`, /// if it is associated with a type, or `null` otherwise. /// /// For a regular component with a non-zero size (an entity with the /// `flecs.core.Component` component) the operation will return the /// entity itself. /// /// For an entity that does not have the `Component` component, or /// its component is zero-sized, the operation will return `null`. /// /// For a `Pair`, the operation will return the type associated with /// the pair, by applying the following rules in order: /// - If `.relation` is a component, it is returned. /// - If `.relation` has `flecs.core.Tag`, `null` is returned. /// - If `.target` is a component, it is returned. /// - Otherwise, `null` is returned. pub fn typeId(self: Self) ?Entity { const raw = c.ecs_get_typeid(self.world.raw, self.raw); return if (raw != 0) Entity.fromRaw(self.world, raw); } /// Returns if the provided `pattern` matches this `Id`. /// The pattern may contain a wildcard (or wildcards, if a `Pair`). pub fn matches(self: Self, pattern: Self) bool { return c.ecs_id_match(self.raw, pattern.raw); } }; }