1use std::{
2 cmp::Ordering,
3 collections::BTreeSet,
4 convert::Infallible,
5 fmt::{Debug, Display, Formatter, Result as FmtResult},
6 ops::{BitOr, BitOrAssign, Sub, SubAssign},
7 str::FromStr,
8};
9
10use crate::{
11 generated_mods::{DoubleTimeOsu, NightcoreOsu, PerfectOsu, SuddenDeathOsu},
12 util, GameModsLegacy,
13};
14
15use crate::GameMode;
16
17use super::{
18 intersection::{GameModsIntermodeIntersection, IntersectionInner},
19 iter::{GameModsIntermodeIter, IntoGameModsIntermodeIter},
20 Acronym, GameMod, GameModIntermode, GameMods,
21};
22
23#[derive(Clone, Default, PartialEq, Eq, Hash)]
25pub struct GameModsIntermode {
26 inner: BTreeSet<GameModIntermode>,
27}
28
29impl GameModsIntermode {
30 pub const fn new() -> Self {
32 Self {
33 inner: BTreeSet::new(),
34 }
35 }
36
37 pub fn bits(&self) -> u32 {
50 self.inner
51 .iter()
52 .copied()
53 .filter_map(GameModIntermode::bits)
54 .fold(0, u32::bitor)
55 }
56
57 pub fn checked_bits(&self) -> Option<u32> {
73 self.inner
74 .iter()
75 .copied()
76 .map(GameModIntermode::bits)
77 .try_fold(0, |bits, next| Some(next? | bits))
78 }
79
80 pub fn is_empty(&self) -> bool {
93 self.inner.is_empty()
94 }
95
96 pub fn len(&self) -> usize {
110 self.inner.len()
111 }
112
113 pub fn insert(&mut self, gamemod: GameModIntermode) {
129 self.inner.insert(gamemod);
130 }
131
132 pub fn contains<M>(&self, gamemod: M) -> bool
143 where
144 GameModIntermode: From<M>,
145 {
146 self.inner.contains(&GameModIntermode::from(gamemod))
147 }
148
149 pub fn contains_acronym(&self, acronym: Acronym) -> bool {
160 self.inner
161 .iter()
162 .any(|gamemod| gamemod.acronym() == acronym)
163 }
164
165 pub fn remove<M>(&mut self, gamemod: M) -> bool
178 where
179 GameModIntermode: From<M>,
180 {
181 self.inner.remove(&GameModIntermode::from(gamemod))
182 }
183
184 pub fn remove_all<I, M>(&mut self, mods: I)
199 where
200 I: IntoIterator<Item = M>,
201 GameModIntermode: From<M>,
202 {
203 for gamemod in mods {
204 self.remove(gamemod);
205 }
206 }
207
208 pub fn from_bits(mut bits: u32) -> Self {
218 struct BitIterator(u32);
219
220 impl Iterator for BitIterator {
221 type Item = bool;
222
223 fn next(&mut self) -> Option<Self::Item> {
224 if self.0 == 0 {
225 None
226 } else {
227 let bit = self.0 & 0b1;
228 self.0 >>= 1;
229
230 Some(bit == 1)
231 }
232 }
233
234 fn size_hint(&self) -> (usize, Option<usize>) {
235 (usize::from(self.0 > 0), None)
236 }
237 }
238
239 bits &= if (bits & NightcoreOsu::bits()) == NightcoreOsu::bits() {
241 !DoubleTimeOsu::bits()
242 } else {
243 !(1 << 9)
244 };
245
246 bits &= if (bits & PerfectOsu::bits()) == PerfectOsu::bits() {
247 !SuddenDeathOsu::bits()
248 } else {
249 !(1 << 14)
250 };
251
252 #[allow(clippy::items_after_statements)]
253 const BITFLAG_MODS: [GameModIntermode; 31] = [
254 GameModIntermode::NoFail,
255 GameModIntermode::Easy,
256 GameModIntermode::TouchDevice,
257 GameModIntermode::Hidden,
258 GameModIntermode::HardRock,
259 GameModIntermode::SuddenDeath,
260 GameModIntermode::DoubleTime,
261 GameModIntermode::Relax,
262 GameModIntermode::HalfTime,
263 GameModIntermode::Nightcore,
264 GameModIntermode::Flashlight,
265 GameModIntermode::Autoplay,
266 GameModIntermode::SpunOut,
267 GameModIntermode::Autopilot,
268 GameModIntermode::Perfect,
269 GameModIntermode::FourKeys,
270 GameModIntermode::FiveKeys,
271 GameModIntermode::SixKeys,
272 GameModIntermode::SevenKeys,
273 GameModIntermode::EightKeys,
274 GameModIntermode::FadeIn,
275 GameModIntermode::Random,
276 GameModIntermode::Cinema,
277 GameModIntermode::TargetPractice,
278 GameModIntermode::NineKeys,
279 GameModIntermode::DualStages,
280 GameModIntermode::OneKey,
281 GameModIntermode::ThreeKeys,
282 GameModIntermode::TwoKeys,
283 GameModIntermode::ScoreV2,
284 GameModIntermode::Mirror,
285 ];
286
287 let inner = BitIterator(bits)
288 .zip(BITFLAG_MODS)
289 .filter_map(|(is_set, gamemod)| is_set.then_some(gamemod))
290 .collect();
291
292 Self { inner }
293 }
294
295 pub fn try_from_acronyms(s: &str) -> Option<Self> {
309 let uppercased = util::to_uppercase(s);
310
311 if uppercased == "NM" {
312 return Some(Self::new());
313 }
314
315 if s.len() == 1 {
317 return None;
318 }
319
320 let mut remaining = uppercased.as_ref();
321 let mut mods = BTreeSet::new();
322
323 while !remaining.is_empty() {
324 let (candidate, rest) = util::split_prefix::<2>(remaining);
326
327 let acronym = unsafe { Acronym::from_str_unchecked(candidate) };
329 let gamemod = GameModIntermode::from_acronym(acronym);
330
331 if !matches!(gamemod, GameModIntermode::Unknown(_)) && rest.len() != 1 {
332 mods.insert(gamemod);
333 remaining = rest;
334
335 continue;
336 }
337
338 let (candidate, rest) = util::split_prefix::<3>(remaining);
340
341 let acronym = unsafe { Acronym::from_str_unchecked(candidate) };
343 let gamemod = GameModIntermode::from_acronym(acronym);
344
345 if matches!(gamemod, GameModIntermode::Unknown(_)) {
346 return None;
347 }
348
349 mods.insert(gamemod);
350 remaining = rest;
351 }
352
353 Some(Self { inner: mods })
354 }
355
356 pub fn from_acronyms(s: &str) -> Self {
372 let uppercased = util::to_uppercase(s);
373
374 if uppercased == "NM" {
375 return Self::new();
376 }
377
378 let mut mods = BTreeSet::new();
379
380 let mut remaining = if s.len() == 1 {
382 mods.insert(GameModIntermode::Unknown(Default::default()));
383
384 ""
385 } else {
386 uppercased.as_ref()
387 };
388
389 while !remaining.is_empty() {
390 let (candidate, rest) = util::split_prefix::<2>(remaining);
392
393 let acronym = unsafe { Acronym::from_str_unchecked(candidate) };
395 let gamemod = GameModIntermode::from_acronym(acronym);
396
397 if !matches!(gamemod, GameModIntermode::Unknown(_)) && rest.len() != 1 {
398 mods.insert(gamemod);
399 remaining = rest;
400
401 continue;
402 }
403
404 let (candidate, three_letter_rest) = util::split_prefix::<3>(remaining);
406
407 let acronym = unsafe { Acronym::from_str_unchecked(candidate) };
409 let three_letter_gamemod = GameModIntermode::from_acronym(acronym);
410
411 if !matches!(three_letter_gamemod, GameModIntermode::Unknown(_))
412 || three_letter_rest.is_empty()
413 {
414 mods.insert(three_letter_gamemod);
415 remaining = three_letter_rest;
416 } else {
417 mods.insert(gamemod);
418 remaining = rest;
419 }
420 }
421
422 Self { inner: mods }
423 }
424
425 pub fn intersection<'m>(
439 &'m self,
440 other: &'m GameModsIntermode,
441 ) -> GameModsIntermodeIntersection<'m> {
442 let (self_min, self_max) =
443 if let (Some(self_min), Some(self_max)) = (self.inner.first(), self.inner.last()) {
444 (*self_min, *self_max)
445 } else {
446 return GameModsIntermodeIntersection {
447 inner: IntersectionInner::Answer(None),
448 };
449 };
450
451 let (other_min, other_max) =
452 if let (Some(other_min), Some(other_max)) = (other.inner.first(), other.inner.last()) {
453 (*other_min, *other_max)
454 } else {
455 return GameModsIntermodeIntersection {
456 inner: IntersectionInner::Answer(None),
457 };
458 };
459
460 GameModsIntermodeIntersection {
461 inner: match (self_min.cmp(&other_max), self_max.cmp(&other_min)) {
462 (Ordering::Greater, _) | (_, Ordering::Less) => IntersectionInner::Answer(None),
463 (Ordering::Equal, _) => IntersectionInner::Answer(Some(self_min)),
464 (_, Ordering::Equal) => IntersectionInner::Answer(Some(self_max)),
465 _ => IntersectionInner::new_stitch(self.inner.iter(), other.inner.iter()),
466 },
467 }
468 }
469
470 pub fn intersects(&self, other: &Self) -> bool {
481 self.intersection(other).next().is_some()
482 }
483
484 pub fn legacy_clock_rate(&self) -> f64 {
501 self.iter()
502 .find_map(|gamemod| match gamemod {
503 GameModIntermode::DoubleTime | GameModIntermode::Nightcore => Some(1.5),
504 GameModIntermode::HalfTime | GameModIntermode::Daycore => Some(0.75),
505 _ => None,
506 })
507 .unwrap_or(1.0)
508 }
509
510 pub fn iter(&self) -> GameModsIntermodeIter<'_> {
514 GameModsIntermodeIter::new(self.inner.iter().copied())
515 }
516
517 pub fn try_with_mode(&self, mode: GameMode) -> Option<GameMods> {
532 self.inner
533 .iter()
534 .map(|gamemod| GameMod::new(gamemod.acronym().as_str(), mode))
535 .try_fold(GameMods::default(), |mut mods, next| {
536 if matches!(
537 next,
538 GameMod::UnknownOsu(_)
539 | GameMod::UnknownTaiko(_)
540 | GameMod::UnknownCatch(_)
541 | GameMod::UnknownMania(_)
542 ) {
543 None
544 } else {
545 mods.insert(next);
546
547 Some(mods)
548 }
549 })
550 }
551
552 pub fn with_mode(&self, mode: GameMode) -> GameMods {
568 self.inner
569 .iter()
570 .map(|gamemod| GameMod::new(gamemod.acronym().as_str(), mode))
571 .collect()
572 }
573
574 pub fn as_legacy(&self) -> GameModsLegacy {
576 GameModsLegacy::from_bits(self.bits())
577 }
578
579 pub fn try_as_legacy(&self) -> Option<GameModsLegacy> {
584 self.checked_bits().map(GameModsLegacy::from_bits)
585 }
586}
587
588impl Debug for GameModsIntermode {
589 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
590 f.debug_list().entries(self.inner.iter()).finish()
591 }
592}
593
594impl Display for GameModsIntermode {
595 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
596 if self.is_empty() {
597 f.write_str("NM")
598 } else {
599 for gamemod in self.iter() {
600 f.write_str(gamemod.acronym().as_str())?;
601 }
602
603 Ok(())
604 }
605 }
606}
607
608impl IntoIterator for GameModsIntermode {
609 type Item = GameModIntermode;
610 type IntoIter = IntoGameModsIntermodeIter;
611
612 fn into_iter(self) -> Self::IntoIter {
616 IntoGameModsIntermodeIter::new(self.inner.into_iter())
617 }
618}
619
620impl<'a> IntoIterator for &'a GameModsIntermode {
621 type Item = <GameModsIntermodeIter<'a> as Iterator>::Item;
622 type IntoIter = GameModsIntermodeIter<'a>;
623
624 fn into_iter(self) -> Self::IntoIter {
625 self.iter()
626 }
627}
628
629impl<M> FromIterator<M> for GameModsIntermode
630where
631 GameModIntermode: From<M>,
632{
633 fn from_iter<T: IntoIterator<Item = M>>(iter: T) -> Self {
634 Self {
635 inner: iter.into_iter().map(GameModIntermode::from).collect(),
636 }
637 }
638}
639
640impl<M> Extend<M> for GameModsIntermode
641where
642 GameModIntermode: From<M>,
643{
644 fn extend<T: IntoIterator<Item = M>>(&mut self, iter: T) {
645 self.inner
646 .extend(iter.into_iter().map(GameModIntermode::from));
647 }
648}
649
650impl BitOr<GameModIntermode> for GameModsIntermode {
651 type Output = Self;
652
653 fn bitor(mut self, rhs: GameModIntermode) -> Self::Output {
655 self |= rhs;
656
657 self
658 }
659}
660
661impl BitOrAssign<GameModIntermode> for GameModsIntermode {
662 fn bitor_assign(&mut self, rhs: GameModIntermode) {
664 self.insert(rhs);
665 }
666}
667
668impl Sub<GameModIntermode> for GameModsIntermode {
669 type Output = Self;
670
671 fn sub(mut self, rhs: GameModIntermode) -> Self::Output {
673 self -= rhs;
674
675 self
676 }
677}
678
679impl SubAssign<GameModIntermode> for GameModsIntermode {
680 fn sub_assign(&mut self, rhs: GameModIntermode) {
682 self.remove(rhs);
683 }
684}
685
686impl From<GameMods> for GameModsIntermode {
687 fn from(mods: GameMods) -> Self {
688 Self {
689 inner: mods.inner.values().map(GameMod::intermode).collect(),
690 }
691 }
692}
693
694impl From<GameModsLegacy> for GameModsIntermode {
695 fn from(mods: GameModsLegacy) -> Self {
696 mods.to_intermode()
697 }
698}
699
700impl From<GameModIntermode> for GameModsIntermode {
701 fn from(gamemod: GameModIntermode) -> Self {
702 let mut mods = Self::new();
703 mods.insert(gamemod);
704
705 mods
706 }
707}
708
709impl FromStr for GameModsIntermode {
710 type Err = Infallible;
711
712 fn from_str(s: &str) -> Result<Self, Self::Err> {
713 Ok(Self::from_acronyms(s))
714 }
715}
716
717#[cfg(feature = "serde")]
718#[cfg_attr(all(docsrs, not(doctest)), doc(cfg(feature = "serde")))]
719const _: () = {
720 use serde::{
721 de::{Deserialize, Deserializer, Error as DeError, SeqAccess, Visitor},
722 ser::{Serialize, Serializer},
723 };
724
725 use crate::serde::{GameModRaw, GameModRawSeed, MaybeOwnedStr, BITFLAGS_U32};
726
727 impl<'de> Deserialize<'de> for GameModsIntermode {
728 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
729 struct GameModsIntermodeVisitor;
730
731 impl<'de> Visitor<'de> for GameModsIntermodeVisitor {
732 type Value = GameModsIntermode;
733
734 fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
735 f.write_str("integer bitflags, mod acronyms, or a sequence of mods")
736 }
737
738 fn visit_i64<E: DeError>(self, v: i64) -> Result<Self::Value, E> {
739 let bits = u32::try_from(v).map_err(|_| DeError::custom(BITFLAGS_U32))?;
740
741 self.visit_u32(bits)
742 }
743
744 fn visit_u32<E: DeError>(self, v: u32) -> Result<Self::Value, E> {
745 Ok(GameModsIntermode::from_bits(v))
746 }
747
748 fn visit_u64<E: DeError>(self, v: u64) -> Result<Self::Value, E> {
749 let bits = u32::try_from(v).map_err(|_| DeError::custom(BITFLAGS_U32))?;
750
751 self.visit_u32(bits)
752 }
753
754 fn visit_str<E: DeError>(self, v: &str) -> Result<Self::Value, E> {
755 Ok(GameModsIntermode::from_acronyms(v))
756 }
757
758 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
759 let mut inner = BTreeSet::new();
760 let seed = GameModRawSeed {
761 deny_unknown_fields: true,
762 };
763
764 while let Some(raw) = seq.next_element_seed(seed)? {
765 fn try_acronym_to_gamemod<E: DeError>(
766 acronym: &MaybeOwnedStr<'_>,
767 ) -> Result<GameModIntermode, E> {
768 acronym
769 .as_str()
770 .parse()
771 .map(GameModIntermode::from_acronym)
772 .map_err(DeError::custom)
773 }
774
775 let gamemod = match raw {
776 GameModRaw::Bits(bits) => GameModIntermode::try_from_bits(bits)
777 .ok_or_else(|| DeError::custom("invalid bitflags"))?,
778 GameModRaw::Acronym(acronym) => try_acronym_to_gamemod(&acronym)?,
779 GameModRaw::Full { acronym, .. } => try_acronym_to_gamemod(&acronym)?,
780 };
781
782 inner.insert(gamemod);
783 }
784
785 Ok(GameModsIntermode { inner })
786 }
787 }
788
789 d.deserialize_any(GameModsIntermodeVisitor)
790 }
791 }
792
793 impl Serialize for GameModsIntermode {
794 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
795 self.inner.serialize(s)
796 }
797 }
798};
799
800#[cfg(feature = "rkyv")]
801#[cfg_attr(all(docsrs, not(doctest)), doc(cfg(feature = "rkyv")))]
802const _: () = {
803 use rkyv::{
804 rancor::Fallible,
805 ser::{Allocator, Writer},
806 vec::{ArchivedVec, VecResolver},
807 Archive, Archived, Deserialize, Place, Serialize,
808 };
809
810 impl Archive for GameModsIntermode {
811 type Archived = Archived<Vec<GameModIntermode>>;
812 type Resolver = VecResolver;
813
814 fn resolve(&self, resolver: Self::Resolver, out: Place<Self::Archived>) {
815 ArchivedVec::resolve_from_len(self.inner.len(), resolver, out);
816 }
817 }
818
819 impl<S: Fallible + Allocator + Writer + ?Sized> Serialize<S> for GameModsIntermode {
820 fn serialize(&self, s: &mut S) -> Result<Self::Resolver, S::Error> {
821 ArchivedVec::serialize_from_iter::<GameModIntermode, _, _>(self.inner.iter(), s)
822 }
823 }
824
825 impl<D: Fallible + ?Sized> Deserialize<GameModsIntermode, D>
826 for ArchivedVec<Archived<GameModIntermode>>
827 {
828 fn deserialize(&self, _: &mut D) -> Result<GameModsIntermode, D::Error> {
829 Ok(self.iter().copied().collect())
830 }
831 }
832};
833
834#[cfg(test)]
835mod tests {
836 use super::*;
837
838 #[test]
839 fn push() {
840 let mut mods = GameModsIntermode::default();
841 mods.insert(GameModIntermode::HardRock);
842 mods.insert(GameModIntermode::Wiggle);
843
844 assert_eq!(mods.len(), 2);
845 assert_eq!(mods.to_string(), "HRWG");
846 }
847
848 #[test]
849 fn from_bits_nomod() {
850 assert!(GameModsIntermode::from_bits(0).is_empty());
851 }
852
853 #[test]
854 fn from_bits_valid() {
855 let mut expected = GameModsIntermode::default();
856 expected.insert(GameModIntermode::Nightcore);
857 expected.insert(GameModIntermode::Hidden);
858
859 assert_eq!(GameModsIntermode::from_bits(8 + 64 + 512), expected);
860 }
861
862 #[test]
863 fn from_bits_invalid_nightcore() {
864 assert_eq!(GameModsIntermode::from_bits(512), GameModsIntermode::new());
865 }
866
867 #[test]
868 fn from_str_nonempty() {
869 let mods: GameModsIntermode = "TCWGFLWU".parse().unwrap();
870
871 let mut expected = GameModsIntermode::default();
872 expected.insert(GameModIntermode::Flashlight);
873 expected.insert(GameModIntermode::Traceable);
874 expected.insert(GameModIntermode::Wiggle);
875 expected.insert(GameModIntermode::WindUp);
876
877 assert_eq!(mods, expected);
878 }
879
880 #[test]
881 fn from_str_unknown() {
882 let mut iter = "YYQQQ".parse::<GameModsIntermode>().unwrap().into_iter();
883
884 assert_eq!(iter.next().unwrap().to_string(), "QQQ");
888 assert_eq!(iter.next().unwrap().to_string(), "YY");
889 assert!(iter.next().is_none());
890 }
891
892 #[test]
893 fn contains() {
894 let mut mods = GameModsIntermode::default();
895 mods.insert(GameModIntermode::Hidden);
896 mods.insert(GameModIntermode::HardRock);
897 mods.insert(GameModIntermode::Nightcore);
898
899 assert!(mods.contains(GameModIntermode::Nightcore));
900 assert!(mods.contains(GameModIntermode::Hidden));
901 assert!(!mods.contains(GameModIntermode::DoubleTime));
902 }
903
904 #[test]
905 fn checked_bits() {
906 let mut mods = GameModsIntermode::default();
907 mods.insert(GameModIntermode::Hidden);
908 mods.insert(GameModIntermode::Traceable);
909 mods.insert(GameModIntermode::DoubleTime);
910
911 assert_eq!(mods.checked_bits(), None);
912 }
913
914 #[test]
915 fn unchecked_bits() {
916 let mut mods = GameModsIntermode::default();
917 mods.insert(GameModIntermode::Traceable);
918 mods.insert(GameModIntermode::DoubleTime);
919 mods.insert(GameModIntermode::Hidden);
920
921 assert_eq!(mods.bits(), 72);
922 }
923
924 #[test]
925 fn intersection() {
926 let mut a = GameModsIntermode::default();
927 a.insert(GameModIntermode::Hidden);
928 a.insert(GameModIntermode::WindUp);
929 a.insert(GameModIntermode::HardRock);
930
931 let mut b = GameModsIntermode::default();
932 b.insert(GameModIntermode::WindUp);
933 b.insert(GameModIntermode::Classic);
934 b.insert(GameModIntermode::HardRock);
935
936 let mut intersection = a.intersection(&b);
937 assert_eq!(intersection.next(), Some(GameModIntermode::HardRock));
938 assert_eq!(intersection.next(), Some(GameModIntermode::WindUp));
939 assert_eq!(intersection.next(), None);
940 }
941
942 #[cfg(feature = "serde")]
943 mod serde {
944 use super::*;
945
946 #[test]
947 fn deser_str() {
948 let json = r#""HDHRWG""#;
949 let mods = serde_json::from_str::<GameModsIntermode>(json).unwrap();
950
951 let mut expected = GameModsIntermode::default();
952 expected.insert(GameModIntermode::Hidden);
953 expected.insert(GameModIntermode::HardRock);
954 expected.insert(GameModIntermode::Wiggle);
955
956 assert_eq!(mods, expected);
957 }
958
959 #[test]
960 fn deser_bits() {
961 let json = "1096";
962 let mods = serde_json::from_str::<GameModsIntermode>(json).unwrap();
963
964 let mut expected = GameModsIntermode::default();
965 expected.insert(GameModIntermode::Hidden);
966 expected.insert(GameModIntermode::DoubleTime);
967 expected.insert(GameModIntermode::Flashlight);
968
969 assert_eq!(mods, expected);
970 }
971
972 #[test]
973 fn deser_seq() {
974 let json = r#"["WU", "BL", 2, { "acronym": "NS", "settings": { "any": true } }]"#;
975 let mods = serde_json::from_str::<GameModsIntermode>(json).unwrap();
976
977 let mut expected = GameModsIntermode::default();
978 expected.insert(GameModIntermode::Blinds);
979 expected.insert(GameModIntermode::Easy);
980 expected.insert(GameModIntermode::WindUp);
981 expected.insert(GameModIntermode::NoScope);
982
983 assert_eq!(mods, expected);
984 }
985 }
986}