use {
crate::{
core::{
cache::{CacheRead, CacheWrite},
primitive::prollytree::{
refimpl::{Create, Read},
roller::Config as RollerConfig,
},
},
Addr, Error, Key, Value,
},
std::collections::HashMap,
};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Change {
Insert(Value),
Remove,
}
pub struct Update<'s, C> {
cache: &'s C,
root_addr: Addr,
roller_config: RollerConfig,
}
impl<'s, C> Update<'s, C> {
pub fn new(cache: &'s C, root_addr: Addr) -> Self {
Self::with_roller(cache, root_addr, RollerConfig::default())
}
pub fn with_roller(cache: &'s C, root_addr: Addr, roller_config: RollerConfig) -> Self {
Self {
cache,
root_addr,
roller_config,
}
}
}
impl<'s, C> Update<'s, C>
where
C: CacheWrite + CacheRead,
{
pub async fn with_vec(self, mut changes: Vec<(Key, Change)>) -> Result<Addr, Error> {
{
changes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
let maybe_dups_len = changes.len();
changes.dedup_by(|a, b| a.0 == b.0);
if maybe_dups_len != changes.len() {
return Err(Error::Prolly {
message: "cannot construct prolly tree with non-unique keys".to_owned(),
});
}
}
let all_kvs = Read::new(self.cache, self.root_addr.clone())
.to_vec()
.await?;
let mut kvs = all_kvs
.into_iter()
.filter(|(source_key, _)| {
let change = changes
.iter()
.find(|(changed_key, _)| source_key == changed_key)
.map(|(_, change)| change);
let key_is_removed = matches!(change, Some(Change::Remove));
!key_is_removed
})
.collect::<HashMap<_, _>>();
changes
.into_iter()
.filter_map(|(changed_key, change)| match change {
Change::Remove => None,
Change::Insert(changed_value) => Some((changed_key, changed_value)),
})
.for_each(|(k, v)| {
kvs.insert(k, v);
});
Create::with_roller(self.cache, self.roller_config)
.with_hashmap(kvs)
.await
}
}
#[cfg(test)]
pub mod test {
use {super::*, crate::core::Fixity};
const TEST_PATTERN: u32 = (1 << 8) - 1;
#[tokio::test]
async fn apply_compare_updates() {
let mut env_builder = env_logger::builder();
env_builder.is_test(true);
if std::env::var("RUST_LOG").is_err() {
env_builder.filter(Some("fixity"), log::LevelFilter::Debug);
}
let _ = env_builder.try_init();
use TestChange::*;
let test_cases = vec![
((0..3), vec![(0, Remove)], vec![(1..3)]),
(
(0..7),
vec![(0, Remove), (7, Insert), (8, Insert), (9, Insert)],
vec![(1..10)],
),
];
for (source_kvs, changes, expected_kvs) in test_cases.into_iter() {
let expected_kvs = expected_kvs
.into_iter()
.flatten()
.map(|i| (Key::from(i), Value::from(i)))
.collect::<Vec<_>>();
let cache = Fixity::memory().into_cache();
let source_addr = {
let tree = Create::with_roller(&cache, RollerConfig::with_pattern(TEST_PATTERN));
let source_kvs = source_kvs
.map(|i| (i, i))
.map(|(k, v)| (Key::from(k), Value::from(v)))
.collect::<Vec<_>>();
tree.with_vec(source_kvs).await.unwrap()
};
let got_addr = {
Update::with_roller(
&cache,
source_addr,
RollerConfig::with_pattern(TEST_PATTERN),
)
.with_vec(
changes
.into_iter()
.map(|(i, change)| match change {
Remove => (Key::from(i), Change::Remove),
Insert => (Key::from(i), Change::Insert(Value::from(i))),
})
.collect::<Vec<_>>(),
)
.await
.unwrap()
};
let got_kvs = Read::new(&cache, got_addr.clone()).to_vec().await.unwrap();
assert_eq!(expected_kvs, got_kvs);
let expected_addr = {
Create::with_roller(&cache, RollerConfig::with_pattern(TEST_PATTERN))
.with_vec(expected_kvs)
.await
.unwrap()
};
assert_eq!(expected_addr, got_addr,);
}
}
enum TestChange {
Remove,
Insert,
}
}