1#[non_exhaustive]
3#[derive(Debug, Copy, Clone, Eq, PartialEq)]
4pub struct State {
5 pub current: Set,
7 pub pushed: Set,
11 pub released: Set,
15}
16
17impl State {
18 #[inline]
20 #[must_use]
21 pub fn is_pressed(self, button: Button) -> bool {
22 self.current.contains(button)
23 }
24
25 #[inline]
29 #[must_use]
30 pub fn is_just_pressed(self, button: Button) -> bool {
31 self.pushed.contains(button)
32 }
33
34 #[inline]
38 #[must_use]
39 pub fn is_just_released(self, button: Button) -> bool {
40 self.released.contains(button)
41 }
42
43 #[inline]
45 #[must_use]
46 pub fn is_any_pressed(&self, buttons: Set) -> bool {
47 self.current.contains_any(buttons)
48 }
49
50 #[inline]
52 #[must_use]
53 pub fn is_any_just_pressed(&self, buttons: Set) -> bool {
54 self.pushed.contains_any(buttons)
55 }
56
57 #[inline]
59 #[must_use]
60 pub fn is_any_just_released(&self, buttons: Set) -> bool {
61 self.released.contains_any(buttons)
62 }
63
64 #[inline]
68 #[must_use]
69 pub fn d_pad<T: From<i8>>(self) -> [T; 2] {
70 self.current.d_pad()
71 }
72
73 #[inline]
77 #[must_use]
78 pub fn d_pad_just_pressed<T: From<i8>>(self) -> [T; 2] {
79 self.pushed.d_pad()
80 }
81
82 #[inline]
86 #[must_use]
87 pub fn d_pad_just_released<T: From<i8>>(self) -> [T; 2] {
88 self.released.d_pad()
89 }
90}
91
92#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
94pub struct Set(pub(crate) u8);
95
96impl Set {
97 #[allow(clippy::cast_possible_truncation)]
99 pub const D_PAD: Self =
100 Self(Button::Left as u8 | Button::Right as u8 | Button::Up as u8 | Button::Down as u8);
101
102 #[inline]
103 #[must_use]
104 #[allow(missing_docs)]
105 pub fn new() -> Self {
106 Self::default()
107 }
108
109 #[allow(missing_docs)]
110 pub fn insert(&mut self, button: Button) {
111 self.0 |= Set::from(button).0;
112 }
113
114 #[inline]
116 #[must_use]
117 pub fn contains(self, button: Button) -> bool {
118 self.contains_any(button)
119 }
120
121 #[inline]
125 #[must_use]
126 pub fn contains_any(self, buttons: impl Into<Self>) -> bool {
127 (self.0 & buttons.into().0) > 0
128 }
129
130 #[must_use]
140 pub fn d_pad<T: From<i8>>(self) -> [T; 2] {
141 let mut x = 0;
142 let mut y = 0;
143 if self.contains(Button::Up) {
144 y -= 1;
145 }
146 if self.contains(Button::Down) {
147 y += 1;
148 }
149 if self.contains(Button::Left) {
150 x -= 1;
151 }
152 if self.contains(Button::Right) {
153 x += 1;
154 }
155 [x.into(), y.into()]
156 }
157}
158
159impl Extend<Button> for Set {
160 fn extend<T: IntoIterator<Item = Button>>(&mut self, iter: T) {
161 iter.into_iter().for_each(|b| self.insert(b));
162 }
163}
164
165impl FromIterator<Button> for Set {
166 fn from_iter<T: IntoIterator<Item = Button>>(iter: T) -> Self {
167 let mut result = Self::default();
168 result.extend(iter);
169 result
170 }
171}
172
173impl From<&[Button]> for Set {
174 fn from(value: &[Button]) -> Self {
175 value.iter().copied().collect()
176 }
177}
178
179impl<const N: usize> From<[Button; N]> for Set {
180 fn from(value: [Button; N]) -> Self {
181 value.into_iter().collect()
182 }
183}
184
185impl From<Button> for Set {
186 fn from(value: Button) -> Self {
187 Self(value as u8)
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use rstest::rstest;
194
195 use super::*;
196
197 #[rstest]
198 #[case(Button::A, Button::A, true)]
199 #[case(Button::A, Button::B, false)]
200 #[case(Button::B, Button::A, false)]
201 #[case(Button::B, Button::B, true)]
202 #[case([Button::A, Button::B], Button::B, true)]
203 #[case([Button::A, Button::B], Button::A, true)]
204 #[case([Button::A, Button::B], Button::Up, false)]
205 #[case([Button::A, Button::B, Button::Up], Button::Up, true)]
206 fn test_set_contains(
207 #[case] set: impl Into<Set>,
208 #[case] button: Button,
209 #[case] expected: bool,
210 ) {
211 let set = set.into();
212 assert_eq!(set.contains(button), expected);
213 assert_eq!(set.contains_any(button), expected);
214 }
215
216 #[rstest]
217 #[case(Set::default(), Set::from_iter([Button::A]), false)]
218 #[case(Set::default(), Set::from_iter([Button::A, Button::B]), false)]
219 #[case(Set::default(), Set::default(), false)]
220 #[case(Set::from_iter([Button::A]), Set::default(), false)]
221 #[case(Set::from_iter([Button::A]), Set::from_iter([Button::A]), true)]
222 #[case(Set::from_iter([Button::A, Button::B]), Set::from_iter([Button::A]), true)]
223 #[case(Set::from_iter([Button::A, Button::B]), Set::from_iter([Button::A, Button::B]), true)]
224 #[case(Set::from_iter([Button::A]), Set::from_iter([Button::A, Button::B]), true)]
225 fn test_set_contains_any(#[case] set: Set, #[case] buttons: Set, #[case] expected: bool) {
226 assert_eq!(set.contains_any(buttons), expected);
227 }
228
229 #[rstest]
230 #[case(Set::default(), [0, 0])]
231 #[case([Button::Up], [0, -1])]
232 #[case([Button::Down], [0, 1])]
233 #[case([Button::Left], [-1, 0])]
234 #[case([Button::Right], [1, 0])]
235 #[case([Button::Right, Button::Down, Button::Up], [1, 0])]
236 #[case([Button::Left, Button::Right, Button::Up], [0, -1])]
237 #[case([Button::Left, Button::Right, Button::Up, Button::Down], [0, 0])]
238 fn d_pad_vector(#[case] set: impl Into<Set>, #[case] expected: [i8; 2]) {
239 let set = set.into();
240 assert_eq!(set.d_pad::<i8>(), expected);
241 assert_eq!(set.d_pad::<i32>(), [expected[0].into(), expected[1].into()]);
242 let _: [f32; 2] = set.d_pad::<f32>();
243 }
244}
245
246#[repr(u8)]
248#[allow(clippy::exhaustive_enums, missing_docs)]
249#[derive(Debug, Copy, Clone, Eq, PartialEq)]
250pub enum Button {
251 Left = 1,
252 Right = 2,
253 Up = 4,
254 Down = 8,
255 B = 16,
256 A = 32,
257}