bevy-yoleck 0.4.0

Your Own Level Editor Creation Kit
Documentation
use std::any::Any;
use std::hash::{BuildHasher, Hash, Hasher};

use bevy::ecs::system::EntityCommands;
use bevy::prelude::*;
use bevy::utils::HashMap;

/// Marks an entity as a knob - a temporary helper entity used for editing another entity.
#[derive(Component)]
pub struct YoleckKnob;

#[doc(hidden)]
#[derive(Default, Resource)]
pub struct YoleckKnobsCache {
    by_key_hash: HashMap<u64, Vec<CachedKnob>>,
}

struct CachedKnob {
    key: Box<dyn Send + Sync + Any>,
    entity: Entity,
    keep_alive: bool,
}

impl YoleckKnobsCache {
    pub fn access<'w, 's, 'a, K>(
        &mut self,
        key: K,
        commands: &'a mut Commands<'w, 's>,
    ) -> KnobFromCache<'w, 's, 'a>
    where
        K: 'static + Send + Sync + Hash + Eq,
    {
        let mut hasher = self.by_key_hash.hasher().build_hasher();
        key.hash(&mut hasher);
        let entries = self.by_key_hash.entry(hasher.finish()).or_default();
        for entry in entries.iter_mut() {
            if let Some(cached_key) = entry.key.downcast_ref::<K>() {
                if key == *cached_key {
                    entry.keep_alive = true;
                    return KnobFromCache {
                        cmd: commands.entity(entry.entity),
                        is_new: false,
                    };
                }
            }
        }
        let cmd = commands.spawn(YoleckKnob);
        entries.push(CachedKnob {
            key: Box::new(key),
            entity: cmd.id(),
            keep_alive: true,
        });
        KnobFromCache { cmd, is_new: true }
    }

    pub fn clean_untouched(&mut self, mut clean_func: impl FnMut(Entity)) {
        self.by_key_hash.retain(|_, entries| {
            entries.retain_mut(|entry| {
                if entry.keep_alive {
                    entry.keep_alive = false;
                    true
                } else {
                    clean_func(entry.entity);
                    false
                }
            });
            !entries.is_empty()
        });
    }

    pub fn drain(&mut self) -> impl '_ + Iterator<Item = Entity> {
        self.by_key_hash
            .drain()
            .flat_map(|(_, entries)| entries.into_iter().map(|entry| entry.entity))
    }
}

pub struct KnobFromCache<'w, 's, 'a> {
    pub cmd: EntityCommands<'w, 's, 'a>,
    pub is_new: bool,
}