use crate::error::OutOfBoundsError;
use crate::response::ResponseStream;
use crate::{Block, Coordinate, Error, Size};
#[derive(Clone, Debug)]
pub struct Chunk {
list: Vec<Block>,
origin: Coordinate,
size: Size,
}
impl Chunk {
pub fn get_offset(&self, coordinate: impl Into<Coordinate>) -> Result<Block, OutOfBoundsError> {
let coordinate = coordinate.into();
if !self.size.contains(coordinate) {
return Err(OutOfBoundsError);
}
let index = self.size.offset_to_index(coordinate);
assert!(
index < self.list.len(),
"calculated index should be less than internal list length"
);
Ok(self.list[index])
}
pub fn get_worldspace(
&self,
coordinate: impl Into<Coordinate>,
) -> Result<Block, OutOfBoundsError> {
self.get_offset(coordinate.into() - self.origin)
}
pub const fn origin(&self) -> Coordinate {
self.origin
}
pub const fn size(&self) -> Size {
self.size
}
}
impl<'a> IntoIterator for &'a Chunk {
type Item = IterItem<'a>;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
Iter {
chunk: self,
index: 0,
}
}
}
#[derive(Debug)]
pub struct ChunkStream<'a> {
response: ResponseStream<'a>,
index: usize,
origin: Coordinate,
size: Size,
}
#[derive(Debug)]
pub struct ChunkStreamItem<'a> {
chunk: &'a ChunkStream<'a>,
index: usize,
block: Block,
}
impl<'a> ChunkStream<'a> {
pub(crate) fn new(
a: impl Into<Coordinate>,
b: impl Into<Coordinate>,
response: ResponseStream<'a>,
) -> Self {
let a = a.into();
let b = b.into();
Self {
response,
index: 0,
origin: a.min(b),
size: a.size_between(b),
}
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<ChunkStreamItem<'_>>, Error> {
if self.is_at_end() {
return Ok(None);
}
self.index += 1;
let block = if self.is_at_end() {
self.response.final_block()?
} else {
self.response.next_block()?
};
Ok(Some(ChunkStreamItem {
chunk: self,
block,
index: self.index,
}))
}
pub fn collect(mut self) -> Result<Chunk, Error> {
assert!(self.index == 0, "cannot collect partially-consumed stream");
let mut list = Vec::with_capacity(self.size().volume());
while let Some(item) = self.next()? {
list.push(item.block);
}
Ok(Chunk {
list,
origin: self.origin,
size: self.size,
})
}
pub const fn origin(&self) -> Coordinate {
self.origin
}
pub const fn size(&self) -> Size {
self.size
}
fn is_at_end(&self) -> bool {
self.index >= self.size.volume()
}
}
impl ChunkStreamItem<'_> {
pub fn block(&self) -> Block {
self.block
}
pub const fn position_offset(&self) -> Coordinate {
self.chunk.size.index_to_offset(self.index)
}
pub fn position_worldspace(&self) -> Coordinate {
self.position_offset() + self.chunk.origin
}
}
#[derive(Debug)]
pub struct Iter<'a> {
chunk: &'a Chunk,
index: usize,
}
#[derive(Debug)]
pub struct IterItem<'a> {
chunk: &'a Chunk,
index: usize,
}
impl<'a> Iterator for Iter<'a> {
type Item = IterItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.chunk.list.len() {
return None;
}
let index = self.index;
self.index += 1;
let item = IterItem {
chunk: self.chunk,
index,
};
Some(item)
}
}
impl<'a> IterItem<'a> {
pub const fn chunk(&self) -> &'a Chunk {
self.chunk
}
pub fn block(&self) -> Block {
*self
.chunk
.list
.get(self.index)
.expect("should be valid index in chunk")
}
pub const fn position_offset(&self) -> Coordinate {
self.chunk.size.index_to_offset(self.index)
}
pub fn position_worldspace(&self) -> Coordinate {
self.position_offset() + self.chunk.origin
}
}