1use super::*;
2use TimecatError::*;
3
4#[cfg(feature = "pyo3")]
5#[derive(Debug)]
6pub enum Pyo3Error {
7 #[cfg(feature = "pyo3")]
8 Pyo3TypeConversionError { from: String, to: String },
9}
10
11#[cfg(feature = "pyo3")]
12impl From<Pyo3Error> for PyErr {
13 fn from(err: Pyo3Error) -> PyErr {
14 match err {
15 Pyo3Error::Pyo3TypeConversionError { from, to } => {
16 pyo3::exceptions::PyTypeError::new_err(format!(
17 "Failed to convert {from} into {to}"
18 ))
19 }
20 }
21 }
22}
23
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25#[derive(Clone, PartialEq, Eq, Debug, Hash)]
26pub enum TimecatError {
27 UnknownCommand,
28 NoInput,
29 NotImplemented,
30 EngineNotRunning,
31 BadFen {
32 fen: String,
33 },
34 InvalidDepth {
35 depth: Depth,
36 },
37 IllegalMove {
38 valid_or_null_move: ValidOrNullMove,
39 board_fen: String,
40 },
41 ColoredOutputUnchanged {
42 b: bool,
43 },
44 UCIModeUnchanged,
45 ConsoleModeUnchanged,
46 EmptyStack,
47 BestMoveNotFound {
48 fen: String,
49 },
50 NullMoveInCheck {
51 fen: String,
52 },
53 WTimeNotMentioned,
54 BTimeNotMentioned,
55 GameAlreadyOver,
56 UnknownDebugCommand {
57 command: String,
58 },
59 InvalidSpinValue {
60 name: String,
61 value: Spin,
62 min: Spin,
63 max: Spin,
64 },
65 InvalidMoveStructGeneration,
66 InvalidSanOrLanMove {
67 valid_or_null_move: ValidOrNullMove,
68 fen: String,
69 },
70 InvalidSanMoveString {
71 s: String,
72 },
73 InvalidLanMoveString {
74 s: String,
75 },
76 InvalidMoveString {
77 s: String,
78 },
79 InvalidRankString {
80 s: String,
81 },
82 InvalidFileString {
83 s: String,
84 },
85 InvalidSquareString {
86 s: String,
87 },
88 InvalidPieceTypeString {
89 s: String,
90 },
91 InvalidPieceString {
92 s: String,
93 },
94 InvalidUciMoveString {
95 s: String,
96 },
97 InvalidBoardPosition {
98 position: ChessPosition,
99 },
100 InvalidGoCommand {
101 s: String,
102 },
103 IllegalSearchMoves {
104 illegal_moves: Vec<Move>,
105 },
106 FeatureNotEnabled {
107 s: String,
108 },
109 BadNNUEFile,
110 BadPolyglotFile,
111 PolyglotTableParseError,
112 CustomError {
113 err_msg: String,
114 },
115}
116
117impl TimecatError {
118 pub fn get_custom_error<E: Error>(error: E) -> Self {
119 Self::CustomError {
120 err_msg: format!("{error}! Please try again!"),
121 }
122 }
123}
124
125impl fmt::Display for TimecatError {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 match self {
128 UnknownCommand => write!(
129 f,
130 "{}",
131 UnknownCommand.stringify_with_optional_raw_input(None)
132 ),
133 NoInput => write!(f, "No input! Please try again!"),
134 NotImplemented => write!(f, "Sorry, this command is not implemented yet :("),
135 EngineNotRunning => write!(f, "Engine is not running! Please try again!"),
136 BadFen { fen } => write!(f, "Bad FEN string: {fen}! Please try Again!"),
137 InvalidDepth { depth } => write!(f, "Invalid depth {depth}! Please try again!"),
138 IllegalMove {
139 valid_or_null_move,
140 board_fen,
141 } => write!(
142 f,
143 "Illegal move {valid_or_null_move} in position {board_fen}! Please try again!"
144 ),
145 ColoredOutputUnchanged { b } => {
146 write!(f, "Colored output already set to {b}! Please try again!")
147 }
148 UCIModeUnchanged => write!(f, "Already in UCI Mode! Please try again!"),
149 ConsoleModeUnchanged => write!(f, "Already in Console Mode! Please try again!"),
150 EmptyStack => write!(
151 f,
152 "Move Stack is empty, pop not possible! Please try again!"
153 ),
154 BestMoveNotFound { fen } => write!(
155 f,
156 "Best move not found in position {fen}! Please try again!"
157 ),
158 NullMoveInCheck { fen } => write!(
159 f,
160 "Cannot apply null move in position {fen}, as king is in check! Please try again!"
161 ),
162 WTimeNotMentioned => write!(f, "You didn't mention wtime! Please try again!"),
163 BTimeNotMentioned => write!(f, "You didn't mention btime! Please try again!"),
164 GameAlreadyOver => write!(
165 f,
166 "Game is already over! Please start a game from another position!"
167 ),
168 UnknownDebugCommand { command } => write!(
169 f,
170 "Debug command {command} is unknown! The possible commands are on or off! Please try again!"
171 ),
172 InvalidSpinValue {
173 name,
174 value,
175 min,
176 max,
177 } => write!(
178 f,
179 "Cannot set value of {name} to {value}, the value must be from {min} to {max}! Please try again!"
180 ),
181 InvalidMoveStructGeneration => {
182 write!(f, "The from square and to square of a move cannot be same!")
183 }
184 InvalidSanOrLanMove {
185 valid_or_null_move,
186 fen,
187 } => write!(
188 f,
189 "san() and lan() expect move to be legal or null, but got {} in {}",
190 valid_or_null_move, fen
191 ),
192 InvalidSanMoveString { s } => {
193 write!(f, "Got invalid SAN move string {s}! Please try again!")
194 }
195 InvalidLanMoveString { s } => {
196 write!(f, "Got invalid LAN move string {s}! Please try again!")
197 }
198 InvalidMoveString { s } => write!(f, "Got invalid move string {s}! Please try again!"),
199 InvalidRankString { s } => write!(f, "Got invalid rank string {s}! Please try again!"),
200 InvalidFileString { s } => write!(f, "Got invalid file string {s}! Please try again!"),
201 InvalidSquareString { s } => {
202 write!(f, "Got invalid square string {s}! Please try again!")
203 }
204 InvalidPieceTypeString { s } => {
205 write!(f, "Got invalid piece type string {s}! Please try again!")
206 }
207 InvalidPieceString { s } => {
208 write!(f, "Got invalid piece string {s}! Please try again!")
209 }
210 InvalidUciMoveString { s } => {
211 write!(f, "Invalid uci move string {s}! Please try again!")
212 }
213 InvalidBoardPosition { position } => {
214 write!(f, "Invalid position generated:\n\n{position:#?}")
215 }
216 InvalidGoCommand { s } => write!(f, "Got invalid go command: {s:?}! Please try again!"),
217 IllegalSearchMoves { illegal_moves } => write!(
218 f,
219 "Got illegal search moves: {}! Please try again!",
220 illegal_moves.iter().map(ToString::to_string).join(", ")
221 ),
222 FeatureNotEnabled { s } => write!(
223 f,
224 "The feature {s:?} is not enabled. Please recompile the chess engine with this feature enabled!"
225 ),
226 BadNNUEFile => write!(
227 f,
228 "The NNUE file cannot be parsed properly! Try again with a different NNUE file!"
229 ),
230 BadPolyglotFile => write!(
231 f,
232 "The Polyglot file cannot be parsed properly! Try again with a different Polyglot file!"
233 ),
234 PolyglotTableParseError => write!(
235 f,
236 "The Polyglot Table cannot be parsed properly! Try again with a different Polyglot file!"
237 ),
238 CustomError { err_msg } => write!(f, "{err_msg}"),
239 }
240 }
241}
242
243impl Error for TimecatError {}
244
245impl TimecatError {
246 pub fn stringify_with_optional_raw_input(&self, optional_raw_input: Option<&str>) -> String {
247 match self {
248 Self::UnknownCommand => {
249 let command_type = if GLOBAL_TIMECAT_STATE.is_in_console_mode() {
250 "Console"
251 } else {
252 "UCI"
253 };
254 match optional_raw_input {
255 Some(raw_input) => format!(
256 "Unknown {command_type} Command: {:?}\nType help for more information!",
257 raw_input.trim_end_matches('\n')
258 ),
259 None => format!("Unknown {command_type} Command!\nPlease try again!"),
260 }
261 }
262 other_err => other_err.to_string(),
263 }
264 }
265}
266
267impl From<TimecatError> for String {
268 fn from(error: TimecatError) -> Self {
269 error.stringify()
270 }
271}
272
273impl From<&Self> for TimecatError {
274 fn from(error: &Self) -> Self {
275 error.clone()
276 }
277}
278
279impl From<ParseBoolError> for TimecatError {
280 fn from(error: ParseBoolError) -> Self {
281 CustomError {
282 err_msg: format!("Failed to parse bool, {error}! Please try again!"),
283 }
284 }
285}
286
287impl From<ParseIntError> for TimecatError {
288 fn from(error: ParseIntError) -> Self {
289 CustomError {
290 err_msg: format!("Failed to parse integer, {error}! Please try again!"),
291 }
292 }
293}
294
295macro_rules! impl_error_convert {
296 ($class:ty) => {
297 impl From<$class> for TimecatError {
298 fn from(error: $class) -> Self {
299 Self::get_custom_error(error)
300 }
301 }
302 };
303}
304
305impl_error_convert!(std::io::Error);
306impl_error_convert!(std::array::TryFromSliceError);
307
308impl From<String> for TimecatError {
309 fn from(err_msg: String) -> Self {
310 CustomError { err_msg }
311 }
312}
313
314impl From<&str> for TimecatError {
315 fn from(err_msg: &str) -> Self {
316 err_msg.to_string().into()
317 }
318}
319
320#[cfg(feature = "pyo3")]
321impl From<TimecatError> for PyErr {
322 fn from(err: TimecatError) -> PyErr {
323 pyo3::exceptions::PyRuntimeError::new_err(format!("TimecatError occurred: {:?}", err))
324 }
325}