use bevy_ecs::{
prelude::{Entity, World},
world::Command,
};
use bevy_hierarchy::prelude::DespawnRecursiveExt;
use backtrace::Backtrace;
use crate::{
Broken, ConnectionFailure, EntryForScope, ForkTargetStorage, OperationError, OperationResult,
OrBroken, ScopeEntryStorage, SingleInputStorage, SingleTargetStorage, StreamTargetMap,
UnhandledErrors,
};
#[derive(Clone, Copy, Debug)]
pub(crate) struct Connect {
pub(crate) original_target: Entity,
pub(crate) new_target: Entity,
}
impl Command for Connect {
fn apply(self, world: &mut World) {
if let Err(OperationError::Broken(backtrace)) = try_connect(self, world) {
world
.get_resource_or_insert_with(UnhandledErrors::default)
.connections
.push(ConnectionFailure {
original_target: self.original_target,
new_target: self.new_target,
backtrace: backtrace.unwrap_or_else(Backtrace::new),
})
}
}
}
fn try_connect(connect: Connect, world: &mut World) -> OperationResult {
if let Some(EntryForScope(scope)) = world.get(connect.original_target) {
let scope = *scope;
world
.get_entity_mut(connect.new_target)
.or_broken()?
.insert(EntryForScope(scope));
world
.get_entity_mut(scope)
.or_broken()?
.insert(ScopeEntryStorage(connect.new_target));
world
.get_entity_mut(connect.original_target)
.or_broken()?
.despawn_recursive();
return Ok(());
}
let old_inputs = world
.get_entity_mut(connect.original_target)
.or_broken()?
.take::<SingleInputStorage>()
.or_broken()?
.take();
for input in old_inputs.into_iter() {
let mut input_mut = world.get_entity_mut(input).or_broken()?;
let mut connection_happened = false;
if let Some(mut target) = input_mut.get_mut::<SingleTargetStorage>() {
if target.get() == connect.original_target {
connection_happened = true;
target.set(connect.new_target);
}
}
if let Some(mut targets) = input_mut.get_mut::<ForkTargetStorage>() {
for target in &mut targets.0 {
if *target == connect.original_target {
connection_happened = true;
*target = connect.new_target;
}
}
}
if let Some(mut targets) = input_mut.get_mut::<StreamTargetMap>() {
for target in &mut targets.map {
if *target == connect.original_target {
connection_happened = true;
*target = connect.new_target;
}
}
}
if !connection_happened {
world
.get_resource_or_insert_with(UnhandledErrors::default)
.broken
.push(Broken {
node: input,
backtrace: Some(Backtrace::new()),
});
}
if let Some(mut new_inputs_mut) = world.get_mut::<SingleInputStorage>(connect.new_target) {
new_inputs_mut.add(input);
} else {
world
.get_entity_mut(connect.new_target)
.or_broken()?
.insert(SingleInputStorage::new(input));
}
}
world
.get_entity_mut(connect.original_target)
.or_broken()?
.despawn_recursive();
Ok(())
}