backgammon 0.18.0

The Rust Backgammon library
Documentation
/*
 * BSD 2-Clause License
 *
 * Copyright (c) 2020-2026, Carlo Strub <cs@carlostrub.ch>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/// This module contains the error definition for the Backgammon game.
use std::fmt;

/// Stores all possible Backgammon game error types.
#[derive(Debug, PartialEq)]
pub enum Error {
    /// The game has already started.
    GameStarted,
    /// The game has already ended.
    GameEnded,
    /// The cube has been offered, await acceptance or rejection.
    CubeAwaitReaction,
    /// The opponent has offered the doubling cube. You need to decide whether to accept or decline.
    CubeReceived,
    /// Doubling is not permitted.
    CubeNotPermitted,
    /// Invalid cube value.
    CubeValueInvalid,
    /// Invalid player.
    PlayerInvalid,
    /// Field blocked.
    FieldBlocked,
    /// Invalid field.
    FieldInvalid,
    /// Not your turn.
    NotYourTurn,
    /// Match ended.
    MatchEnded,
    /// The match has not ended yet.
    MatchNotEnded,
    /// Invalid move.
    MoveInvalid,
    /// Invalid move, checker on the bar.
    MoveInvalidBar,
    /// Invalid move, not all checkers are in the home field.
    MoveInvalidOff,
    /// Move first.
    MoveFirst,
    /// Roll first.
    RollFirst,
    /// Invalid rule, for example, when asking for a Murphy limit if the Murphy rule is not set.
    RuleInvalid,
    /// Dice invalid, for example, when rolling a dice with a value outside the range [1, 6].
    DiceInvalid,
    /// Dice Consumed.
    DiceConsumed,
    /// Invalid points.
    PointsInvalid,
}

// implement Display trait
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::GameStarted => write!(f, "The game has already started."),
            Error::GameEnded => write!(f, "The game has already ended."),
            Error::CubeAwaitReaction => {
                write!(f, "Awaiting the opponent's response to the doubling cube.")
            }
            Error::CubeReceived => write!(
                f,
                "Your opponent has offered the doubling cube. You must accept or decline."
            ),
            Error::CubeNotPermitted => write!(f, "Doubling is not permitted at this time."),
            Error::CubeValueInvalid => write!(f, "Invalid doubling cube value."),
            Error::PlayerInvalid => write!(f, "Invalid player."),
            Error::FieldBlocked => write!(f, "Invalid move: The target point is blocked."),
            Error::FieldInvalid => write!(f, "Invalid point specified."),
            Error::NotYourTurn => write!(f, "It is not your turn."),
            Error::MatchEnded => write!(f, "The match has already ended."),
            Error::MatchNotEnded => write!(f, "The match has not ended yet."),
            Error::MoveInvalid => write!(f, "Invalid move."),
            Error::MoveInvalidBar => write!(
                f,
                "Invalid move: You must enter your checker(s) from the bar first."
            ),
            Error::MoveInvalidOff => {
                write!(
                    f,
                    "Invalid move: All checkers must be in your home board to bear off."
                )
            }
            Error::MoveFirst => write!(f, "You must make a move first."),
            Error::RollFirst => write!(f, "You must roll the dice first."),
            Error::RuleInvalid => write!(f, "Invalid rule configuration."),
            Error::DiceInvalid => write!(f, "Invalid dice value."),
            Error::DiceConsumed => write!(f, "Dice consumed: No further moves are allowed."),
            Error::PointsInvalid => write!(f, "Invalid points."),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_display() {
        assert_eq!(
            format!("{}", Error::GameStarted),
            "The game has already started."
        );
        assert_eq!(
            format!("{}", Error::GameEnded),
            "The game has already ended."
        );
        assert_eq!(format!("{}", Error::PlayerInvalid), "Invalid player.");
        assert_eq!(
            format!("{}", Error::CubeAwaitReaction),
            "Awaiting the opponent's response to the doubling cube."
        );
        assert_eq!(
            format!("{}", Error::CubeReceived),
            "Your opponent has offered the doubling cube. You must accept or decline."
        );
        assert_eq!(
            format!("{}", Error::CubeValueInvalid),
            "Invalid doubling cube value."
        );
        assert_eq!(
            format!("{}", Error::CubeNotPermitted),
            "Doubling is not permitted at this time."
        );
        assert_eq!(
            format!("{}", Error::FieldBlocked),
            "Invalid move: The target point is blocked."
        );
        assert_eq!(
            format!("{}", Error::FieldInvalid),
            "Invalid point specified."
        );
        assert_eq!(format!("{}", Error::NotYourTurn), "It is not your turn.");
        assert_eq!(
            format!("{}", Error::MatchEnded),
            "The match has already ended."
        );
        assert_eq!(
            format!("{}", Error::MatchNotEnded),
            "The match has not ended yet."
        );
        assert_eq!(format!("{}", Error::MoveInvalid), "Invalid move.");
        assert_eq!(
            format!("{}", Error::MoveInvalidBar),
            "Invalid move: You must enter your checker(s) from the bar first."
        );
        assert_eq!(
            format!("{}", Error::MoveInvalidOff),
            "Invalid move: All checkers must be in your home board to bear off."
        );
        assert_eq!(
            format!("{}", Error::MoveFirst),
            "You must make a move first."
        );
        assert_eq!(
            format!("{}", Error::RollFirst),
            "You must roll the dice first."
        );
        assert_eq!(
            format!("{}", Error::RuleInvalid),
            "Invalid rule configuration."
        );
        assert_eq!(format!("{}", Error::DiceInvalid), "Invalid dice value.");
        assert_eq!(format!("{}", Error::PointsInvalid), "Invalid points.");
        assert_eq!(
            format!("{}", Error::DiceConsumed),
            "Dice consumed: No further moves are allowed."
        );
    }

    #[test]
    fn test_error_source() {
        use std::error::Error as StdError;

        assert!(StdError::source(&Error::GameStarted).is_none());
        assert!(StdError::source(&Error::GameEnded).is_none());
        assert!(StdError::source(&Error::CubeAwaitReaction).is_none());
        assert!(StdError::source(&Error::CubeReceived).is_none());
        assert!(StdError::source(&Error::CubeNotPermitted).is_none());
        assert!(StdError::source(&Error::CubeValueInvalid).is_none());
        assert!(StdError::source(&Error::PlayerInvalid).is_none());
        assert!(StdError::source(&Error::FieldBlocked).is_none());
        assert!(StdError::source(&Error::FieldInvalid).is_none());
        assert!(StdError::source(&Error::NotYourTurn).is_none());
        assert!(StdError::source(&Error::MatchEnded).is_none());
        assert!(StdError::source(&Error::MatchNotEnded).is_none());
        assert!(StdError::source(&Error::MoveInvalid).is_none());
        assert!(StdError::source(&Error::MoveInvalidBar).is_none());
        assert!(StdError::source(&Error::MoveInvalidOff).is_none());
        assert!(StdError::source(&Error::MoveFirst).is_none());
        assert!(StdError::source(&Error::RollFirst).is_none());
        assert!(StdError::source(&Error::RuleInvalid).is_none());
        assert!(StdError::source(&Error::DiceInvalid).is_none());
        assert!(StdError::source(&Error::DiceConsumed).is_none());
        assert!(StdError::source(&Error::PointsInvalid).is_none());
    }
}