rosu_mods/
iter.rs

1use std::{
2    collections::{
3        btree_map::{IntoValues, Values, ValuesMut},
4        btree_set::{IntoIter, Iter},
5    },
6    fmt::{Debug, Formatter, Result as FmtResult},
7    iter::{Copied, FusedIterator},
8};
9
10use crate::{legacy::GameModsLegacy, order::GameModOrder};
11
12use super::{GameMod, GameModIntermode};
13
14macro_rules! mods_iter {
15    (
16        $( #[$meta:meta] )*
17        $name:ident $( < $outer_lifetime:lifetime > )? :
18        $inner:ident < $( $inner_generic:tt ),+ > =>
19        $item:ty
20    ) => {
21        $( #[ $meta ] )*
22        pub struct $name $( < $outer_lifetime > )? {
23            inner: $inner < $( $inner_generic ),* >,
24        }
25
26        impl $( < $outer_lifetime > )? $name $( < $outer_lifetime > )? {
27            pub(super) const fn new(inner: $inner < $( $inner_generic ),* >) -> Self {
28                Self { inner }
29            }
30        }
31
32        impl $( < $outer_lifetime > )? Debug for $name $( < $outer_lifetime > )? {
33            fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
34                Debug::fmt(&self.inner, f)
35            }
36        }
37
38        impl $( < $outer_lifetime > )? Iterator for $name $( < $outer_lifetime > )? {
39            type Item = $item;
40
41            fn next(&mut self) -> Option<Self::Item> {
42                self.inner.next()
43            }
44
45            fn size_hint(&self) -> (usize, Option<usize>) {
46                self.inner.size_hint()
47            }
48
49            fn last(mut self) -> Option<Self::Item> {
50                self.inner.next_back()
51            }
52        }
53
54        impl $( < $outer_lifetime > )? DoubleEndedIterator for $name $( < $outer_lifetime > )? {
55            fn next_back(&mut self) -> Option<Self::Item> {
56                self.inner.next_back()
57            }
58        }
59
60        impl $( < $outer_lifetime > )? ExactSizeIterator for $name $( < $outer_lifetime > )? {
61            fn len(&self) -> usize {
62                self.inner.len()
63            }
64        }
65
66        impl $( < $outer_lifetime > )? FusedIterator for $name $( < $outer_lifetime > )? {}
67    };
68}
69
70mods_iter! {
71    #[derive(Clone)]
72    #[doc = "Iterates over [`GameMod`] references"]
73    GameModsIter<'m>: Values<'m, GameModOrder, GameMod> => &'m GameMod
74}
75mods_iter! {
76    #[doc = "Iterates over mutable [`GameMod`] references"]
77    GameModsIterMut<'m>: ValuesMut<'m, GameModOrder, GameMod> => &'m mut GameMod
78}
79mods_iter! {
80    #[doc = "Iterates over [`GameMod`]"]
81    IntoGameModsIter: IntoValues<GameModOrder, GameMod> => GameMod
82}
83
84type GameModsIntermodeIterInner<'m> = Copied<Iter<'m, GameModIntermode>>;
85
86mods_iter! {
87    #[derive(Clone)]
88    #[doc = "Iterates over [`GameModIntermode`]"]
89    GameModsIntermodeIter<'m>: GameModsIntermodeIterInner<'m> => GameModIntermode
90}
91mods_iter! {
92    #[doc = "Iterates over [`GameModIntermode`]"]
93    IntoGameModsIntermodeIter: IntoIter<GameModIntermode> => GameModIntermode
94}
95
96/// Iterates over [`GameModsLegacy`]
97pub struct GameModsLegacyIter {
98    mods: GameModsLegacy,
99    shift: usize,
100}
101
102impl GameModsLegacyIter {
103    /// Creates a new [`GameModsLegacyIter`]
104    pub const fn new(mods: GameModsLegacy) -> Self {
105        Self { mods, shift: 0 }
106    }
107}
108
109impl Iterator for GameModsLegacyIter {
110    type Item = GameModsLegacy;
111
112    fn next(&mut self) -> Option<Self::Item> {
113        if !self.mods.is_empty() {
114            loop {
115                if self.shift == 32 {
116                    return None;
117                }
118
119                let mut bit = 1 << self.shift;
120                self.shift += 1;
121
122                if (GameModsLegacy::from_bits_retain(bit) == GameModsLegacy::SuddenDeath
123                    && self.mods.contains(GameModsLegacy::Perfect))
124                    || (GameModsLegacy::from_bits_retain(bit) == GameModsLegacy::DoubleTime
125                        && self.mods.contains(GameModsLegacy::Nightcore))
126                {
127                    continue;
128                }
129
130                if bit == (GameModsLegacy::Nightcore - GameModsLegacy::DoubleTime).bits() {
131                    bit += GameModsLegacy::DoubleTime.bits();
132                } else if bit == (GameModsLegacy::Perfect - GameModsLegacy::SuddenDeath).bits() {
133                    bit += GameModsLegacy::SuddenDeath.bits();
134                }
135
136                if self.mods.bits() & bit == bit {
137                    let mods = GameModsLegacy::from_bits(bit);
138                    self.mods.remove(mods);
139
140                    return Some(mods);
141                }
142            }
143        } else if self.shift == 0 {
144            self.shift = 32;
145
146            Some(GameModsLegacy::NoMod)
147        } else {
148            None
149        }
150    }
151
152    fn size_hint(&self) -> (usize, Option<usize>) {
153        let len = self.len();
154
155        (len, Some(len))
156    }
157}
158
159// TODO: impl DoubleEndedIterator for GameModsLegacyIter
160
161impl ExactSizeIterator for GameModsLegacyIter {
162    fn len(&self) -> usize {
163        self.mods.len() + usize::from(self.mods.is_empty() && self.shift == 0)
164    }
165}
166
167impl FusedIterator for GameModsLegacyIter {}