1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/// How a game is resolved.
///
/// [`GameResolution`] and <code>[Option]<[GameResolution]></code> are both 1-byte data types.
/// Because they are cheap to copy, they implement [`Copy`].
#[repr(u8)]
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
#[cfg_attr(feature = "ord", derive(PartialOrd, Ord))]
#[cfg_attr(feature = "hash", derive(Hash))]
pub enum GameResolution {
    /// White's king was mated or white resigned.
    ///
    /// Discriminant = 1.
    BlackWins = 1,
    /// Black's king was mated or black resigned.
    ///
    /// Discriminant = 2.
    WhiteWins = 2,
    /// This can happen if e.g. `持将棋` (*jishōgi*) happens.
    ///
    /// Discriminant = 3.
    Draw = 3,
    /// This can happen if e.g. `千日手` (*sennichite*, repetition) happens.
    ///
    /// Discriminant = 4.
    Rematch = 4,
    /// The game was aborted.
    ///
    /// Discriminant = 5.
    Aborted = 5,
}

impl GameResolution {
    /// Converts a [`u8`] to [`GameResolution`] without checking.
    ///
    /// # Safety
    /// `repr` must be a valid representation of [`GameResolution`].
    /// This condition is equivalent to `1 <= repr && repr <= 5`.
    #[export_name = "GameResolution_from_u8_unchecked"]
    pub unsafe extern "C" fn from_u8_unchecked(repr: u8) -> Self {
        core::mem::transmute(repr)
    }
}

/// <code>[Option]<[GameResolution]></code> with defined representation.
///
/// The representation is:
/// [`None`] => `0`, <code>[Some]\(x\)</code> => `x`.
/// Therefore, valid representations of this type are precisely `0..=5`.
///
/// This type is provided for C interoperability.
/// cbindgen cannot deduce that <code>[Option]<[GameResolution]></code> can be represented by `uint8_t` in C, so we need to define the bridge type.
/// Users of this type should convert to/from <code>[Option]<[GameResolution]></code>.
///
/// See: <https://github.com/eqrion/cbindgen/issues/326>.
#[repr(transparent)]
#[derive(Eq, PartialEq, Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "ord", derive(PartialOrd, Ord))]
#[cfg_attr(feature = "hash", derive(Hash))]
pub struct OptionGameResolution(u8);

impl From<Option<GameResolution>> for OptionGameResolution {
    #[inline(always)]
    fn from(arg: Option<GameResolution>) -> Self {
        Self(match arg {
            Some(result) => result as u8,
            None => 0,
        })
    }
}

impl From<OptionGameResolution> for Option<GameResolution> {
    #[inline(always)]
    fn from(arg: OptionGameResolution) -> Self {
        if arg.0 == 0 {
            None
        } else {
            // Safety: arg is a valid OptionGameResolution, which means 0 <= arg.0 && arg.0 <= 5.
            // arg.0 == 0 is ruled out.
            Some(unsafe { GameResolution::from_u8_unchecked(arg.0) })
        }
    }
}

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

    #[test]
    fn game_resolution_is_one_byte() {
        assert_eq!(core::mem::size_of::<GameResolution>(), 1);
    }

    #[test]
    fn option_game_resolution_default_is_compatible() {
        // Option<T>'s default value is [`None`].
        assert_eq!(OptionGameResolution::default(), None.into());
    }
}