use crate::chunks::key::ChunkKey;
use bevy::prelude::*;
use std::collections::*;
use suon_position::position::Position;
pub mod key;
#[derive(Resource, Default, Debug)]
pub struct Chunks {
inner: HashMap<ChunkKey, Entity>,
}
impl Chunks {
pub fn get(&self, position: &Position) -> Option<Entity> {
self.inner.get(&position.into()).cloned()
}
pub(crate) fn insert(&mut self, position: &Position, entity: Entity) {
self.inner.insert(position.into(), entity);
}
#[cfg(test)]
pub(crate) fn remove(&mut self, position: &Position) -> Option<Entity> {
self.inner.remove(&position.into())
}
pub fn contains(&self, position: &Position) -> bool {
self.inner.contains_key(&position.into())
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[cfg(test)]
pub(crate) fn clear(&mut self) {
self.inner.clear();
}
}
impl FromIterator<(Position, Entity)> for Chunks {
fn from_iter<T: IntoIterator<Item = (Position, Entity)>>(iter: T) -> Self {
let mut chunks = Self::default();
for (position, entity) in iter {
chunks.insert(&position, entity);
}
chunks
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_store_and_lookup_chunk_by_position() {
let mut chunks = Chunks::default();
const CHUNK: Entity = Entity::from_bits(7);
const POSITION: Position = Position { x: 12, y: 20 };
chunks.insert(&POSITION, CHUNK);
assert_eq!(
chunks.get(&POSITION),
Some(CHUNK),
"The stored chunk entity should be returned for the same position"
);
assert!(
chunks.contains(&POSITION),
"Inserted positions should be reported as present"
);
assert_eq!(chunks.len(), 1, "One inserted chunk should be tracked");
assert!(
!chunks.is_empty(),
"The registry should no longer be empty after insertion"
);
}
#[test]
fn should_share_same_chunk_mapping_inside_chunk_bounds() {
let mut chunks = Chunks::default();
const FIRST_CHUNK: Entity = Entity::from_bits(1);
const REPLACEMENT_CHUNK: Entity = Entity::from_bits(2);
const BASE_POSITION: Position = Position { x: 8, y: 16 };
const SAME_CHUNK_POSITION: Position = Position { x: 15, y: 23 };
chunks.insert(&BASE_POSITION, FIRST_CHUNK);
chunks.insert(&SAME_CHUNK_POSITION, REPLACEMENT_CHUNK);
assert_eq!(
chunks.get(&BASE_POSITION),
Some(REPLACEMENT_CHUNK),
"Positions in the same chunk key should resolve to the latest mapped entity"
);
assert_eq!(
chunks.get(&SAME_CHUNK_POSITION),
Some(REPLACEMENT_CHUNK),
"Both positions should share the same chunk entry"
);
assert_eq!(
chunks.len(),
1,
"Two positions inside one chunk should still occupy one registry slot"
);
}
#[test]
fn should_remove_mapping_and_clear_registry() {
let mut chunks = Chunks::default();
const CHUNK: Entity = Entity::from_bits(42);
const POSITION: Position = Position { x: 0, y: 0 };
chunks.insert(&POSITION, CHUNK);
assert_eq!(
chunks.remove(&POSITION),
Some(CHUNK),
"Removing a registered chunk should return its entity"
);
assert!(
chunks.get(&POSITION).is_none(),
"Removed positions should no longer resolve to a chunk"
);
chunks.insert(&POSITION, CHUNK);
chunks.clear();
assert!(
chunks.is_empty(),
"clear should drop all registered mappings"
);
}
#[test]
fn should_build_registry_from_iterator() {
const FIRST: Entity = Entity::from_bits(1);
const SECOND: Entity = Entity::from_bits(2);
let chunks = Chunks::from_iter([
(Position { x: 0, y: 0 }, FIRST),
(Position { x: 8, y: 0 }, SECOND),
]);
assert_eq!(
chunks.get(&Position { x: 7, y: 7 }),
Some(FIRST),
"FromIterator should register the first chunk mapping"
);
assert_eq!(
chunks.get(&Position { x: 8, y: 0 }),
Some(SECOND),
"FromIterator should register each provided chunk mapping"
);
}
}