use super::IdOrdItem;
use crate::support::map_hash::MapHash;
use core::{
fmt,
hash::Hash,
ops::{Deref, DerefMut},
};
pub struct RefMut<'a, T: IdOrdItem>
where
T::Key<'a>: Hash,
{
inner: Option<RefMutInner<'a, T>>,
}
impl<'a, T: IdOrdItem> RefMut<'a, T>
where
T::Key<'a>: Hash,
{
pub(super) fn new(
state: foldhash::fast::FixedState,
hash: MapHash,
borrowed: &'a mut T,
) -> Self {
let inner = RefMutInner { state, hash, borrowed };
Self { inner: Some(inner) }
}
pub fn into_ref(mut self) -> &'a T {
let inner = self.inner.take().unwrap();
inner.into_ref()
}
}
impl<'a, T: for<'k> IdOrdItemMut<'k>> RefMut<'a, T> {
pub fn reborrow<'b>(&'b mut self) -> RefMut<'b, T> {
let inner = self.inner.as_mut().unwrap();
let borrowed = &mut *inner.borrowed;
RefMut::new(inner.state.clone(), inner.hash.clone(), borrowed)
}
}
impl<'a, T: IdOrdItem> Drop for RefMut<'a, T>
where
T::Key<'a>: Hash,
{
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
inner.into_ref();
}
}
}
impl<'a, T: IdOrdItem> Deref for RefMut<'a, T>
where
T::Key<'a>: Hash,
{
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner.as_ref().unwrap().borrowed
}
}
impl<'a, T: IdOrdItem> DerefMut for RefMut<'a, T>
where
T::Key<'a>: Hash,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.as_mut().unwrap().borrowed
}
}
impl<'a, T: IdOrdItem + fmt::Debug> fmt::Debug for RefMut<'a, T>
where
T::Key<'a>: Hash,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner {
Some(ref inner) => inner.fmt(f),
None => {
f.debug_struct("RefMut").field("borrowed", &"missing").finish()
}
}
}
}
struct RefMutInner<'a, T: IdOrdItem> {
state: foldhash::fast::FixedState,
hash: MapHash,
borrowed: &'a mut T,
}
impl<'a, T: IdOrdItem> RefMutInner<'a, T>
where
T::Key<'a>: Hash,
{
fn into_ref(self) -> &'a T {
let key: T::Key<'_> = self.borrowed.key();
let key: T::Key<'a> =
unsafe { std::mem::transmute::<T::Key<'_>, T::Key<'a>>(key) };
if !self.hash.is_same_hash(&self.state, &key) {
panic!("key changed during RefMut borrow");
}
self.borrowed
}
}
impl<T: IdOrdItem + fmt::Debug> fmt::Debug for RefMutInner<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.borrowed.fmt(f)
}
}
pub trait IdOrdItemMut<'a>: IdOrdItem<Key<'a>: Hash> + 'a {}
impl<'a, T> IdOrdItemMut<'a> for T where T: 'a + IdOrdItem<Key<'a>: Hash> {}