| Crates.io | moonshine-tag |
| lib.rs | moonshine-tag |
| version | 0.3.0 |
| created_at | 2024-07-24 00:11:00.978115+00 |
| updated_at | 2025-07-16 00:30:41.980026+00 |
| description | Cheap, fast, mostly unique identifiers designed for Bevy |
| homepage | https://github.com/Zeenobit/moonshine_tag |
| repository | https://github.com/Zeenobit/moonshine_tag |
| max_upload_size | |
| id | 1313436 |
| size | 172,254 |
Cheap, fast, mostly unique identifiers designed for Bevy.
This crate is also included as part of 🍸 Moonshine Core.
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::*;
tags! { APPLE, ORANGE, JUICY, CRUNCHY, POISONED }
let mut world = World::new();
// Define some fruits!
let fruits = [
Tags::from([APPLE, CRUNCHY]),
Tags::from([ORANGE, JUICY]),
Tags::from([APPLE, CRUNCHY, POISONED])
];
// Only crunchy, edible apples, please! :)
let filter: TagFilter = tag_filter!([APPLE, CRUNCHY] & ![POISONED]);
for fruit in &fruits {
if filter.allows(fruit) {
world.spawn(fruit.clone());
}
}
# assert!(filter.allows(&fruits[0]));
u64.You may define Tags from any arbitrary string:
use moonshine_tag::prelude::*;
tags! { A }; // Convenient macro
const A1: Tag = Tag::new("A"); // Manual constant
let a2 = Tag::new("A"); // Runtime
assert_eq!(A, A1);
assert_eq!(A, 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::from(A);
let c = Tags::from([C]);
let ab = Tags::from([A, B]);
let ac = a.union(c);
Tags may be used as a Component or on its own as a generic collection of tags.
A tag [Filter] is used to test if a given Tags set matches a certain pattern:
use moonshine_tag::prelude::*;
tags! { A, B, C }
let a = Tags::from(A);
let c = Tags::from(C);
let a_or_b: TagFilter = TagFilter::any_of([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 = Tags::from([A, B]);
let cd = Tags::from([C, D]);
let c = Tags::from(C);
let filter = (TagFilter::all_of([A, B]) | TagFilter::any_of([C, B])) & TagFilter::any_of(D);
assert!(!filter.allows(&ab));
assert!(!filter.allows(&c));
assert!(filter.allows(&cd));
There is also a convenient filter! macro for constructing tag filters from tag expressions:
use moonshine_tag::prelude::*;
tags! { A, B, C, D }
let _: TagFilter = tag_filter!([A, B, ..]); // Matches any tag set containing A or B
let _: TagFilter = tag_filter!([A, B, ..] | [C]); // Matches any tag set which contains A or B, or exactly C
let _: TagFilter = tag_filter!(![C]); // Matches any tag set not containing C
⚠️ This macro is still in development.
When debugging or implementing tools, it is often useful to have some human-friendly representation of tags.
There are two methods provided to for human-friendly identification of tags:
pretty_hash
resolve_name
pretty_hash or cache the names in memory.tags! macro for this to work, otherwise pretty_hash is returned as fallback.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.
Filter back to TagFilter
tag::Filter and tag::filter! was a cute idea, but when moonshine_tag is used on its own, it would force the user to either import moonshine_tag as tag or use moonshine_tag::Filter. Both are ugly.allows functions into matches functions
pretty_hashresolve_nameto_pretty_string for Tags and TagFiltertag_filter!
See tests for examples
Please post an issue for any bugs, questions, or suggestions.
You may also contact me on the official Bevy Discord server as @Zeenobit.