use crate::{
collections::map::ro,
lookup::store::{position::KeyPosition, Lookup, Retriever, Store},
};
use std::{hash::Hash, ops::Deref};
#[derive(Debug, Clone)]
pub struct LkupHashMap<S, F, K, V> {
field: F,
inner: ro::LkupHashMap<S, K, V>,
}
impl<S, F, K, V> LkupHashMap<S, F, K, V>
where
S: Store<Pos = K>,
F: Fn(&V) -> S::Key,
{
pub fn new<L, P>(lookup: L, field: F) -> Self
where
L: Lookup<S, P>,
P: KeyPosition<Pos = K>,
K: Clone,
{
Self {
inner: ro::LkupHashMap::new(lookup, &field, ro::HashMap::new()),
field,
}
}
}
impl<S, F, K, V> Deref for LkupHashMap<S, F, K, V> {
type Target = ro::LkupHashMap<S, K, V>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<S, F, K, V> LkupHashMap<S, F, K, V>
where
S: Store<Pos = K>,
F: Fn(&V) -> S::Key,
K: Hash + Eq,
{
pub fn insert(&mut self, key: K, item: V) -> Option<V>
where
K: Hash + Eq + Clone,
{
self.inner.store.insert((self.field)(&item), key.clone());
self.inner.items.insert(key, item)
}
pub fn update<U>(&mut self, key: K, mut update: U) -> Option<&V>
where
U: FnMut(&mut V),
{
let v = self.inner.items.get_mut(&key)?;
let old_key = (self.field)(v);
update(v);
self.inner.store.update(old_key, key, (self.field)(v));
Some(v)
}
pub fn remove(&mut self, key: K) -> Option<V> {
let removed = self.inner.items.remove(&key)?;
self.inner.store.delete((self.field)(&removed), &key);
Some(removed)
}
pub fn update_by_key<Q, U>(&mut self, key: Q, mut update: U) -> usize
where
S: Retriever<Q, Pos = K>,
U: FnMut(&mut V),
K: Clone,
{
let mut update_count = 0;
#[allow(clippy::unnecessary_to_owned)]
for idx in self.store.pos_by_key(key).to_vec() {
if self.update(idx, &mut update).is_some() {
update_count += 1;
}
}
update_count
}
pub fn remove_by_key<Q>(&mut self, key: Q) -> usize
where
S: Retriever<Q, Pos = K>,
Q: Clone,
K: Clone,
{
let mut remove_count = 0;
while let Some(idx) = self.store.pos_by_key(key.clone()).iter().next() {
if self.remove(idx.clone()).is_some() {
remove_count += 1;
}
}
remove_count
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{HashLookup, IndexLookup};
#[derive(Debug, PartialEq)]
struct Car(u16, String);
#[test]
fn map_key_string() {
let mut m = LkupHashMap::new(IndexLookup::with_multi_keys(), |c: &Car| c.0);
m.insert(String::from("Audi"), Car(99, String::from("Audi")));
m.insert(String::from("BMW"), Car(1, String::from("BMW")));
assert!(m.contains_key("BMW"));
assert!(m.contains_lkup_key(1));
assert!(!m.contains_lkup_key(1_000));
m.lkup_ext()
.keys()
.for_each(|key| assert!(m.contains_lkup_key(key)));
assert_eq!(
Some(&Car(1_000, String::from("BMW"))),
m.update(String::from("BMW"), |c| c.0 = 1_000)
);
assert!(m.contains_lkup_key(1_000));
assert_eq!(None, m.update("NotFound".into(), |_c| {}));
assert_eq!(1, m.update_by_key(1_000, |c| c.0 = 1));
assert_eq!(0, m.update_by_key(1_000, |c| c.0 = 1_000));
assert_eq!(1, m.update_by_key(1, |c| c.0 = 1_000));
assert_eq!(2, m.len());
assert_eq!(
Some(Car(1_000, String::from("BMW"))),
m.remove("BMW".into())
);
assert!(!m.contains_key("BMW"));
assert!(!m.contains_lkup_key(1_000));
assert_eq!(1, m.len());
assert_eq!(0, m.remove_by_key(2));
assert_eq!(1, m.len());
assert_eq!(1, m.remove_by_key(99));
assert_eq!(0, m.len());
}
#[test]
fn map_key_usize() {
let mut m = LkupHashMap::new(HashLookup::with_unique_key(), |c: &Car| c.1.clone());
m.insert(99, Car(99, String::from("Audi")));
m.insert(1, Car(1, String::from("BMW")));
assert!(m.contains_key(&1));
assert!(m.contains_lkup_key("BMW"));
assert!(!m.contains_lkup_key("NOT FOUND"));
assert_eq!(&Car(99, "Audi".into()), &m[&99]);
m.lkup_ext()
.keys()
.for_each(|key| assert!(m.contains_lkup_key(key)));
assert_eq!(
Some(&Car(1, String::from("VW"))),
m.update(1, |c| c.1 = String::from("VW"))
);
assert!(m.contains_lkup_key("VW"));
assert_eq!(None, m.update(1_000, |_c| {}));
assert_eq!(2, m.len());
assert_eq!(Some(Car(1, String::from("VW"))), m.remove(1));
assert!(!m.contains_key(&1));
assert!(!m.contains_lkup_key("VW"));
assert_eq!(1, m.len());
}
}