From b229049e2789191baeef942a467df9fe480c56b1 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Mon, 19 May 2025 08:34:32 -0700 Subject: [PATCH] [board, core] Update error types to use thiserror::Error --- board/Cargo.toml | 1 + board/src/fen.rs | 30 +++++++++++++++++---- board/src/lib.rs | 1 + board/src/piece_sets.rs | 6 +++-- core/Cargo.toml | 1 + core/src/coordinates.rs | 59 +++++++++++++++++++++++++++++++++++------ 6 files changed, 83 insertions(+), 15 deletions(-) diff --git a/board/Cargo.toml b/board/Cargo.toml index 54a0dd4..098d764 100644 --- a/board/Cargo.toml +++ b/board/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] chessfriend_bitboard = { path = "../bitboard" } chessfriend_core = { path = "../core" } +thiserror = "2" diff --git a/board/src/fen.rs b/board/src/fen.rs index 6fe1c08..4221f1f 100644 --- a/board/src/fen.rs +++ b/board/src/fen.rs @@ -5,6 +5,7 @@ use chessfriend_core::{ coordinates::ParseSquareError, piece, Color, File, Piece, PlacedPiece, Rank, Square, }; use std::fmt::Write; +use thiserror::Error; #[macro_export] macro_rules! fen { @@ -13,18 +14,24 @@ macro_rules! fen { }; } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Error, Eq, PartialEq)] pub enum ToFenStrError { - FmtError(std::fmt::Error), + #[error("{0}")] + FmtError(#[from] std::fmt::Error), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Error, Eq, PartialEq)] pub enum FromFenStrError { + #[error("missing {0} field")] MissingField(Field), + #[error("missing piece placement")] MissingPlacement, + #[error("invalid value")] InvalidValue, - ParseIntError(std::num::ParseIntError), - ParseSquareError(ParseSquareError), + #[error("{0}")] + ParseIntError(#[from] std::num::ParseIntError), + #[error("{0}")] + ParseSquareError(#[from] ParseSquareError), } #[derive(Clone, Debug, Eq, PartialEq)] @@ -37,6 +44,19 @@ pub enum Field { FullMoveCounter, } +impl std::fmt::Display for Field { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Field::Placements => write!(f, "Placements"), + Field::PlayerToMove => write!(f, "Player To Move"), + Field::CastlingRights => write!(f, "Castling Rights"), + Field::EnPassantSquare => write!(f, "En Passant Square"), + Field::HalfMoveClock => write!(f, "Half Move Clock"), + Field::FullMoveCounter => write!(f, "Full move Counter"), + } + } +} + pub trait ToFenStr { type Error; diff --git a/board/src/lib.rs b/board/src/lib.rs index f6c8adb..da05df2 100644 --- a/board/src/lib.rs +++ b/board/src/lib.rs @@ -10,6 +10,7 @@ mod board; mod piece_sets; pub use board::Board; +pub use piece_sets::{PlacePieceError, PlacePieceStrategy}; use castle::Castle; use en_passant::EnPassant; diff --git a/board/src/piece_sets.rs b/board/src/piece_sets.rs index ff66b98..02a7152 100644 --- a/board/src/piece_sets.rs +++ b/board/src/piece_sets.rs @@ -6,6 +6,7 @@ use self::mailbox::Mailbox; use chessfriend_bitboard::{BitBoard, IterationDirection}; use chessfriend_core::{Color, Piece, Shape, Square}; use std::ops::BitOr; +use thiserror::Error; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub enum PlacePieceStrategy { @@ -14,9 +15,10 @@ pub enum PlacePieceStrategy { PreserveExisting, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Error, Eq, PartialEq)] pub enum PlacePieceError { - ExisitingPiece(PlacedPiece), + #[error("cannot place piece on {square} with existing {piece}")] + ExisitingPiece { piece: Piece, square: Square }, } /// The internal data structure of a [Board] that efficiently manages the diff --git a/core/Cargo.toml b/core/Cargo.toml index 9b33128..0905978 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +thiserror = "2" diff --git a/core/src/coordinates.rs b/core/src/coordinates.rs index 9bdfc23..89dfde9 100644 --- a/core/src/coordinates.rs +++ b/core/src/coordinates.rs @@ -2,6 +2,7 @@ use crate::Color; use std::{fmt, str::FromStr}; +use thiserror::Error; macro_rules! try_from_integer { ($type:ident, $int_type:ident) => { @@ -334,8 +335,14 @@ impl Square { } } -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct ParseSquareError; +#[derive(Clone, Debug, Error, Eq, PartialEq)] +pub enum ParseSquareError { + #[error("{0}")] + RankError(#[from] ParseRankError), + + #[error("{0}")] + FileError(#[from] ParseFileError), +} impl FromStr for Square { type Err = ParseSquareError; @@ -346,21 +353,57 @@ impl FromStr for Square { let file: File = chars .next() .and_then(|c| c.try_into().ok()) - .ok_or(ParseSquareError)?; + .ok_or(ParseSquareError::FileError(ParseFileError))?; let rank: Rank = chars .next() .and_then(|c| c.try_into().ok()) - .ok_or(ParseSquareError)?; - - if chars.next().is_some() { - return Err(ParseSquareError); - } + .ok_or(ParseSquareError::RankError(ParseRankError))?; Ok(Square::from_file_rank(file, rank)) } } +#[derive(Clone, Debug, Error, Eq, PartialEq)] +#[error("invalid rank")] +pub struct ParseRankError; + +impl std::str::FromStr for Rank { + type Err = ParseRankError; + + fn from_str(s: &str) -> Result { + let ch = s + .chars() + .nth(0) + .ok_or(ParseRankError) + .map(|ch| ch.to_ascii_lowercase())?; + let offset = 'a' as usize - (ch as usize); + let rank = Rank::ALL[offset]; + + Ok(rank) + } +} + +#[derive(Clone, Debug, Error, Eq, PartialEq)] +#[error("invalid file")] +pub struct ParseFileError; + +impl std::str::FromStr for File { + type Err = ParseFileError; + + fn from_str(s: &str) -> Result { + let ch = s + .chars() + .nth(0) + .ok_or(ParseFileError) + .map(|ch| ch.to_ascii_lowercase())?; + let offset = '1' as usize - (ch as usize); + let file = File::ALL[offset]; + + Ok(file) + } +} + impl fmt::Display for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", Into::::into(*self))