prolly-map 0.4.0

Content-addressed versioned map storage primitives.
Documentation
use prolly::{Config, Error, MemStore, Mutation, Prolly};
use std::ops::ControlFlow;
use std::sync::Arc;

fn base() -> (Prolly<Arc<MemStore>>, prolly::Tree) {
    let manager = Prolly::new(Arc::new(MemStore::new()), Config::default());
    let tree = manager
        .batch(
            &manager.create(),
            vec![
                Mutation::Upsert {
                    key: b"a".to_vec(),
                    val: b"1".to_vec(),
                },
                Mutation::Upsert {
                    key: b"c".to_vec(),
                    val: b"3".to_vec(),
                },
            ],
        )
        .unwrap();
    (manager, tree)
}

#[test]
fn overlay_reads_ranges_and_flush_match_direct_batch() {
    let (manager, tree) = base();
    let mut session = manager.write_session(tree.clone(), 1024);
    session.put(b"b".to_vec(), b"2".to_vec()).unwrap();
    session.put(b"c".to_vec(), b"changed".to_vec()).unwrap();
    session.delete(b"a".to_vec()).unwrap();

    assert_eq!(session.get(b"a").unwrap(), None);
    assert_eq!(session.get(b"b").unwrap(), Some(b"2".to_vec()));
    assert_eq!(
        session.get_with(b"b", |value| value[0]).unwrap(),
        Some(b'2')
    );
    assert_eq!(
        session.range(b"a", Some(b"d")).unwrap(),
        vec![
            (b"b".to_vec(), b"2".to_vec()),
            (b"c".to_vec(), b"changed".to_vec()),
        ]
    );
    let mut borrowed = Vec::new();
    let outcome = session
        .scan_range_until(b"a", Some(b"d"), |entry| {
            borrowed.push(entry.to_owned());
            if borrowed.len() == 2 {
                ControlFlow::Break(entry.key().to_vec())
            } else {
                ControlFlow::Continue(())
            }
        })
        .unwrap();
    assert_eq!(outcome.visited, 2);
    assert_eq!(outcome.break_value, Some(b"c".to_vec()));
    assert_eq!(borrowed, session.range(b"a", Some(b"d")).unwrap());

    let direct = manager
        .batch(
            &tree,
            vec![
                Mutation::Delete { key: b"a".to_vec() },
                Mutation::Upsert {
                    key: b"b".to_vec(),
                    val: b"2".to_vec(),
                },
                Mutation::Upsert {
                    key: b"c".to_vec(),
                    val: b"changed".to_vec(),
                },
            ],
        )
        .unwrap();
    let flushed = session.flush().unwrap();
    assert_eq!(flushed.root, direct.root);
    assert!(session.is_empty());
}

#[test]
fn savepoint_revert_restores_overlay_and_stales_after_flush() {
    let (manager, tree) = base();
    let mut session = manager.write_session(tree, 1024);
    session.put(b"b".to_vec(), b"2".to_vec()).unwrap();
    let savepoint = session.savepoint();
    session.put(b"b".to_vec(), b"changed".to_vec()).unwrap();
    session.delete(b"c".to_vec()).unwrap();
    session.revert(savepoint).unwrap();
    assert_eq!(session.get(b"b").unwrap(), Some(b"2".to_vec()));
    assert_eq!(session.get(b"c").unwrap(), Some(b"3".to_vec()));

    session.flush().unwrap();
    assert!(matches!(
        session.revert(savepoint),
        Err(Error::InvalidSavepoint)
    ));
}

#[test]
fn byte_budget_is_checked_before_mutation() {
    let (manager, tree) = base();
    let mut session = manager.write_session(tree, 4);
    session.put(b"a".to_vec(), b"123".to_vec()).unwrap();
    assert!(matches!(
        session.put(b"b".to_vec(), b"x".to_vec()),
        Err(Error::BufferFull)
    ));
    assert_eq!(session.get(b"b").unwrap(), None);
}