use std::borrow::Borrow;
use std::collections::hash_set::Drain;
use std::collections::{HashSet, TryReserveError};
use std::hash::Hash;
use std::ops::{Deref, DerefMut};
use crate::helper::macros::{default_impl_ref_observe, delegate_methods, shallow_observer};
use crate::helper::{AsDerefMut, QuasiObserver, Unsigned};
shallow_observer! {
struct [T] HashSetObserver(HashSet<T>);
}
default_impl_ref_observe! {
impl [T] RefObserve for HashSet<T>;
}
struct LenGuard<'a, T> {
old_len: usize,
mutated: &'a mut bool,
inner: &'a mut HashSet<T>,
}
impl<T> Drop for LenGuard<'_, T> {
fn drop(&mut self) {
if self.old_len != self.inner.len() {
*self.mutated = true;
}
}
}
impl<T> Deref for LenGuard<'_, T> {
type Target = HashSet<T>;
fn deref(&self) -> &Self::Target {
self.inner
}
}
impl<T> DerefMut for LenGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner
}
}
impl<'ob, S: ?Sized, D, T> HashSetObserver<'ob, S, D>
where
D: Unsigned,
S: AsDerefMut<D, Target = HashSet<T>>,
{
fn nonempty_mut(&mut self) -> &mut HashSet<T> {
if (*self).untracked_ref().is_empty() {
self.untracked_mut()
} else {
self.tracked_mut()
}
}
fn guarded_mut(&mut self) -> LenGuard<'_, T> {
let inner = (*self.ptr).as_deref_mut();
LenGuard {
old_len: inner.len(),
mutated: &mut self.mutated,
inner,
}
}
delegate_methods! { nonempty_mut() as HashSet =>
pub fn drain(&mut self) -> Drain<'_, T>;
pub fn clear(&mut self);
}
delegate_methods! { guarded_mut() as HashSet =>
pub fn retain<F>(&mut self, f: F) where F: FnMut(&T) -> bool;
}
pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, T, F>
where
F: FnMut(&T) -> bool,
{
ExtractIf {
inner: (*self.ptr).as_deref_mut().extract_if(pred),
mutated: &mut self.mutated,
}
}
}
impl<'ob, S: ?Sized, D, T> HashSetObserver<'ob, S, D>
where
D: Unsigned,
S: AsDerefMut<D, Target = HashSet<T>>,
T: Eq + Hash,
{
delegate_methods! { untracked_mut() as HashSet =>
pub fn reserve(&mut self, additional: usize);
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>;
pub fn shrink_to_fit(&mut self);
pub fn shrink_to(&mut self, min_capacity: usize);
}
delegate_methods! { tracked_mut() as HashSet =>
pub fn replace(&mut self, value: T) -> Option<T>;
}
delegate_methods! { guarded_mut() as HashSet =>
pub fn insert(&mut self, value: T) -> bool;
pub fn remove<Q>(&mut self, value: &Q) -> bool where T: Borrow<Q>, Q: Hash + Eq + ?Sized;
pub fn take<Q>(&mut self, value: &Q) -> Option<T> where T: Borrow<Q>, Q: Hash + Eq + ?Sized;
}
}
impl<'ob, S: ?Sized, D, T, U> Extend<U> for HashSetObserver<'ob, S, D>
where
D: Unsigned,
S: AsDerefMut<D, Target = HashSet<T>>,
HashSet<T>: Extend<U>,
{
fn extend<I: IntoIterator<Item = U>>(&mut self, iter: I) {
self.guarded_mut().extend(iter)
}
}
pub struct ExtractIf<'a, T, F> {
inner: std::collections::hash_set::ExtractIf<'a, T, F>,
mutated: &'a mut bool,
}
impl<T, F: FnMut(&T) -> bool> Iterator for ExtractIf<'_, T, F> {
type Item = T;
fn next(&mut self) -> Option<T> {
let result = self.inner.next();
if result.is_some() {
*self.mutated = true;
}
result
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use serde_json::Value;
use crate::adapter::Json;
use crate::helper::QuasiObserver;
use crate::observe::{ObserveExt, SerializeObserverExt};
use crate::{Mutation, MutationKind};
fn is_replace(mutation: &Option<Mutation<Value>>) -> bool {
match mutation {
Some(m) => m.path.is_empty() && matches!(m.kind, MutationKind::Replace(_)),
None => false,
}
}
#[test]
fn no_change() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn insert_triggers_replace() {
let mut set = HashSet::from([1, 2]);
let mut ob = set.__observe();
ob.insert(3);
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
}
#[test]
fn insert_duplicate_no_mutation() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
assert!(!ob.insert(2));
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn remove_existing_triggers_replace() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
assert!(ob.remove(&2));
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
}
#[test]
fn remove_nonexistent_no_mutation() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
assert!(!ob.remove(&99));
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn take_triggers_replace() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
assert_eq!(ob.take(&2), Some(2));
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
}
#[test]
fn take_nonexistent_no_mutation() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
assert_eq!(ob.take(&99), None);
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn clear_empty_no_mutation() {
let mut set: HashSet<i32> = HashSet::new();
let mut ob = set.__observe();
ob.clear();
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn clear_non_empty_triggers_replace() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
ob.clear();
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
}
#[test]
fn double_flush() {
let mut set = HashSet::from([1, 2]);
let mut ob = set.__observe();
ob.insert(3);
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn reserve_no_mutation() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
ob.reserve(100);
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn extend_triggers_replace() {
let mut set = HashSet::from([1]);
let mut ob = set.__observe();
ob.extend([2, 3, 4]);
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
}
#[test]
fn extend_duplicates_no_mutation() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
ob.extend([1, 2, 3]);
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn deref_mut_triggers_replace() {
let mut set = HashSet::from([1, 2]);
let mut ob = set.__observe();
**ob = HashSet::from([10, 20, 30]);
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
}
#[test]
fn retain_triggers_replace() {
let mut set = HashSet::from([1, 2, 3, 4]);
let mut ob = set.__observe();
ob.retain(|&x| x % 2 == 0);
assert_eq!(*ob.untracked_ref(), HashSet::from([2, 4]));
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
}
#[test]
fn retain_all_no_mutation() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
ob.retain(|_| true);
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn extract_if_triggers_replace() {
let mut set = HashSet::from([1, 2, 3, 4]);
let mut ob = set.__observe();
let extracted: HashSet<_> = ob.extract_if(|&x| x % 2 == 0).collect();
assert_eq!(extracted, HashSet::from([2, 4]));
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
}
#[test]
fn extract_if_none_no_mutation() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
let extracted: Vec<_> = ob.extract_if(|_| false).collect();
assert!(extracted.is_empty());
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn extract_if_no_consume_no_mutation() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
let _ = ob.extract_if(|_| true);
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn drain_empty_no_mutation() {
let mut set: HashSet<i32> = HashSet::new();
let mut ob = set.__observe();
let drained: Vec<_> = ob.drain().collect();
assert!(drained.is_empty());
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn replace_triggers_replace() {
let mut set = HashSet::from([1, 2, 3]);
let mut ob = set.__observe();
ob.replace(2);
let Json(mutation) = ob.flush().unwrap();
assert!(is_replace(&mutation));
}
}