Skip to main content

satellite_collections/generic/
fixed_set.rs

1//! Fixed-capacity set implementation for allocation-free environments.
2//!
3//! This module provides [`FixedSet`], a set-like collection for storing unique elements
4//! with compile-time capacity bounds, and [`FixedCapacitySet`], a trait for generic
5//! set operations.
6
7use crate::generic::push_pop::{PushPopCollection, PushPopError};
8use std::fmt::Debug;
9
10/// Error type for [`FixedSet`] operations.
11#[derive(Debug, PartialEq, Eq)]
12pub enum FixedSetError {
13    /// The set has reached its maximum capacity.
14    Full,
15    /// The item already exists in the set.
16    Duplicate,
17}
18
19impl From<FixedSetError> for u32 {
20    fn from(e: FixedSetError) -> Self {
21        match e {
22            FixedSetError::Full => 1,
23            FixedSetError::Duplicate => 2,
24        }
25    }
26}
27
28impl std::fmt::Display for FixedSetError {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        write!(f, "{:?}", self)
31    }
32}
33
34/// Generic trait for fixed-capacity set operations.
35///
36/// This trait provides a common interface for set-like collections with compile-time
37/// capacity bounds. It's implemented by both [`FixedSet`] and types generated by
38/// the [`declare_fixed_set!`] macro.
39///
40/// # Examples
41///
42/// ```rust
43/// use satellite_bitcoin::generic::fixed_set::{FixedSet, FixedCapacitySet};
44///
45/// fn work_with_set<S: FixedCapacitySet<Item = i32>>(set: &mut S) {
46///     set.insert(42).unwrap();
47///     set.insert(100).unwrap();
48///     assert_eq!(set.len(), 2);
49///     assert!(set.contains(&42));
50/// }
51///
52/// let mut set = FixedSet::<i32, 10>::new();
53/// work_with_set(&mut set);
54/// ```
55///
56/// [`declare_fixed_set!`]: crate::declare_fixed_set
57pub trait FixedCapacitySet: Default + Clone + Debug {
58    /// The element type stored in the set.
59    type Item: Copy + PartialEq + Default;
60
61    /// Maximum number of elements the set can hold.
62    fn capacity(&self) -> usize;
63
64    /// Current number of stored elements.
65    fn len(&self) -> usize;
66
67    /// Check whether the set currently contains `item`.
68    fn contains<Q>(&self, item: &Q) -> bool
69    where
70        Self::Item: PartialEq<Q>;
71
72    /// Find the first item equal to `item` in the set.
73    fn find<Q>(&self, item: &Q) -> Option<&Self::Item>
74    where
75        Self::Item: PartialEq<Q>;
76
77    /// Find the first item equal to `item` in the set and return it.
78    fn find_mut<Q>(&mut self, item: &Q) -> Option<&mut Self::Item>
79    where
80        Self::Item: PartialEq<Q>;
81
82    /// Attempt to insert `item`.
83    /// Returns `Ok(())` on success or an appropriate [`FixedSetError`].
84    fn insert(&mut self, item: Self::Item) -> Result<(), FixedSetError>;
85
86    /// Inserts `item` into the set if it is not already present.
87    /// If it is present, modifies it using the provided function.
88    /// Returns `Ok(())` on success or an appropriate [`FixedSetError`].
89    fn insert_or_modify<E, F>(&mut self, item: Self::Item, modify: F) -> Result<(), E>
90    where
91        F: FnMut(&mut Self::Item) -> Result<(), E>,
92        E: From<FixedSetError>;
93
94    /// Remove `item` if present, returning it.
95    fn remove<Q>(&mut self, item: &Q) -> Option<Self::Item>
96    where
97        Self::Item: PartialEq<Q>;
98
99    /// Access the underlying slice of active items.
100    fn as_slice(&self) -> &[Self::Item];
101
102    /// Returns the elements of the set as a mutable slice.
103    fn as_mut_slice(&mut self) -> &mut [Self::Item];
104
105    /// Iterate over the set.
106    fn iter(&self) -> impl Iterator<Item = &Self::Item> {
107        self.as_slice().iter()
108    }
109
110    /// Iterate over the set mutably.
111    fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Item> + '_ {
112        self.as_mut_slice().iter_mut()
113    }
114}
115
116/// A fixed-capacity set for storing unique elements.
117///
118/// `FixedSet` maintains a collection of unique elements with compile-time capacity bounds.
119/// It provides efficient insertion, removal, and lookup operations while maintaining
120/// element uniqueness.
121///
122/// # Type Parameters
123///
124/// * `T` - The element type. Must implement `Default + Copy + PartialEq`.
125/// * `SIZE` - The maximum number of elements the set can hold (compile-time constant).
126///
127/// # Examples
128///
129/// ```rust
130/// use satellite_bitcoin::generic::fixed_set::FixedSet;
131///
132/// let mut set: FixedSet<u32, 8> = FixedSet::new();
133///
134/// set.insert(3).unwrap();
135/// set.insert(5).unwrap();
136/// assert!(set.contains(&3));
137/// assert_eq!(set.len(), 2);
138///
139/// // Duplicate insertion fails
140/// assert!(set.insert(3).is_err());
141///
142/// // Remove elements
143/// assert_eq!(set.remove(&3), Some(3));
144/// assert_eq!(set.len(), 1);
145/// ```
146///
147/// # Memory Layout
148///
149/// The set stores elements in a contiguous array followed by a length field.
150/// Element order is not preserved across insertions and removals due to the
151/// swap-remove optimization used for O(1) removal.
152///
153/// # Bytemuck Compatibility
154///
155/// `FixedSet` implements `Pod` and `Zeroable` when `T` implements these traits,
156/// making it suitable for zero-copy serialization in on-chain programs.
157#[derive(Debug, Clone, Copy)]
158#[repr(C)]
159pub struct FixedSet<T, const SIZE: usize> {
160    items: [T; SIZE],
161    len: usize,
162    _padding: [u8; 8],
163}
164
165impl<T: Default + Copy + PartialEq, const SIZE: usize> Default for FixedSet<T, SIZE> {
166    fn default() -> Self {
167        Self::new()
168    }
169}
170
171impl<T: Default + Copy + PartialEq, const SIZE: usize> FixedSet<T, SIZE> {
172    /// Creates an empty `FixedSet`.
173    ///
174    /// # Examples
175    ///
176    /// ```rust
177    /// use satellite_bitcoin::generic::fixed_set::FixedSet;
178    ///
179    /// let set: FixedSet<u32, 10> = FixedSet::new();
180    /// assert!(set.is_empty());
181    /// assert_eq!(set.len(), 0);
182    /// ```
183    pub fn new() -> Self {
184        Self {
185            items: [T::default(); SIZE],
186            len: 0,
187            _padding: [0; 8],
188        }
189    }
190
191    /// Returns the number of elements currently stored in the set.
192    ///
193    /// # Examples
194    ///
195    /// ```rust
196    /// use satellite_bitcoin::generic::fixed_set::FixedSet;
197    ///
198    /// let mut set: FixedSet<u32, 10> = FixedSet::new();
199    /// assert_eq!(set.len(), 0);
200    ///
201    /// set.insert(42).unwrap();
202    /// assert_eq!(set.len(), 1);
203    /// ```
204    pub fn len(&self) -> usize {
205        self.len
206    }
207
208    /// Returns `true` if the set contains no elements.
209    ///
210    /// # Examples
211    ///
212    /// ```rust
213    /// use satellite_bitcoin::generic::fixed_set::FixedSet;
214    ///
215    /// let mut set: FixedSet<u32, 10> = FixedSet::new();
216    /// assert!(set.is_empty());
217    ///
218    /// set.insert(42).unwrap();
219    /// assert!(!set.is_empty());
220    /// ```
221    pub fn is_empty(&self) -> bool {
222        self.len == 0
223    }
224
225    /// Returns an iterator over all items currently in the set.
226    ///
227    /// # Examples
228    ///
229    /// ```rust
230    /// use satellite_bitcoin::generic::fixed_set::FixedSet;
231    ///
232    /// let mut set: FixedSet<u32, 10> = FixedSet::new();
233    /// set.insert(1).unwrap();
234    /// set.insert(2).unwrap();
235    ///
236    /// let collected: Vec<_> = set.iter().copied().collect();
237    /// assert_eq!(collected.len(), 2);
238    /// assert!(collected.contains(&1));
239    /// assert!(collected.contains(&2));
240    /// ```
241    pub fn iter(&self) -> impl Iterator<Item = &T> + '_ {
242        self.items[..self.len].iter()
243    }
244
245    /// Returns a mutable iterator over all items currently in the set.
246    ///
247    /// # Examples
248    ///
249    /// ```rust
250    /// use satellite_bitcoin::generic::fixed_set::FixedSet;
251    ///
252    /// let mut set: FixedSet<u32, 10> = FixedSet::new();
253    /// set.insert(1).unwrap();
254    /// set.insert(2).unwrap();
255    ///
256    /// for item in set.iter_mut() {
257    ///     *item *= 10;
258    /// }
259    ///
260    /// assert!(set.contains(&10));
261    /// assert!(set.contains(&20));
262    /// ```
263    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> + '_ {
264        self.items[..self.len].iter_mut()
265    }
266
267    /// Inserts `item` into the set if it is not already present.
268    /// If it is present, modifies it using the provided function.
269    ///
270    /// This is useful for implementing "upsert" semantics where you want to
271    /// insert a new item or update an existing one.
272    ///
273    /// # Examples
274    ///
275    /// ```rust
276    /// use satellite_bitcoin::generic::fixed_set::{FixedSet, FixedSetError};
277    ///
278    /// #[derive(Clone, Copy, Debug, Default)]
279    /// struct Counter { id: u32, count: u32 }
280    ///
281    /// impl PartialEq for Counter {
282    ///     fn eq(&self, other: &Self) -> bool {
283    ///         self.id == other.id // Only compare by ID
284    ///     }
285    /// }
286    ///
287    /// impl Eq for Counter {}
288    ///
289    /// #[derive(Debug)]
290    /// enum MyError {
291    ///     SetError(FixedSetError),
292    /// }
293    ///
294    /// impl From<FixedSetError> for MyError {
295    ///     fn from(e: FixedSetError) -> Self {
296    ///         MyError::SetError(e)
297    ///     }
298    /// }
299    ///
300    /// let mut set: FixedSet<Counter, 10> = FixedSet::new();
301    ///
302    /// // Insert new item
303    /// set.insert_or_modify(
304    ///     Counter { id: 1, count: 1 },
305    ///     |_| Ok::<(), MyError>(())
306    /// ).unwrap();
307    ///
308    /// // Update existing item
309    /// set.insert_or_modify(
310    ///     Counter { id: 1, count: 0 }, // count doesn't matter for lookup
311    ///     |existing| { existing.count += 1; Ok::<(), MyError>(()) }
312    /// ).unwrap();
313    ///
314    /// assert_eq!(set.find(&Counter { id: 1, count: 0 }).unwrap().count, 2);
315    /// ```
316    pub fn insert_or_modify<E, F>(&mut self, item: T, mut modify: F) -> Result<(), E>
317    where
318        F: FnMut(&mut T) -> Result<(), E>,
319        E: From<FixedSetError>,
320    {
321        if self.contains(&item) {
322            let pos = self.iter().position(|i| *i == item).unwrap();
323            modify(&mut self.items[pos])?;
324        } else {
325            self.insert(item).map_err(E::from)?;
326        }
327
328        Ok(())
329    }
330
331    /// Returns `true` if the set already contains `item`.
332    ///
333    /// # Examples
334    ///
335    /// ```rust
336    /// use satellite_bitcoin::generic::fixed_set::FixedSet;
337    ///
338    /// let mut set: FixedSet<u32, 10> = FixedSet::new();
339    /// assert!(!set.contains(&42));
340    ///
341    /// set.insert(42).unwrap();
342    /// assert!(set.contains(&42));
343    /// ```
344    pub fn contains<Q>(&self, item: &Q) -> bool
345    where
346        T: PartialEq<Q>,
347    {
348        self.iter().any(|i| i == item)
349    }
350
351    /// Returns the first item equal to `item` in the set.
352    ///
353    /// This is useful when you need to access the actual stored item that matches
354    /// your query, especially when using custom equality implementations.
355    ///
356    /// # Examples
357    ///
358    /// ```rust
359    /// use satellite_bitcoin::generic::fixed_set::FixedSet;
360    ///
361    /// let mut set: FixedSet<u32, 10> = FixedSet::new();
362    /// set.insert(42).unwrap();
363    ///
364    /// assert_eq!(set.find(&42), Some(&42));
365    /// assert_eq!(set.find(&99), None);
366    /// ```
367    pub fn find<Q>(&self, item: &Q) -> Option<&T>
368    where
369        T: PartialEq<Q>,
370    {
371        self.iter().find(|i| *i == item)
372    }
373
374    /// Returns the first item equal to `item` in the set (mutable reference).
375    ///
376    /// # Examples
377    ///
378    /// ```rust
379    /// use satellite_bitcoin::generic::fixed_set::FixedSet;
380    ///
381    /// let mut set: FixedSet<u32, 10> = FixedSet::new();
382    /// set.insert(42).unwrap();
383    ///
384    /// if let Some(item) = set.find_mut(&42) {
385    ///     *item = 100;
386    /// }
387    ///
388    /// assert!(set.contains(&100));
389    /// assert!(!set.contains(&42));
390    /// ```
391    pub fn find_mut<Q>(&mut self, item: &Q) -> Option<&mut T>
392    where
393        T: PartialEq<Q>,
394    {
395        self.iter_mut().find(|i| *i == item)
396    }
397
398    /// Attempts to insert `item` into the set.
399    ///
400    /// # Errors
401    ///
402    /// * Returns `Err(FixedSetError::Duplicate)` if the item already exists in the set.
403    /// * Returns `Err(FixedSetError::Full)` if the set is at full capacity.
404    ///
405    /// # Examples
406    ///
407    /// ```rust
408    /// use satellite_bitcoin::generic::fixed_set::{FixedSet, FixedSetError};
409    ///
410    /// let mut set: FixedSet<u32, 2> = FixedSet::new();
411    /// assert!(set.insert(1).is_ok());
412    /// assert!(set.insert(2).is_ok());
413    ///
414    /// // Duplicate
415    /// assert_eq!(set.insert(1), Err(FixedSetError::Duplicate));
416    /// // Full
417    /// assert_eq!(set.insert(3), Err(FixedSetError::Full));
418    /// ```
419    pub fn insert(&mut self, item: T) -> Result<(), FixedSetError> {
420        if self.contains(&item) {
421            return Err(FixedSetError::Duplicate);
422        }
423        if self.len >= SIZE {
424            return Err(FixedSetError::Full);
425        }
426        self.items[self.len] = item;
427        self.len += 1;
428        Ok(())
429    }
430
431    /// Removes an item equal to `item` from the set and returns it.
432    ///
433    /// Uses swap-remove for O(1) performance, which means element order is not preserved.
434    ///
435    /// # Examples
436    ///
437    /// ```rust
438    /// use satellite_bitcoin::generic::fixed_set::FixedSet;
439    ///
440    /// let mut set: FixedSet<u32, 10> = FixedSet::new();
441    /// set.insert(42).unwrap();
442    ///
443    /// assert_eq!(set.remove(&42), Some(42));
444    /// assert_eq!(set.remove(&42), None);
445    /// assert_eq!(set.len(), 0);
446    /// ```
447    pub fn remove<Q>(&mut self, item: &Q) -> Option<T>
448    where
449        T: PartialEq<Q>,
450    {
451        // Search for the item index without creating an iterator that borrows `self`.
452        let pos_opt = (0..self.len).find(|&i| self.items[i] == *item);
453        if let Some(pos) = pos_opt {
454            // Swap-remove: replace the removed element with the last one to keep O(1) removal.
455            let removed = self.items[pos];
456            self.len -= 1;
457            if pos != self.len {
458                self.items[pos] = self.items[self.len];
459            }
460            Some(removed)
461        } else {
462            None
463        }
464    }
465
466    /// Removes and returns an arbitrary element from the set (the last one).
467    pub fn pop(&mut self) -> Option<T> {
468        if self.len > 0 {
469            self.len -= 1;
470            Some(self.items[self.len])
471        } else {
472            None
473        }
474    }
475
476    /// Returns the elements of the set as a slice.
477    pub fn as_slice(&self) -> &[T] {
478        &self.items[..self.len]
479    }
480
481    /// Returns the elements of the set as a mutable slice.
482    pub fn as_mut_slice(&mut self) -> &mut [T] {
483        &mut self.items[..self.len]
484    }
485
486    /// Creates a [`FixedSet`] from any iterator, returning an error if the
487    /// number of unique elements exceeds the set's capacity.
488    ///
489    /// Duplicate elements yielded by the iterator are ignored (because a set
490    /// cannot contain the same value twice). However, if inserting additional
491    /// *unique* elements would exceed the compile-time capacity `SIZE`, the
492    /// construction fails with [`FixedSetError::Full`].
493    pub fn try_from_iter<I>(iter: I) -> Result<Self, FixedSetError>
494    where
495        I: IntoIterator<Item = T>,
496    {
497        let mut set = Self::new();
498        for item in iter {
499            match set.insert(item) {
500                Ok(()) | Err(FixedSetError::Duplicate) => {}
501                Err(FixedSetError::Full) => return Err(FixedSetError::Full),
502            }
503        }
504        Ok(set)
505    }
506
507    /// Returns a reference to the **first** element in the set (if any).
508    ///
509    /// This is a convenience helper useful when the set is known to hold at
510    /// most one element (e.g. single‐rune outputs). It avoids having to call
511    /// `as_slice().first()` at every call-site.
512    #[inline]
513    pub fn get(&self) -> Option<&T> {
514        self.as_slice().first()
515    }
516}
517
518impl<T: Default + Copy + PartialEq, const SIZE: usize> PushPopCollection<T> for FixedSet<T, SIZE> {
519    fn push(&mut self, item: T) -> Result<(), PushPopError> {
520        match self.insert(item) {
521            Ok(()) => Ok(()),
522            Err(FixedSetError::Full) => Err(PushPopError::Full),
523            // Treat duplicate insertions as a no-op success for the generic API.
524            Err(FixedSetError::Duplicate) => Ok(()),
525        }
526    }
527
528    fn pop(&mut self) -> Option<T> {
529        self.pop()
530    }
531
532    fn as_slice(&self) -> &[T] {
533        self.as_slice()
534    }
535
536    fn len(&self) -> usize {
537        self.len()
538    }
539
540    fn max_size(&self) -> usize {
541        SIZE
542    }
543}
544
545impl<T: Default + Copy + PartialEq + Debug, const SIZE: usize> FixedCapacitySet
546    for FixedSet<T, SIZE>
547{
548    type Item = T;
549
550    fn capacity(&self) -> usize {
551        SIZE
552    }
553
554    fn len(&self) -> usize {
555        self.len()
556    }
557
558    fn contains<Q>(&self, item: &Q) -> bool
559    where
560        Self::Item: PartialEq<Q>,
561    {
562        self.contains(item)
563    }
564
565    fn find<Q>(&self, item: &Q) -> Option<&Self::Item>
566    where
567        Self::Item: PartialEq<Q>,
568    {
569        self.find(item)
570    }
571
572    fn find_mut<Q>(&mut self, item: &Q) -> Option<&mut Self::Item>
573    where
574        Self::Item: PartialEq<Q>,
575    {
576        self.find_mut(item)
577    }
578
579    fn insert(&mut self, item: Self::Item) -> Result<(), FixedSetError> {
580        self.insert(item)
581    }
582
583    fn insert_or_modify<E, F>(&mut self, item: Self::Item, modify: F) -> Result<(), E>
584    where
585        F: FnMut(&mut Self::Item) -> Result<(), E>,
586        E: From<FixedSetError>,
587    {
588        self.insert_or_modify(item, modify)
589    }
590
591    fn remove<Q>(&mut self, item: &Q) -> Option<Self::Item>
592    where
593        Self::Item: PartialEq<Q>,
594    {
595        self.remove(item)
596    }
597
598    fn as_slice(&self) -> &[Self::Item] {
599        self.as_slice()
600    }
601
602    fn as_mut_slice(&mut self) -> &mut [Self::Item] {
603        self.as_mut_slice()
604    }
605}
606
607// SAFETY: FixedSet is `Pod` if its items are `Pod` and it is `repr(C)` with
608// no padding between fields that contain invalid bytes. The `items` array is
609// `Pod` when `T` is `Pod`. Both `usize` and arrays of `Pod` types are `Pod`,
610// so the struct as a whole is `Pod`.
611unsafe impl<T: bytemuck::Pod, const SIZE: usize> bytemuck::Pod for FixedSet<T, SIZE> {}
612
613// SAFETY: A `Zeroable` type must have all-bits-zero represent a valid value.
614// Because `FixedSet::default()` sets `len` to 0 and the `items` array to all
615// `T::default()`, zeroing the entire struct is sound when `T` itself is
616// `Zeroable` (all-bits-zero is a valid representation for `T`).
617unsafe impl<T: bytemuck::Zeroable, const SIZE: usize> bytemuck::Zeroable for FixedSet<T, SIZE> {}
618
619// Conversions to/from FixedList
620impl<T: Default + Copy + PartialEq, const SIZE: usize>
621    From<crate::generic::fixed_list::FixedList<T, SIZE>> for FixedSet<T, SIZE>
622{
623    fn from(list: crate::generic::fixed_list::FixedList<T, SIZE>) -> Self {
624        let mut set = Self::new();
625        for item in list.as_slice().iter().copied() {
626            let _ = set.insert(item);
627        }
628        set
629    }
630}
631
632// Try to build a FixedSet from a slice. Deduplicates; errors on overflow of unique elements.
633impl<T: Default + Copy + PartialEq, const SIZE: usize> core::convert::TryFrom<&[T]>
634    for FixedSet<T, SIZE>
635{
636    type Error = FixedSetError;
637
638    fn try_from(slice: &[T]) -> Result<Self, Self::Error> {
639        let mut set = Self::new();
640        for &item in slice.iter() {
641            match set.insert(item) {
642                Ok(()) | Err(FixedSetError::Duplicate) => {}
643                Err(FixedSetError::Full) => return Err(FixedSetError::Full),
644            }
645        }
646        Ok(set)
647    }
648}
649
650#[cfg(test)]
651mod from_slice_tests {
652    use super::*;
653
654    #[test]
655    fn set_try_from_slice_dedups_and_caps() {
656        let data = [1u32, 2, 2, 3, 4];
657        let set = <FixedSet<u32, 3>>::try_from(&data[..]).unwrap();
658        assert_eq!(set.len(), 3);
659        assert!(set.contains(&1) && set.contains(&2) && set.contains(&3));
660    }
661
662    #[test]
663    fn set_try_from_slice_errors_on_overflow() {
664        let data = [1u32, 2, 3, 4];
665        let res = <FixedSet<u32, 3>>::try_from(&data[..]);
666        assert!(matches!(res, Err(FixedSetError::Full)));
667    }
668}
669
670#[cfg(test)]
671mod tests {
672    use super::*;
673
674    #[test]
675    fn test_default_is_empty() {
676        let set = FixedSet::<u32, 4>::default();
677        assert_eq!(set.len(), 0);
678        assert!(set.is_empty());
679    }
680
681    #[test]
682    fn test_insert_and_len() {
683        let mut set = FixedSet::<u32, 3>::new();
684        assert!(set.insert(10).is_ok());
685        assert!(set.insert(20).is_ok());
686        assert_eq!(set.len(), 2);
687        assert!(!set.is_empty());
688        assert!(set.contains(&10));
689        assert!(set.contains(&20));
690    }
691
692    #[test]
693    fn test_duplicate_insertion() {
694        let mut set = FixedSet::<u32, 2>::new();
695        assert!(set.insert(1).is_ok());
696        assert_eq!(set.insert(1), Err(FixedSetError::Duplicate));
697        assert_eq!(set.len(), 1);
698    }
699
700    #[test]
701    fn test_insert_past_capacity() {
702        let mut set = FixedSet::<u32, 2>::new();
703        assert!(set.insert(1).is_ok());
704        assert!(set.insert(2).is_ok());
705        assert_eq!(set.insert(3), Err(FixedSetError::Full));
706        assert_eq!(set.len(), 2);
707    }
708
709    #[test]
710    fn test_remove() {
711        let mut set = FixedSet::<u32, 3>::new();
712        set.insert(5).unwrap();
713        set.insert(10).unwrap();
714        assert_eq!(set.remove(&5), Some(5));
715        assert!(!set.contains(&5));
716        assert_eq!(set.len(), 1);
717    }
718
719    #[test]
720    fn test_pop() {
721        let mut set = FixedSet::<u32, 2>::new();
722        assert_eq!(set.pop(), None);
723
724        set.insert(100).unwrap();
725        set.insert(200).unwrap();
726        let first = set.pop();
727        let second = set.pop();
728        assert!(matches!(
729            (first, second),
730            (Some(200), Some(100)) | (Some(100), Some(200))
731        ));
732        assert!(set.is_empty());
733    }
734
735    #[test]
736    fn test_push_pop_collection_trait() {
737        let mut set = FixedSet::<u8, 2>::new();
738        PushPopCollection::push(&mut set, 1).unwrap();
739        PushPopCollection::push(&mut set, 2).unwrap();
740        PushPopCollection::push(&mut set, 2).unwrap(); // duplicate ignored
741        assert_eq!(PushPopCollection::len(&set), 2);
742        let slice = PushPopCollection::as_slice(&set);
743        assert!(slice.contains(&1) && slice.contains(&2));
744        PushPopCollection::pop(&mut set);
745        PushPopCollection::pop(&mut set);
746        assert_eq!(PushPopCollection::pop(&mut set), None);
747    }
748
749    #[test]
750    fn test_try_from_iter() {
751        // Successful construction within capacity
752        let data = vec![1, 2, 3];
753        let set = FixedSet::<u32, 3>::try_from_iter(data).unwrap();
754        assert_eq!(set.len(), 3);
755        assert!(set.contains(&1) && set.contains(&2) && set.contains(&3));
756
757        // Construction that exceeds capacity should error
758        let too_many = vec![1, 2, 3, 4];
759        let res = FixedSet::<u32, 3>::try_from_iter(too_many);
760        assert!(matches!(res, Err(FixedSetError::Full)));
761    }
762
763    #[test]
764    fn test_generic_search_methods() {
765        // Create a simple type that implements PartialEq with another type
766        #[derive(Debug, Clone, Copy, PartialEq, Default)]
767        struct SimpleItem {
768            id: u32,
769            value: u32,
770        }
771
772        #[derive(Debug, Clone, Copy, PartialEq)]
773        struct SimpleItemId(u32);
774
775        impl PartialEq<SimpleItemId> for SimpleItem {
776            fn eq(&self, other: &SimpleItemId) -> bool {
777                self.id == other.0
778            }
779        }
780
781        let mut set = FixedSet::<SimpleItem, 3>::new();
782        let item1 = SimpleItem { id: 1, value: 10 };
783        let item2 = SimpleItem { id: 2, value: 20 };
784
785        set.insert(item1).unwrap();
786        set.insert(item2).unwrap();
787
788        let search_id = SimpleItemId(1);
789
790        // Test contains
791        assert!(set.contains(&search_id));
792        assert!(!set.contains(&SimpleItemId(999)));
793
794        // Test find
795        let found = set.find(&search_id);
796        assert_eq!(found, Some(&item1));
797
798        // Test find_mut
799        let found_mut = set.find_mut(&search_id);
800        assert!(found_mut.is_some());
801        if let Some(found_item) = found_mut {
802            found_item.value = 999;
803        }
804
805        // Verify the modification worked
806        let found_again = set.find(&search_id);
807        assert_eq!(found_again.unwrap().value, 999);
808
809        // Test remove
810        let removed = set.remove(&SimpleItemId(2));
811        assert_eq!(removed, Some(item2));
812        assert!(!set.contains(&SimpleItemId(2)));
813        assert_eq!(set.len(), 1);
814    }
815}