#![doc(hidden)]
use std::{hash::Hash, ops::Deref};
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(not(feature = "hashbrown"))]
use std::collections::HashMap;
use crate::{
collections::{rw::Editable, Retriever},
index::store::{Filterable, Store},
};
#[repr(transparent)]
#[derive(Debug)]
pub struct TriggerMap<I, X>(HashMap<X, I>);
impl<I, X> TriggerMap<I, X>
where
X: Hash + Eq,
{
pub fn with_capacity(capacity: usize) -> Self {
Self(HashMap::with_capacity(capacity))
}
#[inline]
pub fn get_mut(&mut self, index: &X) -> Option<&mut I> {
self.0.get_mut(index)
}
#[inline]
pub fn insert<Trigger>(&mut self, index: X, item: I, mut insert: Trigger) -> bool
where
X: Clone,
Trigger: FnMut(X, &I),
{
match self.0.get(&index) {
Some(_) => false, None => {
insert(index.clone(), &item);
self.0.insert(index, item);
true
}
}
}
#[inline]
pub fn remove<Trigger>(&mut self, index: &X, mut remove: Trigger) -> Option<I>
where
Trigger: FnMut(&X, &I),
{
let item = self.0.remove(index)?;
remove(index, &item);
Some(item)
}
}
impl<I, X> Deref for TriggerMap<I, X> {
type Target = HashMap<X, I>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug)]
pub struct Map<S, I, X, F> {
field: F,
store: S,
items: TriggerMap<I, X>,
}
impl<S, I, X, F> Map<S, I, X, F>
where
S: Store<Index = X>,
F: Fn(&I) -> S::Key,
X: Hash + Eq,
{
pub fn new(field: F) -> Self {
Self {
field,
store: S::with_capacity(0),
items: TriggerMap::with_capacity(0),
}
}
pub fn from_iter<It>(field: F, iter: It) -> Self
where
It: IntoIterator<Item = (X, I)> + ExactSizeIterator,
X: Clone,
{
let mut s = Self {
field,
store: S::with_capacity(iter.len()),
items: TriggerMap::with_capacity(iter.len()),
};
iter.into_iter().for_each(|(index, item)| {
s.insert(index, item);
});
s
}
pub fn insert(&mut self, index: X, item: I) -> bool
where
X: Clone,
{
self.items.insert(index, item, |index, item| {
self.store.insert((self.field)(item), index);
})
}
pub fn idx(&self) -> Retriever<'_, S, HashMap<X, I>> {
Retriever::new(&self.store, &self.items)
}
}
impl<S, I, X, F> Editable<I> for Map<S, I, X, F>
where
S: Store<Index = X>,
F: Fn(&I) -> S::Key,
X: Hash + Eq,
{
type Index = X;
fn update<U>(&mut self, index: X, mut update: U) -> Option<&I>
where
U: FnMut(&mut I),
{
self.items.get_mut(&index).map(|item| {
let key = (self.field)(item);
update(item);
self.store.update(key, index, (self.field)(item));
&*item
})
}
fn remove(&mut self, index: X) -> Option<I> {
self.items.remove(&index, |index, item| {
self.store.delete((self.field)(item), index);
})
}
}
impl<S, I, X, F> Filterable for Map<S, I, X, F>
where
S: Store<Index = X>,
{
type Key = S::Key;
type Index = S::Index;
fn contains(&self, key: &Self::Key) -> bool {
self.store.contains(key)
}
fn get(&self, key: &Self::Key) -> &[Self::Index] {
self.store.get(key)
}
}
impl<S, I, X, F> Deref for Map<S, I, X, F> {
type Target = HashMap<X, I>;
fn deref(&self) -> &Self::Target {
&self.items.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::index::IntIndex;
use rstest::{fixture, rstest};
#[derive(PartialEq, Debug, Clone)]
struct Person {
id: i32,
name: String,
}
impl Person {
fn new(id: i32, name: &str) -> Self {
Self {
id,
name: name.into(),
}
}
}
#[fixture]
fn v() -> HashMap<&'static str, Person> {
let mut m = HashMap::new();
m.insert("Paul", Person::new(0, "Paul"));
m.insert("Mario", Person::new(-2, "Mario"));
m.insert("Jasmin", Person::new(2, "Jasmin"));
m
}
#[rstest]
fn check_map(v: HashMap<&'static str, Person>) {
let mut m =
Map::<IntIndex<i32, &'static str>, Person, _, _>::from_iter(|p| p.id, v.into_iter());
assert!(m.insert("Mrs X", Person::new(-3, "Mrs X")));
assert!(m.idx().contains(&-2));
assert!(m.contains(&-3));
assert!(!m.idx().contains(&-1));
assert_eq!(4, m.len());
assert_eq!(Person::new(-3, "Mrs X"), m.remove("Mrs X").unwrap());
assert!(!m.contains(&-3));
assert_eq!(3, m.len());
assert_eq!(Some(&Person::new(2, "Jasmin")), m.idx().get(&2).next());
assert_eq!(
Some(&Person::new(2, "Jasmin 2")),
m.update("Jasmin", |p| {
p.name = String::from("Jasmin 2");
})
);
assert_eq!(Some(&Person::new(2, "Jasmin 2")), m.idx().get(&2).next());
assert_eq!(["Jasmin"], m.get(&2));
}
#[test]
fn invalid_insert() {
let mut m = Map::<IntIndex<i32, &'static str>, Person, _, _>::new(|p| p.id);
assert!(m.insert("Mrs X", Person::new(-3, "Mrs X")));
assert!(!m.insert("Mrs X", Person::new(-3, "Mrs X")));
assert_eq!(1, m.len());
}
}