use crate::errors::{BASE_ROCK_PAPER_SCISSORS_ERROR_CODE, ErrorCode};
use core::{
cmp::{Ord, Ordering, PartialOrd},
fmt,
};
use rand::RngExt;
use std::str::FromStr;
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Weapon {
Rock,
Paper,
Scissors,
}
#[repr(C)]
#[derive(
Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Ord, PartialOrd,
)]
pub enum WeaponParseError {
InvalidWeaponError,
}
impl fmt::Display for WeaponParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
WeaponParseError::InvalidWeaponError => f.write_str("Invalid Weapon"),
}
}
}
impl ErrorCode for WeaponParseError {
fn error_code(&self) -> i32 {
BASE_ROCK_PAPER_SCISSORS_ERROR_CODE
+ match *self {
WeaponParseError::InvalidWeaponError => 1,
}
}
}
impl fmt::Display for Weapon {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Weapon::Rock => "Rock",
Weapon::Paper => "Paper",
Weapon::Scissors => "Scissors",
}
)
}
}
impl FromStr for Weapon {
type Err = WeaponParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.chars()
.next()
.map(|c| c.to_ascii_lowercase())
.and_then(|c| match c {
'r' => Some(Weapon::Rock),
'p' => Some(Weapon::Paper),
's' => Some(Weapon::Scissors),
_ => None,
})
.ok_or(WeaponParseError::InvalidWeaponError)
}
}
impl PartialOrd for Weapon {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Weapon {
fn cmp(&self, other: &Self) -> Ordering {
match self {
Weapon::Rock => match other {
Weapon::Rock => Ordering::Equal,
Weapon::Paper => Ordering::Less,
Weapon::Scissors => Ordering::Greater,
},
Weapon::Paper => match other {
Weapon::Rock => Ordering::Greater,
Weapon::Paper => Ordering::Equal,
Weapon::Scissors => Ordering::Less,
},
Weapon::Scissors => match other {
Weapon::Rock => Ordering::Less,
Weapon::Paper => Ordering::Greater,
Weapon::Scissors => Ordering::Equal,
},
}
}
}
impl Weapon {
pub fn rand() -> Self {
"rps"
.chars()
.nth(crate::get_rng().random_range(0..3))
.and_then(|p| Weapon::from_str(&p.to_string()).ok())
.expect("Rand should always return weapon")
}
}