Skip to main content

rosu_mods/
lib.rs

1//! A library to provide types for all [osu!] gamemods as defined in the official [mods.json] file.
2//!
3//! Individual gamemod types are generated automatically through the [`generate-mods`] binary.
4//!
5//! ## Types
6//!
7//! In total, there are four different ways of handling mods.
8//!
9//! ### `GameModsLegacy`
10//!
11//! [`GameModsLegacy`] is a lightweight type that is essentially just bitflags for the [legacy mods].
12//!
13//! ```
14//! use rosu_mods::GameModsLegacy;
15//!
16//! let hdnc = GameModsLegacy::Nightcore | GameModsLegacy::Hidden;
17//! assert_eq!(hdnc.to_string(), "HDNC");
18//! assert!(hdnc.contains(GameModsLegacy::DoubleTime));
19//! ```
20//!
21//! ### `GameMods`
22//!
23//! [`GameMods`] is a collection of the [`GameMod`] enum. [`GameMod`] distinguishes between each
24//! mode so if a mod is valid for multiple modes, each of those modes will have a variant for that
25//! mod.
26//!
27//! ```
28//! use rosu_mods::{GameMod, GameMods, generated_mods::AccuracyChallengeMania};
29//!
30//! # let mut mods = GameMods::new();
31//! # mods.insert(GameMod::HardRockTaiko(Default::default()));
32//! # mods.insert(GameMod::AccuracyChallengeTaiko(Default::default()));
33//! # /*
34//! // The `mods!` macro is only available if the `macros` feature is enabled
35//! let mut mods: GameMods = rosu_mods::mods!(Taiko: AC HR);
36//! # */
37#![cfg_attr(
38    feature = "macros",
39    doc = "# assert_eq!(mods, rosu_mods::mods!(Taiko: AC HR));"
40)]
41//!
42//! // In addition to the two taiko mods, let's add a mania mod too
43//! mods.insert(GameMod::AccuracyChallengeMania(AccuracyChallengeMania {
44//!     restart: Some(true),
45//!     ..Default::default()
46//! }));
47//!
48//! assert_eq!(mods.to_string(), "ACHRAC");
49//!
50//! let mut iter = mods.into_iter();
51//! assert_eq!(iter.next(), Some(GameMod::AccuracyChallengeTaiko(Default::default())));
52//! assert_eq!(iter.next(), Some(GameMod::HardRockTaiko(Default::default())));
53//! assert_eq!(iter.next(), Some(GameMod::AccuracyChallengeMania(AccuracyChallengeMania {
54//!     restart: Some(true),
55//!     minimum_accuracy: None,
56//!     accuracy_judge_mode: None,
57//! })));
58//! assert_eq!(iter.next(), None);
59//! ```
60//!
61//! ### `GameModsIntermode`
62//!
63//! [`GameModsIntermode`] is a collection of the [`GameModIntermode`] enum. Unlike [`GameMod`],
64//! this enum does not distinguish between modes. As such, variants do not carry further data
65//! because a mod may have different settings depending on the mode.
66//!
67//! Since [`GameModsIntermode`] does not carry additional data and also consists of fewer variants,
68//! it is generally easier to deal with than [`GameMods`].
69//!
70//! ```
71//! use rosu_mods::{GameModIntermode, GameModsIntermode};
72//!
73//! # let mut mods = GameModsIntermode::new();
74//! # mods.insert(GameModIntermode::Wiggle);
75//! # mods.insert(GameModIntermode::FadeIn);
76//! # /*
77//! // The `mods!` macro is only available if the `macros` feature is enabled
78//! let mut mods: GameModsIntermode = rosu_mods::mods!(WG FI);
79//! # */
80#![cfg_attr(
81    feature = "macros",
82    doc = "# assert_eq!(mods, rosu_mods::mods!(WG FI));"
83)]
84//!
85//! // Let's add some more mods
86//! mods.extend([GameModIntermode::Easy, GameModIntermode::HardRock]);
87//!
88//! assert_eq!(mods.to_string(), "EZFIHRWG");
89//!
90//! let mut iter = mods.into_iter();
91//! assert_eq!(iter.next(), Some(GameModIntermode::Easy));
92//! assert_eq!(iter.next(), Some(GameModIntermode::FadeIn));
93//! assert_eq!(iter.next(), Some(GameModIntermode::HardRock));
94//! assert_eq!(iter.next(), Some(GameModIntermode::Wiggle));
95//! assert_eq!(iter.next(), None);
96//! ```
97//!
98//! ### `GameModSimple`
99//!
100//! Unlike the other three, [`GameModSimple`] is not a collection but just a
101//! single mod. Instead of providing types for each mod, it keeps things simple
102//! and stores all settings into a plain `HashMap`.
103//!
104//! ```
105//! use rosu_mods::{GameMod, GameModSimple, SettingSimple, generated_mods::AccuracyChallengeMania};
106//!
107//! let gamemod = GameMod::AccuracyChallengeMania(AccuracyChallengeMania {
108//!     restart: Some(true),
109//!     ..Default::default()
110//! });
111//! let simple = gamemod.into_simple();
112//! assert_eq!(simple.settings.get("restart"), Some(&SettingSimple::Bool(true)));
113//! ```
114//!
115//! ## Features
116//!
117//! | Flag      | Description                                                                                      | Dependencies
118//! | --------- | ------------------------------------------------------------------------------------------------ | ------------
119//! | `default` | No features enabled                                                                              |
120//! | `macros`  | Enables the `mods!` macro                                                                        | [`pastey`]
121//! | `serde`   | Implements `serde::{Deserialize, Serialize}` for all types and enables the `serde` module        | [`serde`]
122//! | `rkyv`    | Implements `rkyv::{Archive, Serialize, Deserialize}` for all types and enables the `rkyv` module | [`rkyv`]
123//!
124//! [osu!]: https://osu.ppy.sh/home
125//! [mods.json]: https://github.com/ppy/osu-web/blob/master/database/mods.json
126//! [`generate-mods`]: https://github.com/MaxOhn/rosu-mods/tree/main/generate-mods
127//! [legacy mods]: https://github.com/ppy/osu-api/wiki#reference
128//! [`pastey`]: https://docs.rs/pastey
129//! [`serde`]: https://docs.rs/serde
130//! [`rkyv`]: https://docs.rs/rkyv
131//! [`GameModsLegacy`]: crate::legacy::GameModsLegacy
132//! [`GameMods`]: crate::mods::GameMods
133//! [`GameMod`]: crate::generated_mods::gamemod::GameMod
134//! [`GameModsIntermode`]: crate::intermode::GameModsIntermode
135//! [`GameModIntermode`]: crate::generated_mods::intermode::GameModIntermode
136//! [`GameModSimple`]: crate::simple::GameModSimple
137
138#![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg))]
139#![deny(rustdoc::broken_intra_doc_links, rustdoc::missing_crate_level_docs)]
140#![warn(clippy::missing_const_for_fn, clippy::pedantic)]
141#![allow(
142    clippy::missing_errors_doc,
143    clippy::module_name_repetitions,
144    clippy::must_use_candidate,
145    clippy::struct_excessive_bools,
146    clippy::match_same_arms,
147    clippy::cast_possible_truncation,
148    clippy::cast_precision_loss,
149    clippy::cast_sign_loss,
150    clippy::explicit_iter_loop,
151    clippy::similar_names,
152    clippy::cast_possible_wrap,
153    clippy::default_trait_access
154)]
155
156#[cfg(feature = "macros")]
157extern crate pastey;
158
159#[macro_use]
160#[doc(hidden)]
161pub mod macros;
162
163mod acronym;
164mod intermode;
165mod kind;
166mod legacy;
167mod mod_manual;
168mod mode;
169mod mods;
170mod order;
171mod simple;
172mod util;
173
174/// Error types
175pub mod error;
176
177pub mod generated_mods;
178
179/// Types to calculate intersecting mods.
180pub mod intersection;
181
182/// Iterator types for mods.
183pub mod iter;
184
185#[cfg(feature = "rkyv")]
186#[doc(inline)]
187pub use generated_mods::rkyv;
188
189/// Types for (de)serialization through `serde`.
190#[cfg_attr(all(docsrs, not(doctest)), doc(cfg(feature = "serde")))]
191pub mod serde;
192
193pub use self::{mode::GameMode, mods::GameMods};
194
195#[doc(inline)]
196pub use self::{
197    acronym::Acronym,
198    generated_mods::{GameMod, GameModIntermode},
199    intermode::GameModsIntermode,
200    kind::GameModKind,
201    legacy::GameModsLegacy,
202    simple::{GameModSimple, SettingSimple},
203};
204
205#[cfg(feature = "serde")]
206#[cfg_attr(all(docsrs, not(doctest)), doc(cfg(feature = "serde")))]
207pub use self::simple::GameModSimpleConversionError;