use crate::edge::NodeMap;
use bevy_ecs::{intern::Interned, prelude::*};
use bevy_log::prelude::*;
use smallvec::SmallVec;
pub use bevy_seedling_macros::NodeLabel;
bevy_ecs::define_label!(
NodeLabel,
NODE_LABEL_INTERNER
);
#[derive(NodeLabel, Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
pub struct MainBus;
pub type InternedNodeLabel = Interned<dyn NodeLabel>;
#[derive(Debug, Default, Component, Clone)]
#[component(immutable)]
pub struct NodeLabels(SmallVec<[InternedNodeLabel; 1]>);
impl NodeLabels {
pub(crate) fn on_add_observer(
trigger: Trigger<OnInsert, NodeLabels>,
labels: Query<&NodeLabels>,
mut map: ResMut<NodeMap>,
) -> Result {
let labels = labels.get(trigger.target())?;
for label in labels.iter() {
if let Some(existing) = map.insert(*label, trigger.target()) {
if existing != trigger.target() {
warn!("node label `{label:?}` has been applied to multiple entities");
}
}
}
Ok(())
}
pub(crate) fn on_replace_observer(
trigger: Trigger<OnReplace, NodeLabels>,
labels: Query<&NodeLabels>,
mut map: ResMut<NodeMap>,
) -> Result {
let labels = labels.get(trigger.target())?;
for label in labels.iter() {
map.remove(label);
}
Ok(())
}
}
impl core::ops::Deref for NodeLabels {
type Target = [InternedNodeLabel];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl NodeLabels {
pub fn insert(&mut self, label: InternedNodeLabel) -> bool {
if !self.contains(&label) {
self.0.push(label);
true
} else {
false
}
}
pub fn remove(&mut self, label: InternedNodeLabel) -> bool {
let index = self.iter().position(|l| l == &label);
match index {
Some(i) => {
self.0.remove(i);
true
}
None => false,
}
}
}
#[cfg(test)]
mod test {
use crate::{
edge::NodeMap,
prelude::*,
test::{prepare_app, run},
};
use bevy::prelude::*;
#[derive(NodeLabel, Debug, Clone, PartialEq, Eq, Hash)]
struct TestLabel;
#[derive(NodeLabel, Debug, Clone, PartialEq, Eq, Hash)]
struct TestLabelTwo;
#[test]
fn test_label_management() {
let interned_one = TestLabel.intern();
let interned_two = TestLabelTwo.intern();
let mut app = prepare_app(|mut commands: Commands| {
commands.spawn(SamplerPool(DefaultPool));
commands
.spawn((MainBus, VolumeNode::default()))
.connect(AudioGraphOutput);
commands.spawn((TestLabel, VolumeNode::default()));
});
run(
&mut app,
move |node: Query<Entity, With<TestLabel>>,
map: Res<NodeMap>,
mut commands: Commands| {
let node = node.single().unwrap();
assert_eq!(map[&interned_one], node);
commands.entity(node).insert(TestLabelTwo);
},
);
run(
&mut app,
move |node: Query<Entity, With<TestLabel>>,
map: Res<NodeMap>,
mut commands: Commands| {
let node = node.single().unwrap();
assert_eq!(map[&interned_one], node);
assert_eq!(map[&interned_two], node);
commands.entity(node).despawn();
},
);
run(&mut app, move |map: Res<NodeMap>| {
assert!(!map.contains_key(&interned_one));
assert!(!map.contains_key(&interned_two));
});
}
}