use std::error::Error;
use std::fmt::{Display, Formatter};
use crate::{BoxID, GoalID, PusherID};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BoardSyntaxErrorKind {
RleDecodingFailed,
IllegalCharacter {
input: char,
x: usize,
y: usize,
},
InvalidHexobanLayout { x: usize, y: usize },
InputSizeNotMatchedByDeclaredSize {
input: usize,
declared_width: usize,
declared_height: usize,
},
TooManyBoxes,
TooManyGoals,
TooManyPushers,
}
#[derive(Debug)]
struct ErrorPayload {
kind: BoardSyntaxErrorKind,
source: Option<Box<dyn Error>>,
}
#[derive(Debug)]
pub struct BoardSyntaxError(Box<ErrorPayload>);
impl BoardSyntaxError {
pub fn kind(&self) -> &BoardSyntaxErrorKind { &self.0.kind }
pub fn new(kind: BoardSyntaxErrorKind) -> Self {
Self(Box::new(ErrorPayload { kind, source: None }))
}
pub fn new_with_source<E>(kind: BoardSyntaxErrorKind, source: Option<E>) -> Self
where
E: Into<Box<dyn Error + Send + Sync>>,
{
Self(Box::new(ErrorPayload {
kind,
source: match source {
Some(e) => Some(e.into()),
None => None,
},
}))
}
}
impl Display for BoardSyntaxError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.0.kind {
BoardSyntaxErrorKind::RleDecodingFailed => {
write!(f, "failed to expand run length encoded game board string")
}
BoardSyntaxErrorKind::IllegalCharacter { input: value, x, y } => {
write!(
f,
"parsing game board encountered invalid character '{value}' in \
row {y} at col {x}"
)
}
BoardSyntaxErrorKind::InvalidHexobanLayout { x, y } => {
write!(
f,
"invalid Hexoban textual board layout. Character at ({x}, {y}) is not \
consistent with the rest of the board. Hexoban boards represent each tile \
with two characters, either space+tile or tile+space. This encoding scheme \
must be used for the whole board consistently."
)
}
BoardSyntaxErrorKind::InputSizeNotMatchedByDeclaredSize {
input,
declared_width,
declared_height,
} => {
write!(
f,
"length of input 1D sequence of tiles ({input}) doesn't match declared 2D \
board W x H ({declared_width} x {declared_height})"
)
}
BoardSyntaxErrorKind::TooManyBoxes => {
write!(
f,
"game board contains more boxes than supported maximum of {}",
u32::from(BoxID::MAX) + 1
)
}
BoardSyntaxErrorKind::TooManyGoals => {
write!(
f,
"game board contains more goals than supported maximum of {}",
u32::from(GoalID::MAX) + 1
)
}
BoardSyntaxErrorKind::TooManyPushers => {
write!(
f,
"game board contains more pushers than supported maximum of {}",
u32::from(PusherID::MAX) + 1
)
}
}
}
}
impl Error for BoardSyntaxError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.0.source {
Some(ref_box_dyn) => Some(ref_box_dyn.as_ref()),
None => None,
}
}
}