hashset_ext 0.1.0

Extension for HashSet with intuitive, chainable Python-like set operations.
Documentation
use std::collections::HashSet;
use std::ops::{BitOr, BitAnd, BitXor, Sub, BitOrAssign, BitAndAssign, BitXorAssign, SubAssign};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HashSetExt<T: Eq + std::hash::Hash>(pub HashSet<T>);

impl<T: Eq + std::hash::Hash> From<Vec<T>> for HashSetExt<T> {
    fn from(vec: Vec<T>) -> Self {
        HashSetExt(vec.into_iter().collect())
    }
}

impl<T: Eq + std::hash::Hash> From<HashSet<T>> for HashSetExt<T> {
    fn from(set: HashSet<T>) -> Self {
        HashSetExt(set)
    }
}

// ----------------- UNION (|) -----------------
impl<'a, 'b, T: Eq + std::hash::Hash + Clone> BitOr<&'b HashSetExt<T>> for &'a HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn bitor(self, other: &'b HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.union(&other.0).cloned().collect())
    }
}

impl<'a, T: Eq + std::hash::Hash + Clone> BitOr<HashSetExt<T>> for &'a HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn bitor(self, other: HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.union(&other.0).cloned().collect())
    }
}

impl<'b, T: Eq + std::hash::Hash + Clone> BitOr<&'b HashSetExt<T>> for HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn bitor(self, other: &'b HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.union(&other.0).cloned().collect())
    }
}

// ----------------- INTERSECTION (&) -----------------
impl<'a, 'b, T: Eq + std::hash::Hash + Clone> BitAnd<&'b HashSetExt<T>> for &'a HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn bitand(self, other: &'b HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.intersection(&other.0).cloned().collect())
    }
}

impl<'a, T: Eq + std::hash::Hash + Clone> BitAnd<HashSetExt<T>> for &'a HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn bitand(self, other: HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.intersection(&other.0).cloned().collect())
    }
}

impl<'b, T: Eq + std::hash::Hash + Clone> BitAnd<&'b HashSetExt<T>> for HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn bitand(self, other: &'b HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.intersection(&other.0).cloned().collect())
    }
}

// ----------------- DIFFERENCE (-) -----------------
impl<'a, 'b, T: Eq + std::hash::Hash + Clone> Sub<&'b HashSetExt<T>> for &'a HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn sub(self, other: &'b HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.difference(&other.0).cloned().collect())
    }
}

impl<'a, T: Eq + std::hash::Hash + Clone> Sub<HashSetExt<T>> for &'a HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn sub(self, other: HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.difference(&other.0).cloned().collect())
    }
}

impl<'b, T: Eq + std::hash::Hash + Clone> Sub<&'b HashSetExt<T>> for HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn sub(self, other: &'b HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.difference(&other.0).cloned().collect())
    }
}

// ----------------- SYMMETRIC DIFFERENCE (^) -----------------
impl<'a, 'b, T: Eq + std::hash::Hash + Clone> BitXor<&'b HashSetExt<T>> for &'a HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn bitxor(self, other: &'b HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.symmetric_difference(&other.0).cloned().collect())
    }
}

impl<'a, T: Eq + std::hash::Hash + Clone> BitXor<HashSetExt<T>> for &'a HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn bitxor(self, other: HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.symmetric_difference(&other.0).cloned().collect())
    }
}

impl<'b, T: Eq + std::hash::Hash + Clone> BitXor<&'b HashSetExt<T>> for HashSetExt<T> {
    type Output = HashSetExt<T>;
    fn bitxor(self, other: &'b HashSetExt<T>) -> Self::Output {
        HashSetExt(self.0.symmetric_difference(&other.0).cloned().collect())
    }
}

// ----------------- IN-PLACE ASSIGN -----------------
impl<T: Eq + std::hash::Hash + Clone> BitOrAssign for HashSetExt<T> {
    fn bitor_assign(&mut self, other: Self) {
        self.0.extend(other.0);
    }
}

impl<T: Eq + std::hash::Hash + Clone> BitAndAssign for HashSetExt<T> {
    fn bitand_assign(&mut self, other: Self) {
        self.0 = self.0.intersection(&other.0).cloned().collect();
    }
}

impl<T: Eq + std::hash::Hash + Clone> SubAssign for HashSetExt<T> {
    fn sub_assign(&mut self, other: Self) {
        self.0 = self.0.difference(&other.0).cloned().collect();
    }
}

impl<T: Eq + std::hash::Hash + Clone> BitXorAssign for HashSetExt<T> {
    fn bitxor_assign(&mut self, other: Self) {
        self.0 = self.0.symmetric_difference(&other.0).cloned().collect();
    }
}