use crate::{DefaultHashBuilder, TriHashItem, support::map_hash::MapHash};
use core::{
fmt,
hash::BuildHasher,
ops::{Deref, DerefMut},
};
pub struct RefMut<
'a,
T: TriHashItem,
S: Clone + BuildHasher = DefaultHashBuilder,
> {
inner: Option<RefMutInner<'a, T, S>>,
}
impl<'a, T: TriHashItem, S: Clone + BuildHasher> RefMut<'a, T, S> {
pub(super) fn new(
state: S,
hashes: [MapHash; 3],
borrowed: &'a mut T,
) -> Self {
Self { inner: Some(RefMutInner { state, hashes, borrowed }) }
}
pub fn reborrow(&mut self) -> RefMut<'_, T, S> {
let inner = self.inner.as_mut().unwrap();
let borrowed = &mut *inner.borrowed;
RefMut::new(inner.state.clone(), inner.hashes.clone(), borrowed)
}
pub fn into_ref(mut self) -> &'a T {
let inner = self.inner.take().unwrap();
inner.into_ref()
}
}
impl<T: TriHashItem, S: Clone + BuildHasher> Drop for RefMut<'_, T, S> {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
inner.into_ref();
}
}
}
impl<T: TriHashItem, S: Clone + BuildHasher> Deref for RefMut<'_, T, S> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner.as_ref().unwrap().borrowed
}
}
impl<T: TriHashItem, S: Clone + BuildHasher> DerefMut for RefMut<'_, T, S> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.as_mut().unwrap().borrowed
}
}
impl<T: TriHashItem + fmt::Debug, S: Clone + BuildHasher> fmt::Debug
for RefMut<'_, T, S>
{
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: TriHashItem, S> {
state: S,
hashes: [MapHash; 3],
borrowed: &'a mut T,
}
impl<'a, T: TriHashItem, S: BuildHasher> RefMutInner<'a, T, S> {
fn into_ref(self) -> &'a T {
if !self.hashes[0].is_same_hash(&self.state, self.borrowed.key1()) {
panic!("key1 changed during RefMut borrow");
}
if !self.hashes[1].is_same_hash(&self.state, self.borrowed.key2()) {
panic!("key2 changed during RefMut borrow");
}
if !self.hashes[2].is_same_hash(&self.state, self.borrowed.key3()) {
panic!("key3 changed during RefMut borrow");
}
self.borrowed
}
}
impl<T: TriHashItem + fmt::Debug, S> fmt::Debug for RefMutInner<'_, T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.borrowed.fmt(f)
}
}