use bincode::{Decode, Encode};
use crate::infinitedb_core::{
address::RevisionId,
block::{Block, BlockId},
snapshot::{Snapshot, SnapshotId},
};
#[derive(Debug, Encode, Decode)]
pub struct Delta {
pub source_snapshot: SnapshotId,
pub target_snapshot: SnapshotId,
pub added_blocks: Vec<Block>,
pub removed_block_ids: Vec<BlockId>,
pub at_revision: RevisionId,
}
impl Delta {
pub fn compute(source: &Snapshot, target: &Snapshot, source_blocks: Vec<Block>) -> Self {
let added_blocks: Vec<Block> = source_blocks
.into_iter()
.filter(|b| !target.blocks.values().any(|id| *id == b.id))
.collect();
let removed_block_ids: Vec<BlockId> = target
.blocks
.values()
.filter(|id| !source.blocks.values().any(|s_id| s_id == *id))
.copied()
.collect();
Delta {
source_snapshot: source.id,
target_snapshot: target.id,
added_blocks,
removed_block_ids,
at_revision: source.revision,
}
}
pub fn apply(&self, snapshot: &Snapshot) -> Snapshot {
use std::collections::BTreeMap;
let mut blocks: BTreeMap<u128, BlockId> = snapshot.blocks.clone();
blocks.retain(|_, id| !self.removed_block_ids.contains(id));
for block in &self.added_blocks {
blocks.insert(block.id.0 as u128, block.id);
}
Snapshot {
id: self.target_snapshot,
space: snapshot.space,
revision: self.at_revision,
parent: Some(snapshot.id),
blocks,
}
}
pub fn is_empty(&self) -> bool {
self.added_blocks.is_empty() && self.removed_block_ids.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
use crate::infinitedb_core::{
address::{RevisionId, SpaceId},
block::{Block, BlockId},
snapshot::{Snapshot, SnapshotId},
};
fn empty_snapshot(id: u64) -> Snapshot {
Snapshot {
id: SnapshotId(id),
space: SpaceId(1),
revision: RevisionId(id),
parent: None,
blocks: BTreeMap::new(),
}
}
fn make_block(id: u64) -> Block {
Block {
id: BlockId(id),
space: SpaceId(1),
records: vec![],
min_revision: RevisionId::ZERO,
max_revision: RevisionId::ZERO,
checksum: [0u8; 32],
}
}
#[test]
fn delta_adds_new_blocks() {
let mut source = empty_snapshot(2);
source.blocks.insert(10, BlockId(10));
let target = empty_snapshot(1);
let delta = Delta::compute(&source, &target, vec![make_block(10)]);
assert_eq!(delta.added_blocks.len(), 1);
assert!(delta.removed_block_ids.is_empty());
let updated = delta.apply(&target);
assert!(updated.blocks.values().any(|id| *id == BlockId(10)));
}
#[test]
fn empty_delta_when_in_sync() {
let mut source = empty_snapshot(1);
source.blocks.insert(5, BlockId(5));
let mut target = empty_snapshot(1);
target.blocks.insert(5, BlockId(5));
let delta = Delta::compute(&source, &target, vec![]);
assert!(delta.is_empty());
}
}