use core::borrow::Borrow;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ProjectionChange<Value> {
Unchanged,
Upsert { incarnation: u64, value: Value },
Remove { incarnation: u64 },
}
pub(super) fn projection_change<Value: Eq, Current: Borrow<Value>>(
before: Option<(u64, &Value)>,
after: Option<(u64, Current)>,
) -> ProjectionChange<Current> {
match (before, after) {
(None, None) => ProjectionChange::Unchanged,
(Some((old_incarnation, old)), Some((incarnation, value)))
if old_incarnation == incarnation && old == value.borrow() =>
{
ProjectionChange::Unchanged
}
(Some((..)) | None, Some((incarnation, value))) => {
ProjectionChange::Upsert { incarnation, value }
}
(Some((incarnation, _)), None) => ProjectionChange::Remove { incarnation },
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_small_change_reconstructs_the_exact_target_state() {
let states = [None, Some((1, 2_u8)), Some((1, 3)), Some((2, 2))];
for before in states {
for after in states {
let change = projection_change(
before
.as_ref()
.map(|(incarnation, value)| (*incarnation, value)),
after,
);
let reconstructed = match change {
ProjectionChange::Unchanged => {
assert_eq!(before, after);
before
}
ProjectionChange::Upsert { incarnation, value } => {
assert_ne!(before, after);
Some((incarnation, value))
}
ProjectionChange::Remove { incarnation } => {
assert_eq!(
before.map(|(incarnation, _)| incarnation),
Some(incarnation)
);
None
}
};
assert_eq!(reconstructed, after);
}
}
}
#[test]
fn upsert_preserves_owned_payloads_and_borrowed_identity() -> Result<(), &'static str> {
#[derive(Debug, Eq, PartialEq)]
struct Payload(u8);
let before = Payload(7);
let after = Payload(11);
let ProjectionChange::Upsert { incarnation, value } =
projection_change(Some((13, &before)), Some((17, &after)))
else {
return Err("changed borrowed payload was not carried");
};
assert_eq!(incarnation, 17);
assert!(core::ptr::eq(value, &after));
assert_eq!(
projection_change(Some((13, &before)), Some((17, after))),
ProjectionChange::Upsert {
incarnation: 17,
value: Payload(11)
}
);
Ok(())
}
}