[explorer, moves, core] Improve error handling in explorer

Implement thiserror::Error for a bunch of error types, and remove string errors
from the implementation of the command handler in explorer.

Clean up parsing of basic types all over the place.

Update Cargo files to include thiserror and anyhow.
This commit is contained in:
Eryn Wells 2025-05-19 14:18:31 -07:00
parent 72eeba84ba
commit 9010f1e9c2
12 changed files with 331 additions and 226 deletions

View file

@ -1,7 +1,7 @@
// Eryn Wells <eryn@erynwells.me>
use crate::Color;
use std::{fmt, str::FromStr};
use std::fmt;
use thiserror::Error;
macro_rules! try_from_integer {
@ -10,6 +10,7 @@ macro_rules! try_from_integer {
type Error = ();
fn try_from(value: $int_type) -> Result<Self, Self::Error> {
#[allow(clippy::cast_possible_truncation)]
Self::try_from(value as u8)
}
}
@ -55,6 +56,7 @@ macro_rules! range_bound_struct {
#[allow(dead_code)]
impl $type {
$vis const NUM: usize = $max;
$vis const FIRST: $type = $type(0);
$vis const LAST: $type = $type($max - 1);
}
@ -148,7 +150,7 @@ impl File {
pub const G: File = File(6);
pub const H: File = File(7);
pub const ALL: [File; 8] = [
pub const ALL: [File; File::NUM] = [
File::A,
File::B,
File::C,
@ -173,7 +175,7 @@ impl Rank {
pub const SEVEN: Rank = Rank(6);
pub const EIGHT: Rank = Rank(7);
pub const ALL: [Rank; 8] = [
pub const ALL: [Rank; Self::NUM] = [
Rank::ONE,
Rank::TWO,
Rank::THREE,
@ -344,7 +346,27 @@ pub enum ParseSquareError {
FileError(#[from] ParseFileError),
}
impl FromStr for Square {
impl TryFrom<&str> for Square {
type Error = ParseSquareError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut chars = value.chars();
let file: File = chars
.next()
.and_then(|c| c.try_into().ok())
.ok_or(ParseSquareError::FileError(ParseFileError))?;
let rank: Rank = chars
.next()
.and_then(|c| c.try_into().ok())
.ok_or(ParseSquareError::RankError(ParseRankError))?;
Ok(Square::from_file_rank(file, rank))
}
}
impl std::str::FromStr for Square {
type Err = ParseSquareError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
@ -377,10 +399,13 @@ impl std::str::FromStr for Rank {
.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)
let offset = 'a' as usize - (ch as usize);
if offset >= Rank::ALL.len() {
return Err(ParseRankError);
}
Ok(Rank::ALL[offset])
}
}
@ -397,10 +422,13 @@ impl std::str::FromStr for File {
.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)
let offset = '1' as usize - (ch as usize);
if offset >= File::ALL.len() {
return Err(ParseFileError);
}
Ok(File::ALL[offset])
}
}
@ -489,12 +517,12 @@ mod tests {
#[test]
fn bad_algebraic_input() {
assert!(Square::from_algebraic_str("a0").is_err());
assert!(Square::from_algebraic_str("j3").is_err());
assert!(Square::from_algebraic_str("a11").is_err());
assert!(Square::from_algebraic_str("b-1").is_err());
assert!(Square::from_algebraic_str("a 1").is_err());
assert!(Square::from_algebraic_str("").is_err());
assert!("a0".parse::<Square>().is_err());
assert!("j3".parse::<Square>().is_err());
assert!("a11".parse::<Square>().is_err());
assert!("b-1".parse::<Square>().is_err());
assert!("a 1".parse::<Square>().is_err());
assert!("".parse::<Square>().is_err());
}
#[test]