mindustry logic execution, map- and schematic- parsing and rendering
Support errors in block state serialization
KosmosPrime 2023-01-12
parent 15d2d4f · commit 738bfb7
-rw-r--r--src/block/logic.rs280
-rw-r--r--src/block/mod.rs95
-rw-r--r--src/block/simple.rs10
-rw-r--r--src/data/schematic.rs53
4 files changed, 361 insertions, 77 deletions
diff --git a/src/block/logic.rs b/src/block/logic.rs
index edd4297..d426970 100644
--- a/src/block/logic.rs
+++ b/src/block/logic.rs
@@ -1,12 +1,15 @@
-use std::any::{Any, type_name};
+use std::any::Any;
use std::borrow::Cow;
+use std::error::Error;
+use std::fmt;
+use std::string::FromUtf8Error;
-use flate2::{Compress, Compression, Decompress, FlushCompress, FlushDecompress, Status};
+use flate2::{Compress, CompressError, Compression, Decompress, DecompressError, FlushCompress, FlushDecompress, Status};
-use crate::block::{BlockLogic, make_register};
+use crate::block::{BlockLogic, DeserializeError, make_register, SerializeError};
use crate::block::simple::{SimpleBlock, state_impl};
-use crate::data::{DataRead, DataWrite};
-use crate::data::dynamic::DynData;
+use crate::data::{self, DataRead, DataWrite};
+use crate::data::dynamic::{DynData, DynType};
make_register!
(
@@ -45,13 +48,13 @@ impl BlockLogic for MessageLogic
DynData::Empty
}
- fn deserialize_state(&self, data: DynData) -> Option<Box<dyn Any>>
+ fn deserialize_state(&self, data: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError>
{
match data
{
- DynData::Empty | DynData::String(None) => Some(Self::create_state(String::new())),
- DynData::String(Some(s)) => Some(Self::create_state(s)),
- _ => panic!("{} cannot use data of {:?}", type_name::<Self>(), data.get_type()),
+ DynData::Empty | DynData::String(None) => Ok(Some(Self::create_state(String::new()))),
+ DynData::String(Some(s)) => Ok(Some(Self::create_state(s))),
+ _ => Err(DeserializeError::InvalidType{have: data.get_type(), expect: DynType::String}),
}
}
@@ -60,9 +63,9 @@ impl BlockLogic for MessageLogic
Box::new(Self::get_state(state).clone())
}
- fn serialize_state(&self, state: &dyn Any) -> DynData
+ fn serialize_state(&self, state: &dyn Any) -> Result<DynData, SerializeError>
{
- DynData::String(Some(Self::get_state(state).clone()))
+ Ok(DynData::String(Some(Self::get_state(state).clone())))
}
}
@@ -90,13 +93,13 @@ impl BlockLogic for SwitchLogic
DynData::Empty
}
- fn deserialize_state(&self, data: DynData) -> Option<Box<dyn Any>>
+ fn deserialize_state(&self, data: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError>
{
match data
{
- DynData::Empty => Some(Self::create_state(true)),
- DynData::Boolean(enabled) => Some(Self::create_state(enabled)),
- _ => panic!("{} cannot use data of {:?}", type_name::<Self>(), data.get_type()),
+ DynData::Empty => Ok(Some(Self::create_state(true))),
+ DynData::Boolean(enabled) => Ok(Some(Self::create_state(enabled))),
+ _ => Err(DeserializeError::InvalidType{have: data.get_type(), expect: DynType::Boolean}),
}
}
@@ -105,9 +108,9 @@ impl BlockLogic for SwitchLogic
Box::new(Self::get_state(state).clone())
}
- fn serialize_state(&self, state: &dyn Any) -> DynData
+ fn serialize_state(&self, state: &dyn Any) -> Result<DynData, SerializeError>
{
- DynData::Boolean(*Self::get_state(state))
+ Ok(DynData::Boolean(*Self::get_state(state)))
}
}
@@ -138,11 +141,11 @@ impl BlockLogic for ProcessorLogic
DynData::Empty
}
- fn deserialize_state(&self, data: DynData) -> Option<Box<dyn Any>>
+ fn deserialize_state(&self, data: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError>
{
match data
{
- DynData::Empty => Some(Self::create_state(ProcessorState::new())),
+ DynData::Empty => Ok(Some(Self::create_state(ProcessorState::new()))),
DynData::ByteArray(arr) =>
{
let mut input = arr.as_ref();
@@ -153,7 +156,7 @@ impl BlockLogic for ProcessorLogic
{
let t_in = dec.total_in();
let t_out = dec.total_out();
- let res = dec.decompress_vec(input, &mut raw, FlushDecompress::Finish).unwrap();
+ let res = ProcessorDeserializeError::forward(dec.decompress_vec(input, &mut raw, FlushDecompress::Finish))?;
if dec.total_in() > t_in
{
// we have to advance input every time, decompress_vec only knows the output position
@@ -169,43 +172,43 @@ impl BlockLogic for ProcessorLogic
if dec.total_in() == t_in && dec.total_out() == t_out
{
// protect against looping forever
- panic!("decompressor stalled");
+ return Err(DeserializeError::Custom(Box::new(ProcessorDeserializeError::DecompressStall)));
}
raw.reserve(1024);
}
let mut buff = DataRead::new(&raw);
- let ver = buff.read_u8().unwrap();
+ let ver = ProcessorDeserializeError::forward(buff.read_u8())?;
if ver != 1
{
- panic!("unknown version {ver}");
+ return Err(DeserializeError::Custom(Box::new(ProcessorDeserializeError::Version(ver))));
}
- let code_len = buff.read_i32().unwrap();
+ let code_len = ProcessorDeserializeError::forward(buff.read_i32())?;
if code_len < 0 || code_len > 500 * 1024
{
- panic!("invalid code length ({code_len})");
+ return Err(DeserializeError::Custom(Box::new(ProcessorDeserializeError::CodeLength(code_len))));
}
let mut code = Vec::<u8>::new();
code.resize(code_len as usize, 0);
- buff.read_bytes(&mut code).unwrap();
- let code = String::from_utf8(code).unwrap();
- let link_cnt = buff.read_i32().unwrap();
+ ProcessorDeserializeError::forward(buff.read_bytes(&mut code))?;
+ let code = ProcessorDeserializeError::forward(String::from_utf8(code))?;
+ let link_cnt = ProcessorDeserializeError::forward(buff.read_i32())?;
if link_cnt < 0
{
- panic!("link count is negative ({link_cnt})");
+ return Err(DeserializeError::Custom(Box::new(ProcessorDeserializeError::LinkCount(link_cnt))));
}
let mut links = Vec::<ProcessorLink>::new();
links.reserve(link_cnt as usize);
for _ in 0..link_cnt
{
- let name = buff.read_utf().unwrap();
- let x = buff.read_i16().unwrap();
- let y = buff.read_i16().unwrap();
+ let name = ProcessorDeserializeError::forward(buff.read_utf())?;
+ let x = ProcessorDeserializeError::forward(buff.read_i16())?;
+ let y = ProcessorDeserializeError::forward(buff.read_i16())?;
links.push(ProcessorLink{name: String::from(name), x, y});
}
- Some(Self::create_state(ProcessorState{code, links}))
+ Ok(Some(Self::create_state(ProcessorState{code, links})))
},
- _ => panic!("{} cannot use data of {:?}", type_name::<Self>(), data.get_type()),
+ _ => Err(DeserializeError::InvalidType{have: data.get_type(), expect: DynType::Boolean}),
}
}
@@ -214,27 +217,21 @@ impl BlockLogic for ProcessorLogic
Box::new(Self::get_state(state).clone())
}
- fn serialize_state(&self, state: &dyn Any) -> DynData
+ fn serialize_state(&self, state: &dyn Any) -> Result<DynData, SerializeError>
{
let state = Self::get_state(state);
let mut rbuff = DataWrite::new();
- rbuff.write_u8(1).unwrap();
- if state.code.len() > i32::MAX as usize
- {
- panic!("code too long ({})", state.code.len());
- }
- rbuff.write_i32(state.code.len() as i32).unwrap();
- rbuff.write_bytes(state.code.as_bytes()).unwrap();
- if state.links.len() > i32::MAX as usize
- {
- panic!("too many links ({})", state.links.len());
- }
- rbuff.write_i32(state.links.len() as i32).unwrap();
+ ProcessorSerializeError::forward(rbuff.write_u8(1))?;
+ assert!(state.code.len() < 500 * 1024);
+ ProcessorSerializeError::forward(rbuff.write_i32(state.code.len() as i32))?;
+ ProcessorSerializeError::forward(rbuff.write_bytes(state.code.as_bytes()))?;
+ assert!(state.links.len() < i32::MAX as usize);
+ ProcessorSerializeError::forward(rbuff.write_i32(state.links.len() as i32))?;
for link in state.links.iter()
{
- rbuff.write_utf(&link.name).unwrap();
- rbuff.write_i16(link.x).unwrap();
- rbuff.write_i16(link.y).unwrap();
+ ProcessorSerializeError::forward(rbuff.write_utf(&link.name))?;
+ ProcessorSerializeError::forward(rbuff.write_i16(link.x))?;
+ ProcessorSerializeError::forward(rbuff.write_i16(link.y))?;
}
let mut input = rbuff.get_written();
let mut comp = Compress::new(Compression::default(), true);
@@ -244,7 +241,7 @@ impl BlockLogic for ProcessorLogic
{
let t_in = comp.total_in();
let t_out = comp.total_out();
- let res = comp.compress_vec(input, &mut dst, FlushCompress::Finish).unwrap();
+ let res = ProcessorSerializeError::forward(comp.compress_vec(input, &mut dst, FlushCompress::Finish))?;
if comp.total_in() > t_in
{
// we have to advance input every time, compress_vec only knows the output position
@@ -260,11 +257,152 @@ impl BlockLogic for ProcessorLogic
if comp.total_in() == t_in && comp.total_out() == t_out
{
// protect against looping forever
- panic!("compressor stalled");
+ return Err(SerializeError::Custom(Box::new(ProcessorSerializeError::CompressStall)));
}
dst.reserve(1024);
}
- DynData::ByteArray(dst)
+ Ok(DynData::ByteArray(dst))
+ }
+}
+
+#[derive(Debug)]
+pub enum ProcessorDeserializeError
+{
+ Read(data::ReadError),
+ Decompress(DecompressError),
+ DecompressStall,
+ FromUtf8(FromUtf8Error),
+ Version(u8),
+ CodeLength(i32),
+ LinkCount(i32),
+}
+
+impl ProcessorDeserializeError
+{
+ pub fn forward<T, E: Into<Self>>(result: Result<T, E>) -> Result<T, DeserializeError>
+ {
+ match result
+ {
+ Ok(v) => Ok(v),
+ Err(e) => Err(DeserializeError::Custom(Box::new(e.into()))),
+ }
+ }
+}
+
+impl From<data::ReadError> for ProcessorDeserializeError
+{
+ fn from(value: data::ReadError) -> Self
+ {
+ Self::Read(value)
+ }
+}
+
+impl From<DecompressError> for ProcessorDeserializeError
+{
+ fn from(value: DecompressError) -> Self
+ {
+ Self::Decompress(value)
+ }
+}
+
+impl From<FromUtf8Error> for ProcessorDeserializeError
+{
+ fn from(value: FromUtf8Error) -> Self
+ {
+ Self::FromUtf8(value)
+ }
+}
+
+impl fmt::Display for ProcessorDeserializeError
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ {
+ match self
+ {
+ Self::Read(..) => write!(f, "Failed to read data from buffer"),
+ Self::Decompress(e) => e.fmt(f),
+ Self::DecompressStall => write!(f, "Decompressor stalled before completion"),
+ Self::FromUtf8(e) => e.fmt(f),
+ Self::Version(ver) => write!(f, "Unsupported version ({ver})"),
+ Self::CodeLength(len) => write!(f, "Invalid code length ({len})"),
+ Self::LinkCount(cnt) => write!(f, "Invalid link count ({cnt})"),
+ }
+ }
+}
+
+impl Error for ProcessorDeserializeError
+{
+ fn source(&self) -> Option<&(dyn Error + 'static)>
+ {
+ match self
+ {
+ Self::Decompress(e) => Some(e),
+ Self::FromUtf8(e) => Some(e),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum ProcessorSerializeError
+{
+ Write(data::WriteError),
+ Compress(CompressError),
+ CompressEof(usize),
+ CompressStall,
+}
+
+impl ProcessorSerializeError
+{
+ pub fn forward<T, E: Into<Self>>(result: Result<T, E>) -> Result<T, SerializeError>
+ {
+ match result
+ {
+ Ok(v) => Ok(v),
+ Err(e) => Err(SerializeError::Custom(Box::new(e.into()))),
+ }
+ }
+}
+
+impl From<data::WriteError> for ProcessorSerializeError
+{
+ fn from(value: data::WriteError) -> Self
+ {
+ Self::Write(value)
+ }
+}
+
+impl From<CompressError> for ProcessorSerializeError
+{
+ fn from(value: CompressError) -> Self
+ {
+ Self::Compress(value)
+ }
+}
+
+impl fmt::Display for ProcessorSerializeError
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ {
+ match self
+ {
+ Self::Write(..) => write!(f, "Failed to write data to buffer"),
+ Self::Compress(e) => e.fmt(f),
+ Self::CompressEof(remain) => write!(f, "Compression overflow with {remain} bytes of input remaining"),
+ Self::CompressStall => write!(f, "Compressor stalled before completion"),
+ }
+ }
+}
+
+impl Error for ProcessorSerializeError
+{
+ fn source(&self) -> Option<&(dyn Error + 'static)>
+ {
+ match self
+ {
+ Self::Compress(e) => Some(e),
+ _ => None,
+ }
}
}
@@ -345,13 +483,13 @@ impl ProcessorState
{
if name.len() > u16::MAX as usize
{
- return Err(CreateError::NameLength{len: name.len()})
+ return Err(CreateError::NameLength(name.len()))
}
for curr in self.links.iter()
{
if &name == &curr.name
{
- return Err(CreateError::DuplicateName{name});
+ return Err(CreateError::DuplicateName(name));
}
if x == curr.x && y == curr.y
{
@@ -386,10 +524,38 @@ pub enum CodeError
TooLong(usize),
}
+impl fmt::Display for CodeError
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ {
+ match self
+ {
+ Self::TooLong(len) => write!(f, "Code too long ({len} bytes)"),
+ }
+ }
+}
+
+impl Error for CodeError {}
+
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CreateError
{
- NameLength{len: usize},
- DuplicateName{name: String},
+ NameLength(usize),
+ DuplicateName(String),
DuplicatePos{name: String, x: i16, y: i16},
}
+
+impl fmt::Display for CreateError
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ {
+ match self
+ {
+ Self::NameLength(len) => write!(f, "Link name too long ({len} bytes)"),
+ Self::DuplicateName(name) => write!(f, "There already is a link named {name}"),
+ Self::DuplicatePos{name, x, y} => write!(f, "Link {name} already points to {x} / {y}"),
+ }
+ }
+}
+
+impl Error for CreateError {}
diff --git a/src/block/mod.rs b/src/block/mod.rs
index bcd475e..82ca0a0 100644
--- a/src/block/mod.rs
+++ b/src/block/mod.rs
@@ -2,9 +2,11 @@ use std::any::Any;
use std::borrow::Cow;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
+use std::error::Error;
+use std::fmt;
use crate::access::BoxAccess;
-use crate::data::dynamic::DynData;
+use crate::data::dynamic::{DynData, DynType};
pub mod base;
pub mod defense;
@@ -26,11 +28,94 @@ pub trait BlockLogic
fn data_from_i32(&self, config: i32) -> DynData;
- fn deserialize_state(&self, data: DynData) -> Option<Box<dyn Any>>;
+ fn deserialize_state(&self, data: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError>;
fn clone_state(&self, state: &dyn Any) -> Box<dyn Any>;
- fn serialize_state(&self, state: &dyn Any) -> DynData;
+ fn serialize_state(&self, state: &dyn Any) -> Result<DynData, SerializeError>;
+}
+
+#[derive(Debug)]
+pub enum DeserializeError
+{
+ InvalidType{have: DynType, expect: DynType},
+ Custom(Box<dyn Error>),
+}
+
+impl DeserializeError
+{
+ pub fn filter<T, E: Error + 'static>(result: Result<T, E>) -> Result<T, Self>
+ {
+ match result
+ {
+ Ok(v) => Ok(v),
+ Err(e) => Err(Self::Custom(Box::new(e))),
+ }
+ }
+}
+
+impl fmt::Display for DeserializeError
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ {
+ match self
+ {
+ Self::InvalidType{have, expect} => write!(f, "Expected type {expect:?} but got {have:?}"),
+ Self::Custom(e) => e.fmt(f),
+ }
+ }
+}
+
+impl Error for DeserializeError
+{
+ fn source(&self) -> Option<&(dyn Error + 'static)>
+ {
+ match self
+ {
+ Self::Custom(e) => Some(e.as_ref()),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum SerializeError
+{
+ Custom(Box<dyn Error>),
+}
+
+impl SerializeError
+{
+ pub fn filter<T, E: Error + 'static>(result: Result<T, E>) -> Result<T, Self>
+ {
+ match result
+ {
+ Ok(v) => Ok(v),
+ Err(e) => Err(Self::Custom(Box::new(e))),
+ }
+ }
+}
+
+impl fmt::Display for SerializeError
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ {
+ match self
+ {
+ Self::Custom(e) => e.fmt(f),
+ }
+ }
+}
+
+impl Error for SerializeError
+{
+ fn source(&self) -> Option<&(dyn Error + 'static)>
+ {
+ match self
+ {
+ Self::Custom(e) => Some(e.as_ref()),
+ }
+ }
}
pub struct Block
@@ -66,7 +151,7 @@ impl Block
self.logic.data_from_i32(config)
}
- pub fn deserialize_state(&self, data: DynData) -> Option<Box<dyn Any>>
+ pub fn deserialize_state(&self, data: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError>
{
self.logic.deserialize_state(data)
}
@@ -76,7 +161,7 @@ impl Block
self.logic.clone_state(state)
}
- pub fn serialize_state(&self, state: &dyn Any) -> DynData
+ pub fn serialize_state(&self, state: &dyn Any) -> Result<DynData, SerializeError>
{
self.logic.serialize_state(state)
}
diff --git a/src/block/simple.rs b/src/block/simple.rs
index c363064..87f631f 100644
--- a/src/block/simple.rs
+++ b/src/block/simple.rs
@@ -1,6 +1,6 @@
use std::any::{Any, type_name};
-use crate::block::BlockLogic;
+use crate::block::{BlockLogic, DeserializeError, SerializeError};
use crate::data::dynamic::DynData;
macro_rules!gen_state_empty
@@ -12,9 +12,9 @@ macro_rules!gen_state_empty
DynData::Empty
}
- fn deserialize_state(&self, _: DynData) -> Option<Box<dyn Any>>
+ fn deserialize_state(&self, _: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError>
{
- None
+ Ok(None)
}
fn clone_state(&self, _: &dyn Any) -> Box<dyn Any>
@@ -22,9 +22,9 @@ macro_rules!gen_state_empty
panic!("{} has no custom state", type_name::<Self>())
}
- fn serialize_state(&self, _: &dyn Any) -> DynData
+ fn serialize_state(&self, _: &dyn Any) -> Result<DynData, SerializeError>
{
- DynData::Empty
+ Ok(DynData::Empty)
}
};
}
diff --git a/src/data/schematic.rs b/src/data/schematic.rs
index 4cdad07..eca03a2 100644
--- a/src/data/schematic.rs
+++ b/src/data/schematic.rs
@@ -1,13 +1,14 @@
use std::any::Any;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
+use std::error::Error;
use std::fmt::{self, Write};
use std::iter::FusedIterator;
use std::slice::Iter;
use flate2::{Compress, CompressError, Compression, Decompress, DecompressError, FlushCompress, FlushDecompress, Status};
-use crate::block::{Block, BlockRegistry, Rotation};
+use crate::block::{self, Block, BlockRegistry, Rotation};
use crate::data::{self, DataRead, DataWrite, GridPos, Serializer};
use crate::data::base64;
use crate::data::dynamic::{self, DynSerializer, DynData};
@@ -53,10 +54,10 @@ impl Placement
}
}
- pub fn set_state(&mut self, data: DynData) -> Option<Box<dyn Any>>
+ pub fn set_state(&mut self, data: DynData) -> Result<Option<Box<dyn Any>>, block::DeserializeError>
{
- let state = self.block.deserialize_state(data);
- std::mem::replace(&mut self.state, state)
+ let state = self.block.deserialize_state(data)?;
+ Ok(std::mem::replace(&mut self.state, state))
}
pub fn get_rotation(&self) -> Rotation
@@ -261,7 +262,7 @@ impl Schematic
if self.is_region_empty(x - off, y - off, sz, sz)
{
let idx = self.blocks.len();
- let state = block.deserialize_state(data);
+ let state = block.deserialize_state(data)?;
self.blocks.push(Placement{pos: GridPos(x, y), block, state, rot});
self.fill_lookup(x as usize, y as usize, block.get_size() as usize, Some(idx));
Ok(&self.blocks[idx])
@@ -298,7 +299,7 @@ impl Schematic
}
}
let idx = self.blocks.len();
- let state = block.deserialize_state(data);
+ let state = block.deserialize_state(data)?;
self.blocks.push(Placement{pos: GridPos(x, y), block, state, rot});
self.fill_lookup(x as usize, y as usize, sz as usize, Some(idx));
Ok(result)
@@ -311,14 +312,14 @@ impl Schematic
None =>
{
let idx = self.blocks.len();
- let state = block.deserialize_state(data);
+ let state = block.deserialize_state(data)?;
self.blocks.push(Placement{pos: GridPos(x, y), block, state, rot});
self.lookup[pos] = Some(idx);
Ok(if collect {Some(Vec::new())} else {None})
},
Some(idx) =>
{
- let state = block.deserialize_state(data);
+ let state = block.deserialize_state(data)?;
let prev = std::mem::replace(&mut self.blocks[idx], Placement{pos: GridPos(x, y), block, state, rot});
self.fill_lookup(prev.pos.0 as usize, prev.pos.1 as usize, prev.block.get_size() as usize, None);
self.fill_lookup(x as usize, y as usize, sz as usize, Some(idx));
@@ -470,11 +471,20 @@ impl fmt::Display for PosError
}
}
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Debug)]
pub enum PlaceError
{
Bounds{x: u16, y: u16, sz: u8, w: u16, h: u16},
Overlap{x: u16, y: u16},
+ Deserialize(block::DeserializeError),
+}
+
+impl From<block::DeserializeError> for PlaceError
+{
+ fn from(value: block::DeserializeError) -> Self
+ {
+ PlaceError::Deserialize(value)
+ }
}
impl fmt::Display for PlaceError
@@ -485,6 +495,19 @@ impl fmt::Display for PlaceError
{
PlaceError::Bounds{x, y, sz, w, h} => write!(f, "Block placement {x} / {y} (size {sz}) within {w} / {h}"),
PlaceError::Overlap{x, y} => write!(f, "Overlapping an existing block at {x} / {y}"),
+ PlaceError::Deserialize(e) => e.fmt(f),
+ }
+ }
+}
+
+impl Error for PlaceError
+{
+ fn source(&self) -> Option<&(dyn Error + 'static)>
+ {
+ match self
+ {
+ PlaceError::Deserialize(e) => Some(e),
+ _ => None,
}
}
}
@@ -835,7 +858,7 @@ impl<'l> Serializer<Schematic> for SchematicSerializer<'l>
let data = match curr.state
{
None => DynData::Empty,
- Some(ref s) => curr.block.serialize_state(s.as_ref()),
+ Some(ref s) => curr.block.serialize_state(s.as_ref())?,
};
DynSerializer.serialize(&mut rbuff, &data)?;
rbuff.write_u8(curr.rot.into())?;
@@ -974,6 +997,7 @@ pub enum WriteError
Write(data::WriteError),
TagCount(usize),
TableSize(usize),
+ StateSerialize(block::SerializeError),
BlockState(dynamic::WriteError),
Compress(CompressError),
CompressEof(usize),
@@ -988,6 +1012,14 @@ impl From<data::WriteError> for WriteError
}
}
+impl From<block::SerializeError> for WriteError
+{
+ fn from(value: block::SerializeError) -> Self
+ {
+ Self::StateSerialize(value)
+ }
+}
+
impl From<CompressError> for WriteError
{
fn from(value: CompressError) -> Self
@@ -1013,6 +1045,7 @@ impl fmt::Display for WriteError
WriteError::Write(..) => write!(f, "Failed to write data to buffer"),
WriteError::TagCount(cnt) => write!(f, "Invalid tag count ({cnt})"),
WriteError::TableSize(cnt) => write!(f, "Invalid block table size ({cnt})"),
+ WriteError::StateSerialize(e) => e.fmt(f),
WriteError::BlockState(..) => write!(f, "Failed to write block state"),
WriteError::Compress(e) => e.fmt(f),
WriteError::CompressEof(remain) => write!(f, "Compression overflow with {remain} bytes of input remaining"),