shogi_core/game_resolution.rs
1/// How a game is resolved.
2///
3/// [`GameResolution`] and <code>[Option]<[GameResolution]></code> are both 1-byte data types.
4/// Because they are cheap to copy, they implement [`Copy`].
5#[repr(u8)]
6#[derive(Eq, PartialEq, Clone, Copy, Debug)]
7pub enum GameResolution {
8 /// White's king was mated or white resigned.
9 ///
10 /// Discriminant = 1.
11 BlackWins = 1,
12 /// Black's king was mated or black resigned.
13 ///
14 /// Discriminant = 2.
15 WhiteWins = 2,
16 /// This can happen if e.g. `持将棋` (*jishōgi*) happens.
17 ///
18 /// Discriminant = 3.
19 Draw = 3,
20 /// This can happen if e.g. `千日手` (*sennichite*, repetition) happens.
21 ///
22 /// Discriminant = 4.
23 Rematch = 4,
24 /// The game was aborted.
25 ///
26 /// Discriminant = 5.
27 Aborted = 5,
28}
29
30impl GameResolution {
31 /// Converts a [`u8`] to [`GameResolution`] without checking.
32 ///
33 /// # Safety
34 /// `repr` must be a valid representation of [`GameResolution`].
35 /// This condition is equivalent to `1 <= repr && repr <= 5`.
36 #[export_name = "GameResolution_from_u8_unchecked"]
37 pub unsafe extern "C" fn from_u8_unchecked(repr: u8) -> Self {
38 core::mem::transmute(repr)
39 }
40}
41
42impl_ord_for_fieldless_enum!(GameResolution);
43impl_hash_for_fieldless_enum!(GameResolution);
44
45/// <code>[Option]<[GameResolution]></code> with defined representation.
46///
47/// The representation is:
48/// [`None`] => `0`, <code>[Some]\(x\)</code> => `x`.
49/// Therefore, valid representations of this type are precisely `0..=5`.
50///
51/// This type is provided for C interoperability.
52/// cbindgen cannot deduce that <code>[Option]<[GameResolution]></code> can be represented by `uint8_t` in C, so we need to define the bridge type.
53/// Users of this type should convert to/from <code>[Option]<[GameResolution]></code>.
54///
55/// See: <https://github.com/eqrion/cbindgen/issues/326>.
56#[repr(transparent)]
57#[derive(Eq, PartialEq, Clone, Copy, Debug, Default)]
58pub struct OptionGameResolution(u8);
59
60impl From<Option<GameResolution>> for OptionGameResolution {
61 #[inline(always)]
62 fn from(arg: Option<GameResolution>) -> Self {
63 Self(match arg {
64 Some(result) => result as u8,
65 None => 0,
66 })
67 }
68}
69
70impl From<OptionGameResolution> for Option<GameResolution> {
71 #[inline(always)]
72 fn from(arg: OptionGameResolution) -> Self {
73 if arg.0 == 0 {
74 None
75 } else {
76 // Safety: arg is a valid OptionGameResolution, which means 0 <= arg.0 && arg.0 <= 5.
77 // arg.0 == 0 is ruled out.
78 Some(unsafe { GameResolution::from_u8_unchecked(arg.0) })
79 }
80 }
81}
82
83impl_ord_for_single_field!(OptionGameResolution);
84impl_hash_for_single_field!(OptionGameResolution);
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn game_resolution_is_one_byte() {
92 assert_eq!(core::mem::size_of::<GameResolution>(), 1);
93 }
94
95 #[test]
96 fn option_game_resolution_default_is_compatible() {
97 // Option<T>'s default value is [`None`].
98 assert_eq!(OptionGameResolution::default(), None.into());
99 }
100}