#[cfg(feature = "indexmap")]
use indexmap::Equivalent;
use std::{
borrow::Borrow,
collections::{BTreeMap, HashMap},
hash::{BuildHasher, Hash},
};
use thiserror::Error;
#[cfg(feature = "indexmap")]
pub use indexmap::IndexMap;
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ReplaceKeyErr {
#[error("replace_key: the old key does not exist")]
OldKeyNotExist,
#[error("replace_key: the new key is already occupied")]
NewKeyOccupied,
}
pub trait MapExt<K, Q: ?Sized = K> {
fn replace_key(&mut self, k1: &Q, k2: K) -> Result<(), ReplaceKeyErr>
where
K: Borrow<Q>;
}
impl<K, Q, V, S> MapExt<K, Q> for HashMap<K, V, S>
where
K: Eq + Hash + Borrow<Q>,
Q: Hash + Eq + ?Sized,
S: BuildHasher,
{
fn replace_key(&mut self, k1: &Q, k2: K) -> Result<(), ReplaceKeyErr> {
if !self.contains_key(k1) {
return Err(ReplaceKeyErr::OldKeyNotExist);
}
if k1 == k2.borrow() {
return Ok(());
}
if self.contains_key(k2.borrow()) {
return Err(ReplaceKeyErr::NewKeyOccupied);
}
let v = self.remove(k1).expect("this should be unreachable");
self.insert(k2, v);
Ok(())
}
}
impl<K, Q, V> MapExt<K, Q> for BTreeMap<K, V>
where
K: Borrow<Q> + Ord,
Q: Ord + ?Sized,
{
fn replace_key(&mut self, k1: &Q, k2: K) -> Result<(), ReplaceKeyErr> {
if !self.contains_key(k1) {
return Err(ReplaceKeyErr::OldKeyNotExist);
}
if k1 == k2.borrow() {
return Ok(());
}
if self.contains_key(k2.borrow()) {
return Err(ReplaceKeyErr::NewKeyOccupied);
}
let v = self.remove(k1).expect("this should be unreachable");
self.insert(k2, v);
Ok(())
}
}
#[cfg(feature = "indexmap")]
impl<K, Q, V, S> MapExt<K, Q> for IndexMap<K, V, S>
where
K: Borrow<Q> + Hash + Eq,
Q: ?Sized + Hash + Equivalent<K>,
S: BuildHasher,
{
fn replace_key(&mut self, k1: &Q, k2: K) -> Result<(), ReplaceKeyErr> {
let Some(i) = self.get_index_of(k1) else {
return Err(ReplaceKeyErr::OldKeyNotExist);
};
if k1.equivalent(&k2) {
return Ok(());
}
if self.contains_key(k2.borrow()) {
return Err(ReplaceKeyErr::NewKeyOccupied);
}
let Some((_, v)) = self.swap_remove_index(i) else {
return Err(ReplaceKeyErr::OldKeyNotExist);
};
let (j, _) = self.insert_full(k2, v);
self.swap_indices(i, j);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn replace_key_hashmap() {
let mut map = HashMap::new();
map.insert("k1".to_string(), 123);
map.insert("k2".to_string(), 456);
assert_eq!(map["k1"], 123);
assert_eq!(map["k2"], 456);
assert_eq!(
map.replace_key("k3", "k2".to_string()),
Err(ReplaceKeyErr::OldKeyNotExist)
);
assert_eq!(
map.replace_key("k3", "k3".to_string()),
Err(ReplaceKeyErr::OldKeyNotExist)
);
assert_eq!(
map.replace_key("k3", "k4".to_string()),
Err(ReplaceKeyErr::OldKeyNotExist)
);
let cloned = map.clone();
assert_eq!(map.replace_key("k1", "k1".to_string()), Ok(()));
assert_eq!(map, cloned);
assert_eq!(
map.replace_key("k1", "k2".to_string()),
Err(ReplaceKeyErr::NewKeyOccupied)
);
assert_eq!(map.replace_key("k1", "k3".to_string()), Ok(()));
assert!(!map.contains_key("k1"));
assert_eq!(map["k3"], 123);
assert_eq!(map["k2"], 456);
}
#[test]
fn replace_key_btreemap() {
let mut map = BTreeMap::new();
map.insert("k1".to_string(), 123);
map.insert("k2".to_string(), 456);
assert_eq!(map["k1"], 123);
assert_eq!(map["k2"], 456);
assert_eq!(
map.replace_key("k3", "k2".to_string()),
Err(ReplaceKeyErr::OldKeyNotExist)
);
assert_eq!(
map.replace_key("k3", "k3".to_string()),
Err(ReplaceKeyErr::OldKeyNotExist)
);
assert_eq!(
map.replace_key("k3", "k4".to_string()),
Err(ReplaceKeyErr::OldKeyNotExist)
);
let cloned = map.clone();
assert_eq!(map.replace_key("k1", "k1".to_string()), Ok(()));
assert_eq!(map, cloned);
assert_eq!(
map.replace_key("k1", "k2".to_string()),
Err(ReplaceKeyErr::NewKeyOccupied)
);
assert_eq!(map.replace_key("k1", "k3".to_string()), Ok(()));
assert!(!map.contains_key("k1"));
assert_eq!(map["k3"], 123);
assert_eq!(map["k2"], 456);
}
#[test]
#[cfg(feature = "indexmap")]
fn replace_key_indexmap() {
let mut map = indexmap::indexmap! {
"k1".to_string() => 123,
"k2".to_string() => 456
};
assert_eq!(map["k1"], 123);
assert_eq!(map["k2"], 456);
assert_eq!(map.get_index_of("k1"), Some(0));
assert_eq!(map.get_index(0), Some((&"k1".to_string(), &123)));
assert_eq!(
map.replace_key("k3", "k2".to_string()),
Err(ReplaceKeyErr::OldKeyNotExist)
);
assert_eq!(
map.replace_key("k3", "k3".to_string()),
Err(ReplaceKeyErr::OldKeyNotExist)
);
assert_eq!(
map.replace_key("k3", "k4".to_string()),
Err(ReplaceKeyErr::OldKeyNotExist)
);
let cloned = map.clone();
assert_eq!(map.replace_key("k1", "k1".to_string()), Ok(()));
assert_eq!(map, cloned);
assert_eq!(
map.replace_key("k1", "k2".to_string()),
Err(ReplaceKeyErr::NewKeyOccupied)
);
assert_eq!(map.replace_key("k1", "k3".to_string()), Ok(()));
assert!(!map.contains_key("k1"));
assert_eq!(map["k3"], 123);
assert_eq!(map["k2"], 456);
assert_eq!(map.get_index_of("k3"), Some(0));
assert_eq!(map.get_index(0), Some((&"k3".to_string(), &123)));
}
}