use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum GfError {
InvalidAsk,
InvalidTarget,
OutOfTurn,
NotEnoughPlayers,
TooManyPlayers,
GameAlreadyOver,
EmptyDrawPile,
ParseError(String),
IoError(String),
NoReplayData,
}
impl fmt::Display for GfError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidAsk => f.write_str("asker does not hold any card of the requested rank"),
Self::InvalidTarget => {
f.write_str("invalid target: player targeted themselves or index is out of range")
}
Self::OutOfTurn => f.write_str("action called when it is not this player's turn"),
Self::NotEnoughPlayers => {
f.write_str("game started with fewer players than the variant minimum")
}
Self::TooManyPlayers => {
f.write_str("game started with more players than the variant maximum")
}
Self::GameAlreadyOver => f.write_str("action called after the game has already ended"),
Self::EmptyDrawPile => f.write_str("draw attempted on an empty draw pile"),
Self::ParseError(msg) => write!(f, "parse error: {msg}"),
Self::IoError(msg) => write!(f, "io error: {msg}"),
Self::NoReplayData => {
f.write_str("no replay data: record is missing actions or initial deck state")
}
}
}
}
impl std::error::Error for GfError {}
impl From<serde_json::Error> for GfError {
fn from(err: serde_json::Error) -> Self {
Self::ParseError(err.to_string())
}
}
#[cfg(feature = "history")]
impl From<serde_norway::Error> for GfError {
fn from(err: serde_norway::Error) -> Self {
Self::ParseError(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_error_inequality() {
assert_ne!(
GfError::ParseError("one".into()),
GfError::ParseError("two".into())
);
}
#[test]
fn test_error_source_is_none() {
use std::error::Error;
assert!(GfError::InvalidAsk.source().is_none());
assert!(GfError::ParseError("x".into()).source().is_none());
}
#[test]
fn test_display_messages_are_non_empty() {
let variants = [
GfError::InvalidAsk,
GfError::InvalidTarget,
GfError::OutOfTurn,
GfError::NotEnoughPlayers,
GfError::TooManyPlayers,
GfError::GameAlreadyOver,
GfError::EmptyDrawPile,
GfError::ParseError("bad".into()),
GfError::IoError("disk full".into()),
GfError::NoReplayData,
];
for v in &variants {
assert!(!v.to_string().is_empty(), "{v:?} Display must not be empty");
}
}
#[test]
fn test_io_error_display() {
let err = GfError::IoError("permission denied".to_string());
assert_eq!(err.to_string(), "io error: permission denied");
}
#[test]
fn test_no_replay_data_display() {
let err = GfError::NoReplayData;
assert_eq!(
err.to_string(),
"no replay data: record is missing actions or initial deck state",
);
}
}