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