sokoban_elements/game_board/
errors.rs1use std::error::Error;
2use std::fmt::{Display, Formatter};
3
4use crate::{BoxID, GoalID, PusherID};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum BoardSyntaxErrorKind {
9 RleDecodingFailed,
11
12 IllegalCharacter {
14 input: char,
15 x: usize,
16 y: usize,
17 },
18
19 InvalidHexobanLayout { x: usize, y: usize },
22
23 InputSizeNotMatchedByDeclaredSize {
26 input: usize,
27 declared_width: usize,
28 declared_height: usize,
29 },
30
31 TooManyBoxes,
34
35 TooManyGoals,
38
39 TooManyPushers,
42}
43
44#[derive(Debug)]
45struct ErrorPayload {
46 kind: BoardSyntaxErrorKind,
47 source: Option<Box<dyn Error>>,
48}
49
50#[derive(Debug)]
52pub struct BoardSyntaxError(Box<ErrorPayload>);
53
54impl BoardSyntaxError {
55 pub fn kind(&self) -> &BoardSyntaxErrorKind { &self.0.kind }
56
57 pub fn new(kind: BoardSyntaxErrorKind) -> Self {
58 Self(Box::new(ErrorPayload { kind, source: None }))
59 }
60
61 pub fn new_with_source<E>(kind: BoardSyntaxErrorKind, source: Option<E>) -> Self
62 where
63 E: Into<Box<dyn Error + Send + Sync>>,
64 {
65 Self(Box::new(ErrorPayload {
66 kind,
67 source: match source {
68 Some(e) => Some(e.into()),
69 None => None,
70 },
71 }))
72 }
73}
74
75impl Display for BoardSyntaxError {
76 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
77 match &self.0.kind {
78 BoardSyntaxErrorKind::RleDecodingFailed => {
79 write!(f, "failed to expand run length encoded game board string")
80 }
81
82 BoardSyntaxErrorKind::IllegalCharacter { input: value, x, y } => {
83 write!(
84 f,
85 "parsing game board encountered invalid character '{value}' in \
86 row {y} at col {x}"
87 )
88 }
89
90 BoardSyntaxErrorKind::InvalidHexobanLayout { x, y } => {
91 write!(
92 f,
93 "invalid Hexoban textual board layout. Character at ({x}, {y}) is not \
94 consistent with the rest of the board. Hexoban boards represent each tile \
95 with two characters, either space+tile or tile+space. This encoding scheme \
96 must be used for the whole board consistently."
97 )
98 }
99
100 BoardSyntaxErrorKind::InputSizeNotMatchedByDeclaredSize {
101 input,
102 declared_width,
103 declared_height,
104 } => {
105 write!(
106 f,
107 "length of input 1D sequence of tiles ({input}) doesn't match declared 2D \
108 board W x H ({declared_width} x {declared_height})"
109 )
110 }
111
112 BoardSyntaxErrorKind::TooManyBoxes => {
113 write!(
114 f,
115 "game board contains more boxes than supported maximum of {}",
116 u32::from(BoxID::MAX) + 1
117 )
118 }
119
120 BoardSyntaxErrorKind::TooManyGoals => {
121 write!(
122 f,
123 "game board contains more goals than supported maximum of {}",
124 u32::from(GoalID::MAX) + 1
125 )
126 }
127
128 BoardSyntaxErrorKind::TooManyPushers => {
129 write!(
130 f,
131 "game board contains more pushers than supported maximum of {}",
132 u32::from(PusherID::MAX) + 1
133 )
134 }
135 }
136 }
137}
138
139impl Error for BoardSyntaxError {
140 fn source(&self) -> Option<&(dyn Error + 'static)> {
141 match &self.0.source {
142 Some(ref_box_dyn) => Some(ref_box_dyn.as_ref()),
143 None => None,
144 }
145 }
146}