use crate::utils::TypeIdMap;
use std::{any::TypeId, sync::Arc};
pub trait Tag: 'static {}
pub struct TagFactory {
tag_id_map: TypeIdMap<u32>,
next_id: u32,
}
pub struct TagTracker {
tags: Arc<[bool]>,
factory: Arc<TagFactory>,
}
impl Clone for TagTracker {
fn clone(&self) -> Self {
Self {
tags: Arc::clone(&self.tags),
factory: Arc::clone(&self.factory),
}
}
}
impl Default for TagFactory {
fn default() -> Self {
Self {
tag_id_map: TypeIdMap::default(),
next_id: 0,
}
}
}
impl TagFactory {
pub fn register_tag<T: Tag>(&mut self) -> u32 {
let id = self.next_id;
self.next_id += 1;
self.tag_id_map.insert(TypeId::of::<T>(), id);
id
}
pub fn tag_id<T: Tag>(&self) -> Option<u32> {
self.tag_id_map.get(&TypeId::of::<T>()).copied()
}
pub unsafe fn tag_id_unchecked<T: Tag>(&self) -> u32 { unsafe {
*self.tag_id_map.get(&TypeId::of::<T>()).unwrap_unchecked()
}}
pub fn new_tracker(this: &Arc<TagFactory>) -> TagTracker {
TagTracker {
tags: vec![false; this.next_id as usize].into(),
factory: Arc::clone(this),
}
}
}
impl TagTracker {
pub unsafe fn tag<T: Tag>(&mut self) { unsafe {
let id = self.factory.tag_id_unchecked::<T>();
Arc::get_mut_unchecked(&mut self.tags)[id as usize] = true;
}}
pub unsafe fn untag<T: Tag>(&mut self) { unsafe {
let id = self.factory.tag_id_unchecked::<T>();
Arc::get_mut_unchecked(&mut self.tags)[id as usize] = false;
}}
pub unsafe fn toggle_unchecked<T: Tag>(&mut self) { unsafe {
let id = self.factory.tag_id_unchecked::<T>();
let current = self.is_tagged::<T>();
Arc::get_mut_unchecked(&mut self.tags)[id as usize] = !current;
}}
pub fn is_tag_registered<T: Tag>(&self) -> bool {
self.factory.tag_id::<T>().is_some()
}
pub unsafe fn is_tagged<T: Tag>(&self) -> bool {
let id = self.factory.tag_id::<T>().unwrap();
self.tags[id as usize]
}
pub unsafe fn is_tagged_unchecked<T: Tag>(&self) -> bool { unsafe {
let id = self.factory.tag_id_unchecked::<T>();
self.tags[id as usize]
}}
pub unsafe fn untag_all(&mut self) { unsafe {
Arc::get_mut_unchecked(&mut self.tags)
.iter_mut()
.for_each(|tag| *tag = false);
}}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[derive(Tag)]
struct Flying;
#[derive(Tag)]
struct HasWings;
#[derive(Component)]
#[allow(unused)]
struct Bird(&'static str);
#[derive(Component)]
#[allow(unused)]
struct FlyingSpeed(f32);
#[test]
fn test_tags() {
let mut tagf = TagFactory::default();
tagf.register_tag::<Flying>();
tagf.register_tag::<HasWings>();
let mut world = World::with_tags(tagf);
let eagle = world.spawn((Bird("Eagle"), FlyingSpeed(10.0)));
let mut eagle_tracker = world.get_tag_tracker(eagle);
unsafe {
eagle_tracker.tag::<Flying>();
eagle_tracker.tag::<HasWings>();
}
unsafe {
assert!(eagle_tracker.is_tagged::<Flying>());
assert!(eagle_tracker.is_tagged::<HasWings>());
}
unsafe {
eagle_tracker.untag::<Flying>();
eagle_tracker.untag_all();
}
unsafe {
assert!(!eagle_tracker.is_tagged::<Flying>());
assert!(!eagle_tracker.is_tagged::<HasWings>());
}
unsafe {
eagle_tracker.tag::<Flying>();
assert!(eagle_tracker.is_tagged::<Flying>());
}
world.despawn(eagle);
unsafe {
assert!(!eagle_tracker.is_tagged::<Flying>());
assert!(!eagle_tracker.is_tagged::<HasWings>());
}
}
#[test]
fn test_tags_query() {}
}