nonempty_containers/
ne_set.rs

1//! A non-empty set type that guarantees at least one element is present. [NESet] has an
2//! interface similar to [HashSet] with additional methods to enforce the invariant. Get started
3//! with:
4//!
5//! ```rust, no_run
6//! # use nonempty_containers::{nes, NESet};
7//! #
8//! let nes = NESet::new(42, vec![1, 2, 3]);
9//! let singleton = NESet::singleton(42);
10//! let r#macro = nes![1, 2, 3];
11//! ```
12//!
13//! [NESet] conforms to [Index], [IntoIterator], [Deref], and many more, so operations are
14//! as [HashSet]-like as possible. They are also usually zero-cost.
15//!
16//! ```rust, no_run
17//! # use nonempty_containers::{nes, NESet};
18//! #
19//! let nes = nes![42, 1, 2, 3];
20//! assert!(nes.contains(&42));
21//! assert_eq!(nes.len(), 4);
22//! ```
23//!
24//! When the feature `arbitrary` is enabled, [NESet] implements [arbitrary::Arbitrary]
25//! for generation of randomly populated instances.
26
27use crate::errors::NonEmptyError;
28use std::collections::hash_set::{IntoIter, Iter};
29use std::collections::HashSet;
30use std::hash::Hash;
31
32/// Non-empty set type.
33#[derive(Debug, Eq, PartialEq, Clone)]
34pub struct NESet<T: Eq + Hash>(HashSet<T>);
35
36impl<T: Eq + Hash> NESet<T> {
37    /// Creates a new [NESet], ensuring at least one element is present.
38    pub fn new(head: T, tail: Vec<T>) -> Self {
39        let mut set = HashSet::with_capacity(1 + tail.len());
40        set.insert(head);
41        set.extend(tail);
42        Self(set)
43    }
44
45    /// Creates a new singleton [NESet]. Semantically equivalent to:
46    /// ```no_run
47    /// # use nonempty_containers::NESet;
48    /// # let value = 42;
49    /// #
50    /// NESet::new(value, Vec::new());
51    /// ```
52    pub fn singleton(value: T) -> Self {
53        let mut set = HashSet::new();
54        set.insert(value);
55        Self(set)
56    }
57
58    /// Creates a new [NESet] from a [HashSet]. Returns an error if the set is empty.
59    pub fn from(set: HashSet<T>) -> Result<Self, NonEmptyError> {
60        match set.is_empty() {
61            true => Err(NonEmptyError::Empty),
62            false => Ok(Self(set)),
63        }
64    }
65
66    /// Creates a new [NESet] from a [HashSet] without checking the invariant. This is unsafe
67    /// and should only be used by macros in this crate.
68    #[doc(hidden)]
69    pub fn __from_set_unsafe(set: HashSet<T>) -> Self {
70        debug_assert!(!set.is_empty());
71        Self(set)
72    }
73
74    /// Extracts the underlying [HashSet]. This operation is zero-cost.
75    pub fn into_set(self) -> HashSet<T> {
76        self.0
77    }
78
79    /// Returns the size of the set.
80    pub fn len(&self) -> usize {
81        self.0.len()
82    }
83
84    /// A [NESet] is always non-empty.
85    pub fn is_empty(&self) -> bool {
86        false
87    }
88
89    /// Adds an element to the set. If the element is already present, it is not modified.
90    pub fn insert(&mut self, value: T) -> bool {
91        self.0.insert(value)
92    }
93
94    /// Removes an element from the set. Returns `true` if the element was present.
95    pub fn remove(&mut self, value: &T) -> bool {
96        self.0.remove(value)
97    }
98
99    /// Checks if the set contains a value.
100    pub fn contains(&self, value: &T) -> bool {
101        self.0.contains(value)
102    }
103}
104
105impl<T: Eq + Hash> From<NESet<T>> for HashSet<T> {
106    fn from(value: NESet<T>) -> Self {
107        value.into_set()
108    }
109}
110
111impl<T: Eq + Hash> TryFrom<HashSet<T>> for NESet<T> {
112    type Error = NonEmptyError;
113
114    fn try_from(set: HashSet<T>) -> Result<Self, Self::Error> {
115        NESet::from(set)
116    }
117}
118
119impl<T: Eq + Hash> From<T> for NESet<T> {
120    fn from(value: T) -> Self {
121        Self::singleton(value)
122    }
123}
124
125impl<'a, T: Eq + Hash> IntoIterator for &'a NESet<T> {
126    type Item = &'a T;
127    type IntoIter = Iter<'a, T>;
128
129    fn into_iter(self) -> Self::IntoIter {
130        self.0.iter()
131    }
132}
133
134impl<T: Eq + Hash> IntoIterator for NESet<T> {
135    type Item = T;
136    type IntoIter = IntoIter<T>;
137
138    fn into_iter(self) -> Self::IntoIter {
139        self.0.into_iter()
140    }
141}