tf2_enum/spell_set.rs
1//! Set for holding up to 2 spells.
2
3use std::fmt;
4use std::hash::{Hash, Hasher};
5use std::ops::{BitAnd, Sub};
6use crate::Spell;
7
8const SPELL_COUNT: usize = 2;
9
10/// Contains up to 2 spells. Although the underlying data structure is an array, this structure
11/// behaves like a set. Most methods mimic those of [`HashSet`](std::collections::HashSet).
12///
13/// This struct solves the following problems:
14/// - An item can only hold up to 2 spells.
15/// - An item cannot have duplicate spells or multiple spells of the same type.
16/// - Comparing spells for equality is order-agnostic.
17/// - Hashing is order-agnostic.
18/// - The type is `Copy`, allowing for cheap and easy duplication.
19///
20/// # Examples
21/// ```
22/// use tf2_enum::{SpellSet, Spell};
23///
24/// // Create a set for spells with one spell.
25/// let mut spells = SpellSet::single(Spell::HeadlessHorseshoes);
26///
27/// // Check that spells contains Headless Horseshoes.
28/// assert!(spells.contains(&Spell::HeadlessHorseshoes));
29/// assert_eq!(spells.len(), 1);
30///
31/// // Add a spell.
32/// spells.insert(Spell::VoicesFromBelow);
33/// assert_eq!(spells.len(), 2);
34///
35/// // If a spell is added when spells are full, the insert will fail.
36/// assert!(!spells.insert(Spell::PumpkinBombs));
37/// assert!(!spells.contains(&Spell::PumpkinBombs));
38///
39/// // Iterate over spells.
40/// for spell in spells {
41/// println!("{}", spell);
42/// }
43/// ```
44#[derive(Debug, Default, Clone, Copy, Eq)]
45pub struct SpellSet {
46 inner: [Option<Spell>; SPELL_COUNT]
47}
48
49impl SpellSet {
50 /// Creates a set for spells.
51 ///
52 /// # Examples
53 /// ```
54 /// use tf2_enum::SpellSet;
55 ///
56 /// let spells = SpellSet::new();
57 /// ```
58 pub fn new() -> Self {
59 Self::default()
60 }
61
62 /// Creates a set for spells with one spell.
63 ///
64 /// # Examples
65 /// ```
66 /// use tf2_enum::{SpellSet, Spell};
67 ///
68 /// let spells = SpellSet::single(Spell::HeadlessHorseshoes);
69 ///
70 /// assert_eq!(spells.len(), 1);
71 /// ```
72 pub fn single(spell: Spell) -> Self {
73 Self {
74 inner: [
75 Some(spell),
76 None,
77 ],
78 }
79 }
80
81 /// Creates a set for spells with two spells.
82 ///
83 /// If the same spell is added multiple times, only one will be kept. This is also true for
84 /// spells of the same type. In cases of multiple spells of the same type, the first occuring
85 /// spell will be prioritized.
86 ///
87 /// # Examples
88 /// ```
89 /// use tf2_enum::{SpellSet, Spell};
90 ///
91 /// let spells = SpellSet::double(Spell::HeadlessHorseshoes, Spell::VoicesFromBelow);
92 ///
93 /// assert_eq!(spells.len(), 2);
94 ///
95 /// let spells = SpellSet::double(Spell::HeadlessHorseshoes, Spell::TeamSpiritFootprints);
96 ///
97 /// assert_eq!(spells.len(), 1);
98 /// assert_eq!(SpellSet::single(Spell::HeadlessHorseshoes), spells);
99 /// ```
100 pub fn double(spell1: Spell, spell2: Spell) -> Self {
101 Self::from([
102 Some(spell1),
103 Some(spell2),
104 ])
105 }
106
107 /// Clears the set, removing all spells.
108 ///
109 /// # Examples
110 /// ```
111 /// use tf2_enum::{SpellSet, Spell};
112 ///
113 /// let mut spells = SpellSet::single(Spell::HeadlessHorseshoes);
114 ///
115 /// spells.clear();
116 ///
117 /// assert_eq!(spells.len(), 0);
118 /// ```
119 pub fn clear(&mut self) {
120 self.inner = [None, None];
121 }
122
123 /// Adds a spell to the first available slot.
124 ///
125 /// Returns `false` if:
126 /// - The spell is already in the set.
127 /// - The set is full.
128 ///
129 /// # Examples
130 /// ```
131 /// use tf2_enum::{SpellSet, Spell};
132 ///
133 /// let mut spells = SpellSet::single(Spell::HeadlessHorseshoes);
134 ///
135 /// assert_eq!(spells.len(), 1);
136 ///
137 /// spells.insert(Spell::VoicesFromBelow);
138 ///
139 /// assert_eq!(spells.len(), 2);
140 ///
141 /// // Spells are full.
142 /// assert!(!spells.insert(Spell::PumpkinBombs));
143 /// ```
144 pub fn insert(&mut self, spell: Spell) -> bool {
145 let attribute_defindex = spell.attribute_defindex();
146
147 if self.inner.iter().flatten().any(|s| s.attribute_defindex() == attribute_defindex) {
148 return false;
149 }
150
151 if let Some(slot) = self.inner.iter_mut().find(|slot| slot.is_none()) {
152 *slot = Some(spell);
153 return true;
154 }
155
156 // full set, insertion failed
157 false
158 }
159
160 /// Removes a spell from the set. Returns whether the value was present in the set.
161 ///
162 /// # Examples
163 /// ```
164 /// use tf2_enum::{SpellSet, Spell};
165 ///
166 /// let mut spells = SpellSet::single(Spell::HeadlessHorseshoes);
167 ///
168 /// assert!(spells.remove(&Spell::HeadlessHorseshoes));
169 /// assert!(!spells.contains(&Spell::HeadlessHorseshoes));
170 /// ```
171 pub fn remove(&mut self, spell: &Spell) -> bool {
172 if self.inner[0] == Some(*spell) {
173 self.inner[0] = None;
174 true
175 } else if self.inner[1] == Some(*spell) {
176 self.inner[1] = None;
177 true
178 } else {
179 false
180 }
181 }
182
183 /// Removes and returns the spell in the set, if any, that is equal to the given one.
184 pub fn take(&mut self, spell: &Spell) -> Option<Spell> {
185 if self.inner[0] == Some(*spell) {
186 self.inner[0] = None;
187 return Some(*spell);
188 } else if self.inner[1] == Some(*spell) {
189 self.inner[1] = None;
190 return Some(*spell);
191 }
192
193 None
194 }
195
196 /// Returns `true` if the set contains no spells.
197 pub fn is_empty(&self) -> bool {
198 self.inner
199 .iter()
200 .all(|s| s.is_none())
201 }
202
203 /// Returns `true` if the set contains a spell.
204 ///
205 /// # Examples
206 /// ```
207 /// use tf2_enum::{SpellSet, Spell};
208 ///
209 /// let mut spells = SpellSet::single(Spell::HeadlessHorseshoes);
210 ///
211 /// assert!(spells.contains(&Spell::HeadlessHorseshoes));
212 /// ```
213 pub fn contains(&self, spell: &Spell) -> bool {
214 self.inner.contains(&Some(*spell))
215 }
216
217 /// Returns the number of spells in the set.
218 ///
219 /// # Examples
220 /// ```
221 /// use tf2_enum::{SpellSet, Spell};
222 ///
223 /// let spells = SpellSet::single(Spell::HeadlessHorseshoes);
224 ///
225 /// assert_eq!(spells.len(), 1);
226 /// ```
227 pub fn len(&self) -> usize {
228 self.inner
229 .iter()
230 .filter(|s| s.is_some())
231 .count()
232 }
233
234 /// Returns the spells that are in `self` but not in `other`.
235 ///
236 /// # Examples
237 /// ```
238 /// use tf2_enum::{SpellSet, Spell};
239 ///
240 /// let spells1 = SpellSet::double(Spell::HalloweenFire, Spell::Exorcism);
241 /// let spells2 = SpellSet::double(Spell::HalloweenFire, Spell::VoicesFromBelow);
242 /// let difference = spells1.difference(&spells2);
243 ///
244 /// assert_eq!(difference, SpellSet::single(Spell::Exorcism));
245 ///
246 /// let difference = spells2.difference(&spells1);
247 ///
248 /// assert_eq!(difference, SpellSet::single(Spell::VoicesFromBelow));
249 /// ```
250 pub fn difference(&self, other: &Self) -> Self {
251 let mut inner = [None, None];
252
253 for (i, s_option) in inner.iter_mut().enumerate() {
254 if let Some(s) = self.inner[i] {
255 if !other.contains(&s) {
256 *s_option = Some(s);
257 }
258 }
259 }
260
261 Self {
262 inner,
263 }
264 }
265
266 /// Returns the spells that are both in `self` and `other`.
267 ///
268 /// # Examples
269 /// ```
270 /// use tf2_enum::{SpellSet, Spell};
271 ///
272 /// let spells1 = SpellSet::double(Spell::HalloweenFire, Spell::Exorcism);
273 /// let spells2 = SpellSet::double(Spell::HalloweenFire, Spell::VoicesFromBelow);
274 /// let intersection = spells1.intersection(&spells2);
275 ///
276 /// assert_eq!(intersection, SpellSet::single(Spell::HalloweenFire));
277 /// ```
278 pub fn intersection(&self, other: &Self) -> Self {
279 let mut inner = [None, None];
280
281 for (i, s_option) in inner.iter_mut().enumerate() {
282 if let Some(s) = self.inner[i] {
283 if other.contains(&s) {
284 *s_option = Some(s);
285 }
286 }
287 }
288
289 Self {
290 inner,
291 }
292 }
293
294 /// Returns `true` if `self` has no spells in common with `other`. This is equivalent to
295 /// checking for an empty intersection.
296 ///
297 /// # Examples
298 /// ```
299 /// use tf2_enum::{SpellSet, Spell};
300 ///
301 /// let spells1 = SpellSet::double(Spell::HalloweenFire, Spell::Exorcism);
302 /// let spells2 = SpellSet::double(Spell::HalloweenFire, Spell::VoicesFromBelow);
303 ///
304 /// assert!(!spells1.is_disjoint(&spells2));
305 /// ```
306 pub fn is_disjoint(&self, other: &Self) -> bool {
307 self.intersection(other).is_empty()
308 }
309
310 /// Returns true if the set is a subset of another, i.e., other contains at least all the values in self.
311 ///
312 /// # Examples
313 /// ```
314 /// use tf2_enum::{SpellSet, Spell};
315 ///
316 /// let sup = SpellSet::double(Spell::HalloweenFire, Spell::Exorcism);
317 /// let mut spells = SpellSet::single(Spell::HalloweenFire);
318 ///
319 /// assert!(spells.is_subset(&sup));
320 ///
321 /// spells.insert(Spell::HeadlessHorseshoes);
322 ///
323 /// assert!(!spells.is_subset(&sup));
324 /// ```
325 pub fn is_subset(&self, other: &Self) -> bool {
326 if self.len() > other.len() {
327 return false;
328 }
329
330 self.iter().all(|spell| other.contains(spell))
331 }
332
333 /// Returns true if the set is a superset of another, i.e., self contains at least all the values in other.
334 ///
335 /// # Examples
336 /// ```
337 /// use tf2_enum::{SpellSet, Spell};
338 ///
339 /// let sub = SpellSet::double(Spell::HalloweenFire, Spell::Exorcism);
340 /// let mut spells = SpellSet::new();
341 ///
342 /// assert!(!spells.is_superset(&sub));
343 ///
344 /// spells.insert(Spell::HalloweenFire);
345 ///
346 /// assert!(!spells.is_superset(&sub));
347 ///
348 /// spells.insert(Spell::Exorcism);
349 ///
350 /// assert!(spells.is_superset(&sub));
351 /// ```
352 pub fn is_superset(&self, other: &Self) -> bool {
353 other.is_subset(self)
354 }
355
356 /// Returns an iterator over the spells in the set.
357 pub fn iter(&self) -> impl Iterator<Item = &Spell> {
358 self.inner.iter().filter_map(|opt| opt.as_ref())
359 }
360}
361
362impl From<[Option<Spell>; SPELL_COUNT]> for SpellSet {
363 fn from(mut inner: [Option<Spell>; SPELL_COUNT]) -> Self {
364 // remove duplicates
365 // since this only contains 2 spells it's not really necessary to do this using loops but
366 // the implementation is consistent with StrangePartSet
367 for i in 0..SPELL_COUNT {
368 if let Some(val_i) = inner[i] {
369 for j in 0..i {
370 if let Some(val_j) = inner[j] {
371 if val_i.attribute_defindex() == val_j.attribute_defindex() {
372 inner[i] = None;
373 break;
374 }
375 }
376 }
377 }
378 }
379
380 Self {
381 inner,
382 }
383 }
384}
385
386// Only Sub is implemented because Add wouldn't make much sense with spells being limited to 2.
387impl Sub for SpellSet {
388 type Output = Self;
389
390 fn sub(self, other: Self) -> Self::Output {
391 self.difference(&other)
392 }
393}
394
395impl Sub for &SpellSet {
396 type Output = SpellSet;
397
398 fn sub(self, other: &SpellSet) -> Self::Output {
399 self.difference(other)
400 }
401}
402
403impl BitAnd for SpellSet {
404 type Output = Self;
405
406 fn bitand(self, other: Self) -> Self::Output {
407 self.intersection(&other)
408 }
409}
410
411impl BitAnd for &SpellSet {
412 type Output = SpellSet;
413
414 fn bitand(self, other: &SpellSet) -> Self::Output {
415 self.intersection(other)
416 }
417}
418
419impl PartialEq<Self> for SpellSet {
420 fn eq(&self, other: &Self) -> bool {
421 (self.inner[0] == other.inner[0] && self.inner[1] == other.inner[1]) ||
422 (self.inner[0] == other.inner[1] && self.inner[1] == other.inner[0])
423 }
424}
425
426impl Hash for SpellSet {
427 fn hash<H: Hasher>(&self, state: &mut H) {
428 if self.inner[0] <= self.inner[1] {
429 self.inner[0].hash(state);
430 self.inner[1].hash(state);
431 } else {
432 self.inner[1].hash(state);
433 self.inner[0].hash(state);
434 }
435 }
436}
437
438impl FromIterator<Spell> for SpellSet {
439 fn from_iter<I: IntoIterator<Item = Spell>>(iter: I) -> Self {
440 let mut spell_set = Self::new();
441
442 for spell in iter {
443 spell_set.insert(spell);
444 }
445
446 spell_set
447 }
448}
449
450impl IntoIterator for SpellSet {
451 type Item = Spell;
452 type IntoIter = SpellSetIterator;
453
454 fn into_iter(self) -> Self::IntoIter {
455 SpellSetIterator {
456 inner: self.inner.into_iter(),
457 }
458 }
459}
460
461impl IntoIterator for &SpellSet {
462 type Item = Spell;
463 type IntoIter = SpellSetIterator;
464
465 fn into_iter(self) -> Self::IntoIter {
466 SpellSetIterator {
467 inner: self.inner.into_iter(),
468 }
469 }
470}
471
472/// Iterator for spells.
473#[derive(Debug, Clone)]
474pub struct SpellSetIterator {
475 inner: std::array::IntoIter<Option<Spell>, SPELL_COUNT>,
476}
477
478impl Iterator for SpellSetIterator {
479 type Item = Spell;
480
481 fn next(&mut self) -> Option<Self::Item> {
482 let iter = self.inner.by_ref();
483
484 for opt in iter {
485 if opt.is_some() {
486 return opt;
487 }
488 }
489
490 None
491 }
492}
493
494impl fmt::Display for SpellSet {
495 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
496 let mut iter = self.into_iter();
497
498 if let Some(first) = iter.next() {
499 write!(f, "{first}")?;
500
501 for s in iter {
502 write!(f, ", {s}")?;
503 }
504 }
505
506 Ok(())
507 }
508}
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513 use std::collections::HashSet;
514
515 #[test]
516 fn spell_set_equals() {
517 assert_eq!(SpellSet::from([
518 Some(Spell::Exorcism),
519 Some(Spell::HalloweenFire),
520 ]), SpellSet::from([
521 Some(Spell::HalloweenFire),
522 Some(Spell::Exorcism),
523 ]));
524 }
525
526 #[test]
527 fn spell_set_hashes() {
528 let mut set = HashSet::new();
529
530 set.insert(SpellSet::from([
531 Some(Spell::Exorcism),
532 Some(Spell::HalloweenFire),
533 ]));
534
535 assert!(set.contains(&SpellSet::from([
536 Some(Spell::HalloweenFire),
537 Some(Spell::Exorcism),
538 ])));
539 }
540
541 #[test]
542 fn spell_set_no_duplicates() {
543 assert_eq!(SpellSet::from([
544 Some(Spell::Exorcism),
545 Some(Spell::Exorcism),
546 ]), SpellSet::from([
547 Some(Spell::Exorcism),
548 None,
549 ]));
550
551 assert_eq!(SpellSet::from([
552 Some(Spell::TeamSpiritFootprints),
553 Some(Spell::HeadlessHorseshoes),
554 ]), SpellSet::from([
555 Some(Spell::TeamSpiritFootprints),
556 None,
557 ]));
558 }
559
560 #[test]
561 fn iterates_spells() {
562 let spells = SpellSet::from([
563 Some(Spell::Exorcism),
564 Some(Spell::HalloweenFire),
565 ]);
566 let mut count = 0;
567
568 for _spell in spells {
569 count += 1;
570 }
571
572 assert_eq!(count, 2);
573
574 let spells = spells.into_iter().collect::<Vec<_>>();
575
576 assert_eq!(spells, vec![Spell::Exorcism, Spell::HalloweenFire]);
577
578 let spells = SpellSet::from_iter(spells);
579
580 assert_eq!(spells, SpellSet::from([
581 Some(Spell::Exorcism),
582 Some(Spell::HalloweenFire),
583 ]));
584 }
585
586 #[test]
587 fn sub() {
588 let spells1 = SpellSet::from([
589 Some(Spell::HalloweenFire),
590 Some(Spell::Exorcism),
591 ]);
592 let spells2 = SpellSet::from([
593 Some(Spell::HalloweenFire),
594 Some(Spell::VoicesFromBelow),
595 ]);
596
597 let difference = spells1 - spells2;
598
599 assert_eq!(difference, SpellSet::from([
600 Some(Spell::Exorcism),
601 None,
602 ]));
603 }
604
605 #[test]
606 fn stringify() {
607 let spells = SpellSet::from([
608 Some(Spell::Exorcism),
609 Some(Spell::HalloweenFire),
610 ]);
611
612 assert_eq!(spells.to_string(), "Exorcism, Halloween Fire");
613 }
614}