#![warn(clippy::all, clippy::pedantic)]
use std::{
collections::HashMap,
hash::{BuildHasherDefault, Hash},
};
use nohash_hasher::NoHashHasher;
pub use variant_set_derive::VariantEnum;
pub trait VariantEnum {
type Variant: Copy + Eq + Hash;
fn variant(&self) -> Self::Variant;
}
pub struct VariantSet<T>
where
T: VariantEnum,
{
data: HashMap<T::Variant, T, BuildHasherDefault<NoHashHasher<usize>>>,
}
impl<T> VariantSet<T>
where
T: VariantEnum,
{
#[must_use]
pub fn new() -> Self {
Self {
data: HashMap::with_hasher(BuildHasherDefault::default()),
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: HashMap::with_capacity_and_hasher(capacity, BuildHasherDefault::default()),
}
}
#[must_use]
pub fn capacity(&self) -> usize {
self.data.capacity()
}
pub fn clear(&mut self) {
self.data.clear();
}
pub fn insert(&mut self, value: T) -> bool {
if let std::collections::hash_map::Entry::Vacant(entry) = self.data.entry(value.variant()) {
entry.insert(value);
true
} else {
false
}
}
pub fn set(&mut self, value: T) -> Option<T> {
self.data.insert(value.variant(), value)
}
pub fn contains(&self, value: T::Variant) -> bool {
self.data.contains_key(&value)
}
pub fn contains_exact(&self, value: &T) -> bool
where
T: PartialEq,
{
matches!(self.data.get(&value.variant()), Some(v) if v == value)
}
pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
self.data.drain().map(|(_, value)| value)
}
pub fn get(&self, value: T::Variant) -> Option<&T> {
self.data.get(&value)
}
pub fn get_or_insert(&mut self, default: T) -> &T {
self.data.entry(default.variant()).or_insert(default)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.data.values()
}
#[must_use]
pub fn len(&self) -> usize {
self.data.len()
}
pub fn remove(&mut self, value: T::Variant) -> Option<T> {
self.data.remove(&value)
}
pub fn remove_exact(&mut self, value: &T) -> Option<T>
where
T: PartialEq,
{
match self.data.get(&value.variant()) {
Some(v) if v == value => self.data.remove(&value.variant()),
_ => None,
}
}
pub fn reserve(&mut self, additional: usize) {
self.data.reserve(additional);
}
pub fn try_reserve(
&mut self,
additional: usize,
) -> Result<(), std::collections::TryReserveError> {
self.data.try_reserve(additional)
}
pub fn shrink_to(&mut self, min_capacity: usize) {
self.data.shrink_to(min_capacity);
}
pub fn shrink_to_fit(&mut self) {
self.data.shrink_to_fit();
}
pub fn take(&mut self, value: T::Variant) -> Option<T> {
self.data.remove(&value)
}
}
impl<T> Default for VariantSet<T>
where
T: VariantEnum,
{
fn default() -> Self {
Self::new()
}
}
impl<T> Clone for VariantSet<T>
where
T: VariantEnum + Clone,
{
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
}
}
}
impl<T> std::fmt::Debug for VariantSet<T>
where
T: VariantEnum + std::fmt::Debug,
T::Variant: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_map().finish()
}
}
impl<T> PartialEq for VariantSet<T>
where
T: VariantEnum + PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl<T> Eq for VariantSet<T> where T: VariantEnum + Eq {}
impl<T> Extend<T> for VariantSet<T>
where
T: VariantEnum,
{
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for value in iter {
self.set(value);
}
}
}
impl<T> IntoIterator for VariantSet<T>
where
T: VariantEnum,
{
type Item = T;
type IntoIter = std::collections::hash_map::IntoValues<T::Variant, T>;
fn into_iter(self) -> Self::IntoIter {
self.data.into_values()
}
}
impl<T> FromIterator<T> for VariantSet<T>
where
T: VariantEnum,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut set = VariantSet::new();
set.extend(iter);
set
}
}
impl<T, const N: usize> From<[T; N]> for VariantSet<T>
where
T: VariantEnum,
{
fn from(array: [T; N]) -> Self {
Self::from_iter(array)
}
}