Skip to main content

rosu_pp/model/
mode.rs

1use std::{
2    error::Error,
3    fmt::{Display, Formatter, Result as FmtResult},
4};
5
6pub use rosu_map::section::general::GameMode;
7
8use crate::{Difficulty, any::CalculateError};
9
10use super::beatmap::Beatmap;
11
12/// A way to specify a gamemode at compile-time.
13///
14/// Notably, this is implemented for the marker types [`Osu`], [`Taiko`],
15/// [`Catch`], and [`Mania`].
16///
17/// [`Osu`]: crate::osu::Osu
18/// [`Taiko`]: crate::taiko::Taiko
19/// [`Catch`]: crate::catch::Catch
20/// [`Mania`]: crate::mania::Mania
21pub trait IGameMode: Sized {
22    /// The resulting type of a difficulty calculation.
23    type DifficultyAttributes;
24
25    /// The resulting type of a strain calculation.
26    type Strains;
27
28    /// The type of a performance calculator.
29    type Performance<'map>;
30
31    /// The hitresults of a score.
32    type HitResults;
33
34    /// The type of a gradual difficulty calculator.
35    type GradualDifficulty;
36
37    /// The type of a gradual performance calculator.
38    type GradualPerformance;
39
40    /// Perform a difficulty calculation for a [`Beatmap`] and process the
41    /// final skill values.
42    fn difficulty(
43        difficulty: &Difficulty,
44        map: &Beatmap,
45    ) -> Result<Self::DifficultyAttributes, ConvertError>;
46
47    /// Same as [`IGameMode::difficulty`] but verifies that the [`Beatmap`] is
48    /// not too suspicious for further calculation.
49    fn checked_difficulty(
50        difficulty: &Difficulty,
51        map: &Beatmap,
52    ) -> Result<Self::DifficultyAttributes, CalculateError>;
53
54    /// Perform a difficulty calculation for a [`Beatmap`] without processing
55    /// the final skill values.
56    fn strains(difficulty: &Difficulty, map: &Beatmap) -> Result<Self::Strains, ConvertError>;
57
58    /// Create a performance calculator for a [`Beatmap`].
59    fn performance(map: &Beatmap) -> Self::Performance<'_>;
60
61    /// Create a gradual difficulty calculator for a [`Beatmap`].
62    fn gradual_difficulty(
63        difficulty: Difficulty,
64        map: &Beatmap,
65    ) -> Result<Self::GradualDifficulty, ConvertError>;
66
67    /// Create a gradual performance calculator for a [`Beatmap`].
68    fn gradual_performance(
69        difficulty: Difficulty,
70        map: &Beatmap,
71    ) -> Result<Self::GradualPerformance, ConvertError>;
72}
73
74/// Error type when failing to convert a [`Beatmap`] from one [`GameMode`] to
75/// another.
76#[derive(Copy, Clone, Debug)]
77pub enum ConvertError {
78    /// Cannot convert an already converted map
79    AlreadyConverted,
80    /// Cannot convert from [`GameMode`] `from` to `to`
81    Convert { from: GameMode, to: GameMode },
82}
83
84impl Error for ConvertError {
85    fn source(&self) -> Option<&(dyn Error + 'static)> {
86        None
87    }
88}
89
90impl Display for ConvertError {
91    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
92        match self {
93            ConvertError::AlreadyConverted => {
94                f.write_str("Cannot convert an already converted map")
95            }
96            ConvertError::Convert { from, to } => {
97                write!(f, "Cannot convert from {from:?} to {to:?}")
98            }
99        }
100    }
101}