rosu_mods/
mode.rs

1use std::fmt;
2
3/// Available game modes
4#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
5#[cfg_attr(
6    feature = "rkyv",
7    repr(u8),
8    derive(rkyv::Portable, rkyv::bytecheck::CheckBytes),
9    bytecheck(crate = rkyv::bytecheck),
10)]
11pub enum GameMode {
12    /// osu!standard
13    #[default]
14    Osu = 0,
15    /// osu!taiko
16    Taiko = 1,
17    /// osu!catch
18    Catch = 2,
19    /// osu!mania
20    Mania = 3,
21}
22
23impl GameMode {
24    /// Returns `osu`, `taiko`, `fruits`, or `mania`.
25    pub const fn as_str(self) -> &'static str {
26        match self {
27            Self::Osu => "osu",
28            Self::Taiko => "taiko",
29            Self::Catch => "fruits",
30            Self::Mania => "mania",
31        }
32    }
33}
34
35impl From<u8> for GameMode {
36    fn from(mode: u8) -> Self {
37        match mode {
38            0 => GameMode::Osu,
39            1 => GameMode::Taiko,
40            2 => GameMode::Catch,
41            3 => GameMode::Mania,
42            _ => GameMode::Osu,
43        }
44    }
45}
46
47impl fmt::Display for GameMode {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.write_str(self.as_str())
50    }
51}
52
53#[cfg(feature = "serde")]
54#[cfg_attr(all(docsrs, not(doctest)), doc(cfg(feature = "serde")))]
55const _: () = {
56    use serde::{
57        de::{Deserialize, Deserializer, Error, Unexpected, Visitor},
58        ser::{Serialize, Serializer},
59    };
60
61    struct ModeVisitor;
62
63    impl Visitor<'_> for ModeVisitor {
64        type Value = GameMode;
65
66        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67            f.write_str("a gamemode")
68        }
69
70        fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
71            let mode = match v {
72                "0" | "osu" | "osu!" => GameMode::Osu,
73                "1" | "taiko" | "tko" => GameMode::Taiko,
74                "2" | "catch" | "ctb" | "fruits" => GameMode::Catch,
75                "3" | "mania" | "mna" => GameMode::Mania,
76                _ => return Err(Error::invalid_value(Unexpected::Str(v), &Self)),
77            };
78
79            Ok(mode)
80        }
81
82        fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
83            match v {
84                0 => Ok(GameMode::Osu),
85                1 => Ok(GameMode::Taiko),
86                2 => Ok(GameMode::Catch),
87                3 => Ok(GameMode::Mania),
88                _ => Err(Error::invalid_value(Unexpected::Unsigned(v), &Self)),
89            }
90        }
91    }
92
93    impl<'de> Deserialize<'de> for GameMode {
94        fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
95            d.deserialize_any(ModeVisitor)
96        }
97    }
98
99    impl Serialize for GameMode {
100        fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
101            s.serialize_u8(*self as u8)
102        }
103    }
104};
105
106#[cfg(feature = "rkyv")]
107#[cfg_attr(all(docsrs, not(doctest)), doc(cfg(feature = "rkyv")))]
108const _: () = {
109    use rkyv::{rancor::Fallible, traits::NoUndef, Archive, Deserialize, Place, Serialize};
110
111    unsafe impl NoUndef for GameMode {}
112
113    impl Archive for GameMode {
114        type Archived = Self;
115        type Resolver = ();
116
117        fn resolve(&self, (): Self::Resolver, out: Place<Self::Archived>) {
118            out.write(*self);
119        }
120    }
121
122    impl<S: Fallible + ?Sized> Serialize<S> for GameMode {
123        fn serialize(&self, _: &mut S) -> Result<(), S::Error> {
124            Ok(())
125        }
126    }
127
128    impl<D: Fallible + ?Sized> Deserialize<Self, D> for GameMode {
129        fn deserialize(&self, _: &mut D) -> Result<Self, D::Error> {
130            Ok(*self)
131        }
132    }
133};