1use crate::errors::{BASE_ROCK_PAPER_SCISSORS_ERROR_CODE, ErrorCode};
2use core::{
3 cmp::{Ord, Ordering, PartialOrd},
4 fmt,
5};
6use rand::RngExt;
7use std::str::FromStr;
8
9#[repr(C)]
11#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
12pub enum Weapon {
13 Rock,
15 Paper,
17 Scissors,
19}
20
21#[repr(C)]
23#[derive(
24 Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Ord, PartialOrd,
25)]
26
27pub enum WeaponParseError {
28 InvalidWeaponError,
30}
31
32impl fmt::Display for WeaponParseError {
33 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 match *self {
35 WeaponParseError::InvalidWeaponError => f.write_str("Invalid Weapon"),
36 }
37 }
38}
39
40impl ErrorCode for WeaponParseError {
41 fn error_code(&self) -> i32 {
42 BASE_ROCK_PAPER_SCISSORS_ERROR_CODE
43 + match *self {
44 WeaponParseError::InvalidWeaponError => 1,
45 }
46 }
47}
48
49impl fmt::Display for Weapon {
50 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51 write!(
52 f,
53 "{}",
54 match self {
55 Weapon::Rock => "Rock",
56 Weapon::Paper => "Paper",
57 Weapon::Scissors => "Scissors",
58 }
59 )
60 }
61}
62
63impl FromStr for Weapon {
64 type Err = WeaponParseError;
65 fn from_str(s: &str) -> Result<Self, Self::Err> {
66 s.chars()
67 .next()
68 .map(|c| c.to_ascii_lowercase())
69 .and_then(|c| match c {
70 'r' => Some(Weapon::Rock),
71 'p' => Some(Weapon::Paper),
72 's' => Some(Weapon::Scissors),
73 _ => None,
74 })
75 .ok_or(WeaponParseError::InvalidWeaponError)
76 }
77}
78
79impl PartialOrd for Weapon {
80 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
81 Some(self.cmp(other))
82 }
83}
84
85impl Ord for Weapon {
86 fn cmp(&self, other: &Self) -> Ordering {
87 match self {
88 Weapon::Rock => match other {
90 Weapon::Rock => Ordering::Equal,
92 Weapon::Paper => Ordering::Less,
94 Weapon::Scissors => Ordering::Greater,
96 },
97 Weapon::Paper => match other {
99 Weapon::Rock => Ordering::Greater,
101 Weapon::Paper => Ordering::Equal,
103 Weapon::Scissors => Ordering::Less,
105 },
106 Weapon::Scissors => match other {
108 Weapon::Rock => Ordering::Less,
110 Weapon::Paper => Ordering::Greater,
112 Weapon::Scissors => Ordering::Equal,
114 },
115 }
116 }
117}
118
119impl Weapon {
120 pub fn rand() -> Self {
122 "rps"
123 .chars()
124 .nth(crate::get_rng().random_range(0..3))
125 .and_then(|p| Weapon::from_str(&p.to_string()).ok())
126 .expect("Rand should always return weapon")
127 }
128}