moonshine-tag

Crates.iomoonshine-tag
lib.rsmoonshine-tag
version
sourcesrc
created_at2024-07-24 00:11:00.978115
updated_at2024-11-30 02:00:36.434381
descriptionCheap, fast, mostly unique identifiers designed for Bevy
homepagehttps://github.com/Zeenobit/moonshine_tag
repositoryhttps://github.com/Zeenobit/moonshine_tag
max_upload_size
id1313436
Cargo.toml error:TOML parse error at line 17, column 1 | 17 | autolib = false | ^^^^^^^ unknown field `autolib`, expected one of `name`, `version`, `edition`, `authors`, `description`, `readme`, `license`, `repository`, `homepage`, `documentation`, `build`, `resolver`, `links`, `default-run`, `default_dash_run`, `rust-version`, `rust_dash_version`, `rust_version`, `license-file`, `license_dash_file`, `license_file`, `licenseFile`, `license_capital_file`, `forced-target`, `forced_dash_target`, `autobins`, `autotests`, `autoexamples`, `autobenches`, `publish`, `metadata`, `keywords`, `categories`, `exclude`, `include`
size0
(Zeenobit)

documentation

README

🏷️ Moonshine Tag

crates.io downloads docs.rs license stars

Cheap, fast, mostly unique identifiers designed for Bevy.

Overview

A Tag represents a cheap, generic, somewhat unique identifier which may be used to associate "things" with each other or to dynamically flag entities.

use bevy::prelude::*;
use moonshine_tag::{prelude::*, self as tag};

tags! { APPLE, ORANGE, JUICY, CRUNCHY, POISONED }

let mut world = World::new();

// Spawn some fruits!
let a = world.spawn([APPLE, CRUNCHY].into_tags()).id();
let b = world.spawn([ORANGE, JUICY].into_tags()).id();
let c = world.spawn([APPLE, CRUNCHY, POISONED].into_tags()).id();

// Only crunchy, edible apples, please! :)
let filter: TagFilter = (APPLE & CRUNCHY) & !POISONED;

assert!(filter.allows(world.tags(a)));

assert!(!filter.allows(world.tags(b)));
assert!(!filter.allows(world.tags(c)));

Features

  • Tags are cheap to create, cheap to copy, cheap to compare and "unique enough". It's just a u64.
  • Serialization support for both tags and tag filters
  • Ability to define complex tag filter expressions
  • Simple implementation with no boilerplate and no procedural macros 🧘

Usage

Tags

You may define tags from any arbitrary string:

use moonshine_tag::prelude::*;

tags! { A0 }; // Convenient macro

const A1: Tag = Tag::new("A"); // Manual constant

let a2 = Tag::new("A"); // Runtime

assert_eq!(A0, A1);
assert_eq!(A0, a2);

Any two tags with the same name are considered equal.

Tags is a specialized collection for managing sets of tags:

use moonshine_tag::prelude::*;

tags! { A, B, C }

let a: Tags = A.into_tags();
let ab = [A, B].into_tags();
let c = C.into_tags();
let ac = a.union(c);

Tag Filters

A TagFilter is used to test if a given Tags set matches a certain pattern:

use moonshine_tag::prelude::*;

tags! { A, B, C }

let a = A.into_tags();
let c = C.into_tags();

let a_or_b: TagFilter = A | B;

assert!(a_or_b.allows(a));
assert!(!a_or_b.allows(c));

Tag filters may be combined which each other to create complex expressions:

use moonshine_tag::prelude::*;

tags! { A, B, C, D }

let ab = [A, B].into_tags();
let c = C.into_tags();
let cd = [C, D].into_tags();

let filter = ([A, B] | C) & D;

assert!(!filter.allows(ab));
assert!(!filter.allows(c));
assert!(filter.allows(cd));

Limitations and Guidelines

Internally, tags are just an FNV-1a (Why?) hash of their string representation. This makes them very cheap to use, but this means they are NOT guaranteed to be unique.

It is the assumption of this library that in most game application domains, this is a minor and unlikely problem.

In most applications, the chance of collision between two different tags within the same subsystem is very low, non-fatal, and easily correctable (just rename one of the tags!).

However, you should NOT use tags for any cryptographic purposes, or as globally unique identifiers.

Instead, prefer to use them for convenient, dynamic pattern matching or flagging "things" within your systems, especially entities.

Commit count: 6

cargo fmt