Split error enum into Encode vs. Decode errors
This commit is contained in:
+5
-5
@@ -1,10 +1,10 @@
|
||||
use codepage_437::CP437_WINGDINGS;
|
||||
|
||||
use super::error::ParseError;
|
||||
use super::error::EncodeError;
|
||||
|
||||
/// Serialize a multi-line code string for an object or a scroll.
|
||||
/// Uses ZZT's convention of CR-terminated lines.
|
||||
pub fn encode_multiline(input: &str) -> Result<Vec<u8>, ParseError> {
|
||||
pub fn encode_multiline(input: &str) -> Result<Vec<u8>, EncodeError> {
|
||||
input
|
||||
.chars()
|
||||
.map(|c| {
|
||||
@@ -13,7 +13,7 @@ pub fn encode_multiline(input: &str) -> Result<Vec<u8>, ParseError> {
|
||||
} else {
|
||||
CP437_WINGDINGS
|
||||
.encode(c)
|
||||
.ok_or_else(|| ParseError::EncodingError(c))
|
||||
.ok_or_else(|| EncodeError::EncodingError(c))
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
@@ -35,13 +35,13 @@ pub fn decode_multiline(input: &[u8]) -> String {
|
||||
|
||||
/// Serialize a single-line string for a board title.
|
||||
/// Newlines cannot be encoded because ZZT interprets them as dingbats (♪).
|
||||
pub fn encode_oneline(input: &str) -> Result<Vec<u8>, ParseError> {
|
||||
pub fn encode_oneline(input: &str) -> Result<Vec<u8>, EncodeError> {
|
||||
input
|
||||
.chars()
|
||||
.map(|c| {
|
||||
CP437_WINGDINGS
|
||||
.encode(c)
|
||||
.ok_or_else(|| ParseError::EncodingError(c))
|
||||
.ok_or_else(|| EncodeError::EncodingError(c))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
+19
-13
@@ -2,7 +2,7 @@ use nom::error::{ErrorKind, ParseError as NomParseError};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ParseError {
|
||||
pub enum DecodeError {
|
||||
#[error("invalid ZZT file magic")]
|
||||
InvalidMagic,
|
||||
|
||||
@@ -12,20 +12,11 @@ pub enum ParseError {
|
||||
#[error("negative stat count")]
|
||||
NegativeStatCount,
|
||||
|
||||
#[error("string too long")]
|
||||
StringTooLong { max: u8 },
|
||||
|
||||
#[error("cannot encode character: {0}")]
|
||||
EncodingError(char),
|
||||
|
||||
#[error("board data too large")]
|
||||
BoardTooLarge,
|
||||
|
||||
#[error("nom error: {0}")]
|
||||
NomError(String),
|
||||
}
|
||||
|
||||
impl<I> NomParseError<I> for ParseError {
|
||||
impl<I> NomParseError<I> for DecodeError {
|
||||
fn from_error_kind(_input: I, kind: ErrorKind) -> Self {
|
||||
Self::NomError(kind.description().to_string())
|
||||
}
|
||||
@@ -35,11 +26,26 @@ impl<I> NomParseError<I> for ParseError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nom::Err<ParseError>> for ParseError {
|
||||
fn from(value: nom::Err<ParseError>) -> Self {
|
||||
impl From<nom::Err<DecodeError>> for DecodeError {
|
||||
fn from(value: nom::Err<DecodeError>) -> Self {
|
||||
match value {
|
||||
nom::Err::Error(e) | nom::Err::Failure(e) => e,
|
||||
nom::Err::Incomplete(needed) => Self::NomError(format!("incomplete: {:?}", needed)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum EncodeError {
|
||||
#[error("invalid tile count: {0}")]
|
||||
InvalidTileCount(usize),
|
||||
|
||||
#[error("string too long")]
|
||||
StringTooLong { max: u8 },
|
||||
|
||||
#[error("cannot encode character: {0}")]
|
||||
EncodingError(char),
|
||||
|
||||
#[error("board data too large")]
|
||||
BoardTooLarge,
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ mod parse;
|
||||
|
||||
pub use elements::Element;
|
||||
pub use encoding::{decode_multiline, decode_oneline, encode_multiline, encode_oneline};
|
||||
pub use error::ParseError;
|
||||
pub use error::{DecodeError, EncodeError};
|
||||
pub use parse::{Board, Program, Stat, Tile, World};
|
||||
|
||||
+21
-21
@@ -9,7 +9,7 @@ use nom::{
|
||||
|
||||
use super::elements::Element;
|
||||
use super::encoding::{decode_multiline, decode_oneline, encode_multiline, encode_oneline};
|
||||
use super::error::ParseError;
|
||||
use super::error::{DecodeError, EncodeError};
|
||||
|
||||
/// A single tile on a ZZT board.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -161,13 +161,13 @@ impl Default for World {
|
||||
|
||||
// Parsing helpers
|
||||
|
||||
fn bool_u8(input: &[u8]) -> IResult<&[u8], bool, ParseError> {
|
||||
fn bool_u8(input: &[u8]) -> IResult<&[u8], bool, DecodeError> {
|
||||
let (input, byte) = le_u8(input)?;
|
||||
Ok((input, byte != 0))
|
||||
}
|
||||
|
||||
fn pstring(cap: u8) -> impl Fn(&[u8]) -> IResult<&[u8], String, ParseError> {
|
||||
move |input: &[u8]| -> IResult<&[u8], String, ParseError> {
|
||||
fn pstring(cap: u8) -> impl Fn(&[u8]) -> IResult<&[u8], String, DecodeError> {
|
||||
move |input: &[u8]| -> IResult<&[u8], String, DecodeError> {
|
||||
let (input, len) = le_u8(input)?;
|
||||
let actual_len = len.min(cap);
|
||||
let (input, data) = take(actual_len)(input)?;
|
||||
@@ -176,7 +176,7 @@ fn pstring(cap: u8) -> impl Fn(&[u8]) -> IResult<&[u8], String, ParseError> {
|
||||
}
|
||||
}
|
||||
|
||||
fn board_slice(bytes: &[u8]) -> IResult<&[u8], &[u8], ParseError> {
|
||||
fn board_slice(bytes: &[u8]) -> IResult<&[u8], &[u8], DecodeError> {
|
||||
let (_, size) = le_u16.parse(bytes)?;
|
||||
take(size as usize + 2).parse(bytes)
|
||||
}
|
||||
@@ -186,7 +186,7 @@ fn board_slice(bytes: &[u8]) -> IResult<&[u8], &[u8], ParseError> {
|
||||
trait SerializationHelpers {
|
||||
fn push_bool(&mut self, value: bool);
|
||||
fn push_i16(&mut self, value: i16);
|
||||
fn push_string(&mut self, cap: u8, value: &str) -> Result<(), ParseError>;
|
||||
fn push_string(&mut self, cap: u8, value: &str) -> Result<(), EncodeError>;
|
||||
fn push_padding(&mut self, size: usize);
|
||||
}
|
||||
|
||||
@@ -199,10 +199,10 @@ impl SerializationHelpers for Vec<u8> {
|
||||
self.extend(value.to_le_bytes());
|
||||
}
|
||||
|
||||
fn push_string(&mut self, cap: u8, value: &str) -> Result<(), ParseError> {
|
||||
fn push_string(&mut self, cap: u8, value: &str) -> Result<(), EncodeError> {
|
||||
let bytes = encode_oneline(value)?;
|
||||
if bytes.len() > cap as usize {
|
||||
return Err(ParseError::StringTooLong { max: cap });
|
||||
return Err(EncodeError::StringTooLong { max: cap });
|
||||
}
|
||||
self.push(bytes.len() as u8);
|
||||
self.extend_from_slice(&bytes);
|
||||
@@ -217,10 +217,10 @@ impl SerializationHelpers for Vec<u8> {
|
||||
|
||||
impl World {
|
||||
/// Parse a ZZT world from bytes.
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<World, ParseError> {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<World, DecodeError> {
|
||||
let (input, _) = tag(&[0xff, 0xff][..])
|
||||
.parse(bytes)
|
||||
.map_err(|_: nom::Err<ParseError>| ParseError::InvalidMagic)?;
|
||||
.map_err(|_: nom::Err<DecodeError>| DecodeError::InvalidMagic)?;
|
||||
let (input, num_boards) = le_i16.parse(input)?;
|
||||
let (input, (ammo, gems, keys)) = (le_i16, le_i16, count(bool_u8, 7)).parse(input)?;
|
||||
let (input, (health, starting_board, torches, torch_cycles, energizer_cycles)) =
|
||||
@@ -235,7 +235,7 @@ impl World {
|
||||
// Load boards
|
||||
let num_boards = num_boards as usize + 1;
|
||||
let (_input, chunks) = count(board_slice, num_boards).parse(input)?;
|
||||
let boards: Result<Vec<Board>, ParseError> = chunks
|
||||
let boards: Result<Vec<Board>, DecodeError> = chunks
|
||||
.iter()
|
||||
.map(|bytes: &&[u8]| Board::from_bytes(bytes))
|
||||
.collect();
|
||||
@@ -261,7 +261,7 @@ impl World {
|
||||
}
|
||||
|
||||
/// Serialize this world to bytes.
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, ParseError> {
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, EncodeError> {
|
||||
let mut result = Vec::with_capacity(512);
|
||||
result.push_i16(-1); // file magic: ZZT world
|
||||
result.push_i16(self.boards.len() as i16 - 1);
|
||||
@@ -295,7 +295,7 @@ impl World {
|
||||
|
||||
impl Board {
|
||||
/// Parse a board from bytes (including the 2-byte size header).
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Board, ParseError> {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Board, DecodeError> {
|
||||
// Ignore length bytes
|
||||
let (input, _) = le_u16.parse(bytes)?;
|
||||
|
||||
@@ -313,7 +313,7 @@ impl Board {
|
||||
for _ in 0..count {
|
||||
tiles.push(Tile { element, color });
|
||||
if tiles.len() > NUM_TILES {
|
||||
return Err(ParseError::InvalidTileCount(tiles.len()));
|
||||
return Err(DecodeError::InvalidTileCount(tiles.len()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +334,7 @@ impl Board {
|
||||
let (input, num_stats) = le_i16(input)?;
|
||||
let num_stats = num_stats + 1;
|
||||
if num_stats < 0 {
|
||||
return Err(ParseError::NegativeStatCount);
|
||||
return Err(DecodeError::NegativeStatCount);
|
||||
}
|
||||
let (_input, stats) = count(Stat::parse, num_stats as usize).parse(input)?;
|
||||
|
||||
@@ -357,19 +357,19 @@ impl Board {
|
||||
}
|
||||
|
||||
/// Parse a standalone .brd file (same format, no world header).
|
||||
pub fn from_brd_bytes(bytes: &[u8]) -> Result<Board, ParseError> {
|
||||
pub fn from_brd_bytes(bytes: &[u8]) -> Result<Board, DecodeError> {
|
||||
Board::from_bytes(bytes)
|
||||
}
|
||||
|
||||
/// Serialize this board to bytes.
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, ParseError> {
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, EncodeError> {
|
||||
let mut result = vec![];
|
||||
result.push_padding(2); // reserve space for board size
|
||||
result.push_string(50, &self.name)?;
|
||||
|
||||
// Encode terrain
|
||||
if self.tiles.len() != 1500 {
|
||||
return Err(ParseError::InvalidTileCount(self.tiles.len()));
|
||||
return Err(EncodeError::InvalidTileCount(self.tiles.len()));
|
||||
}
|
||||
let mut iter = self.tiles.iter().peekable();
|
||||
while let Some(tile) = iter.next() {
|
||||
@@ -407,7 +407,7 @@ impl Board {
|
||||
// Fix up board size
|
||||
let size: u16 = (result.len() - 2)
|
||||
.try_into()
|
||||
.map_err(|_| ParseError::BoardTooLarge)?;
|
||||
.map_err(|_| EncodeError::BoardTooLarge)?;
|
||||
result.splice(0..2, size.to_le_bytes());
|
||||
|
||||
Ok(result)
|
||||
@@ -415,7 +415,7 @@ impl Board {
|
||||
}
|
||||
|
||||
impl Stat {
|
||||
fn parse(input: &[u8]) -> IResult<&[u8], Self, ParseError> {
|
||||
fn parse(input: &[u8]) -> IResult<&[u8], Self, DecodeError> {
|
||||
let (input, (x, y, x_step, y_step)) = (le_u8, le_u8, le_i16, le_i16).parse(input)?;
|
||||
let (input, (cycle, p1, p2, p3)) = (le_i16, le_u8, le_u8, le_u8).parse(input)?;
|
||||
let (input, (follower, leader)) = (le_i16, le_i16).parse(input)?;
|
||||
@@ -459,7 +459,7 @@ impl Stat {
|
||||
))
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Result<Vec<u8>, ParseError> {
|
||||
fn to_bytes(&self) -> Result<Vec<u8>, EncodeError> {
|
||||
let mut result = vec![];
|
||||
result.push(self.x);
|
||||
result.push(self.y);
|
||||
|
||||
Reference in New Issue
Block a user