use thiserror::Error;
use crate::CacheValue;
#[derive(Error, Debug, PartialEq, Eq)]
pub enum MergeError {
#[error("consecutive reads are inconsistent: left read: {left:?}, right read: {right:?}")]
ReadThenRead {
left: Option<CacheValue>,
right: Option<CacheValue>,
},
#[error("the read: {read:?} is in inconsistent with the previous write: {write:?}")]
WriteThenRead {
write: Option<CacheValue>,
read: Option<CacheValue>,
},
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub(crate) enum Access {
Read(Option<CacheValue>),
ReadThenWrite {
original: Option<CacheValue>,
modified: Option<CacheValue>,
},
Write(Option<CacheValue>),
}
impl Access {
pub(crate) fn last_value(&self) -> &Option<CacheValue> {
match self {
Access::Read(value) => value,
Access::ReadThenWrite { modified, .. } => modified,
Access::Write(value) => value,
}
}
pub(crate) fn write_value(&mut self, new_value: Option<CacheValue>) {
match self {
Access::Read(original) => {
if original == &new_value {
return;
}
*self = Access::ReadThenWrite {
original: original.take(),
modified: new_value,
};
}
Access::ReadThenWrite { original, modified } => {
if original == &new_value {
*self = Access::Read(new_value)
} else {
*modified = new_value
}
}
Access::Write(value) => *value = new_value,
}
}
pub(crate) fn merge(&mut self, rhs: Self) -> Result<(), MergeError> {
match self {
Access::Read(left_read) => match rhs {
Access::Read(right_read) => {
if left_read != &right_read {
Err(MergeError::ReadThenRead {
left: left_read.clone(),
right: right_read,
})
} else {
Ok(())
}
}
Access::ReadThenWrite {
original: right_original,
modified: right_modified,
} => {
if left_read != &right_original {
Err(MergeError::ReadThenRead {
left: left_read.clone(),
right: right_original,
})
} else {
*self = Access::ReadThenWrite {
original: right_original,
modified: right_modified,
};
Ok(())
}
}
Access::Write(right_write) => {
*self = Access::ReadThenWrite {
original: left_read.take(),
modified: right_write,
};
Ok(())
}
},
Access::ReadThenWrite {
original: left_original,
modified: left_modified,
} => match rhs {
Access::Read(right_read) => {
if left_modified != &right_read {
Err(MergeError::WriteThenRead {
write: left_modified.clone(),
read: right_read,
})
} else {
Ok(())
}
}
Access::ReadThenWrite {
original: right_original,
modified: right_modified,
} => {
if left_modified != &right_original {
Err(MergeError::WriteThenRead {
write: left_modified.clone(),
read: right_original,
})
} else {
*self = Access::ReadThenWrite {
original: left_original.take(),
modified: right_modified,
};
Ok(())
}
}
Access::Write(right_write) => {
*self = Access::ReadThenWrite {
original: left_original.take(),
modified: right_write,
};
Ok(())
}
},
Access::Write(left_write) => match rhs {
Access::Read(right_read) => {
if left_write != &right_read {
Err(MergeError::WriteThenRead {
write: left_write.clone(),
read: right_read,
})
} else {
Ok(())
}
}
Access::ReadThenWrite {
original: right_original,
modified: right_modified,
} => {
if left_write != &right_original {
Err(MergeError::WriteThenRead {
write: left_write.clone(),
read: right_original,
})
} else {
*self = Access::Write(right_modified);
Ok(())
}
}
Access::Write(right_write) => {
*self = Access::Write(right_write);
Ok(())
}
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::test_util::create_value;
#[test]
fn test_access_read_write() {
let original_value = create_value(1);
let mut access = Access::Read(original_value.clone());
{
let new_value = create_value(2);
access.write_value(new_value.clone());
assert_eq!(access.last_value(), &new_value);
assert_eq!(
access,
Access::ReadThenWrite {
original: original_value.clone(),
modified: new_value
}
);
}
{
let new_value = create_value(3);
access.write_value(new_value.clone());
assert_eq!(access.last_value(), &new_value);
assert_eq!(
access,
Access::ReadThenWrite {
original: original_value,
modified: new_value
}
);
}
}
#[test]
fn test_access_write() {
let original_value = create_value(1);
let mut access = Access::Write(original_value.clone());
{
assert_eq!(access.last_value(), &original_value);
let new_value = create_value(3);
access.write_value(new_value.clone());
assert_eq!(access.last_value(), &new_value);
assert_eq!(access, Access::Write(new_value));
}
}
#[test]
fn test_access_merge() {
let first_read = 1;
let mut value = create_value(first_read);
let mut left = Access::Read(value.clone());
let last_write = 10;
for i in 2..last_write + 1 {
left.merge(Access::Read(value.clone())).unwrap();
value = create_value(i);
left.merge(Access::Write(value.clone())).unwrap();
}
assert_eq!(
left,
Access::ReadThenWrite {
original: create_value(first_read),
modified: create_value(last_write)
}
)
}
#[test]
fn test_err_merge_left_read_neq_right_read() {
let first_read = 1;
let value = create_value(first_read);
let left = &mut Access::Read(value.clone());
let second_read = 2;
let value2 = create_value(second_read);
assert_eq!(
left.merge(Access::Read(value2.clone())),
Err(MergeError::ReadThenRead {
left: value,
right: value2,
})
);
}
#[test]
fn test_err_merge_left_read_neq_right_orig() {
let first_read = 1;
let value = create_value(first_read);
let left = &mut Access::Read(value.clone());
let second_read = 2;
let value2 = create_value(second_read);
let right = Access::ReadThenWrite {
original: value2.clone(),
modified: value.clone(),
};
assert_eq!(
left.merge(right),
Err(MergeError::ReadThenRead {
left: value,
right: value2,
})
);
}
#[test]
fn test_err_merge_left_mod_neq_right_read() {
let first_read = 1;
let value = create_value(first_read);
let second_read = 2;
let value2 = create_value(second_read);
let left = &mut Access::ReadThenWrite {
original: value2.clone(),
modified: value.clone(),
};
let right = Access::Read(value2.clone());
assert_eq!(
left.merge(right),
Err(MergeError::WriteThenRead {
write: value,
read: value2,
})
)
}
#[test]
fn test_err_merge_left_mod_neq_right_orig() {
let first_read = 1;
let value = create_value(first_read);
let second_read = 2;
let value2 = create_value(second_read);
let left = &mut Access::ReadThenWrite {
original: value.clone(),
modified: value2.clone(),
};
let right = Access::ReadThenWrite {
original: value.clone(),
modified: value2.clone(),
};
assert_eq!(
left.merge(right),
Err(MergeError::WriteThenRead {
write: value2,
read: value,
})
)
}
#[test]
fn test_err_merge_left_right_neq_right_orig() {
let first_read = 1;
let value = create_value(first_read);
let second_read = 2;
let value2 = create_value(second_read);
let left = &mut Access::Write(value.clone());
let right = Access::ReadThenWrite {
original: value2.clone(),
modified: value.clone(),
};
assert_eq!(
left.merge(right),
Err(MergeError::WriteThenRead {
write: value,
read: value2,
})
)
}
}