core_memo 0.1.0

Zero-cost, no_std-compatible library for lazy evaluation and memoization
Documentation
use core::cell::Cell;
use Memoize;

const MAGIC: i32 = -420;

#[derive(Debug)]
struct CallTracker {
    count: Cell<usize>,
}

impl CallTracker {
    fn new() -> Self {
        Self {
            count: Cell::new(0),
        }
    }

    fn count(&self) -> usize {
        self.count.get()
    }

    fn incr(&self) {
        self.count.set(self.count.get() + 1);
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct TestOut(i32);

impl Memoize for TestOut {
    type Param = CallTracker;
    fn memoize(p: &CallTracker) -> Self {
        p.incr();
        TestOut(MAGIC)
    }
}

#[test]
fn track_calls_ext() {
    use MemoExt;

    let expected = TestOut(MAGIC);
    let track = CallTracker::new();
    let mut memo: MemoExt<TestOut> = MemoExt::new();

    assert_eq!(memo.is_ready(), false);
    assert_eq!(memo.try_get(), None);
    assert_eq!(track.count(), 0);

    memo.ready(&track);

    assert_eq!(memo.is_ready(), true);
    assert_eq!(track.count(), 1);

    assert_eq!(memo.try_get(), Some(&expected));
    assert_eq!(memo.try_get(), Some(&expected));
    assert_eq!(track.count(), 1);

    assert_eq!(memo.get(&track), &expected);
    assert_eq!(memo.get(&track), &expected);
    assert_eq!(track.count(), 1);

    memo.clear();

    assert_eq!(memo.is_ready(), false);
    assert_eq!(memo.try_get(), None);
    assert_eq!(track.count(), 1);

    assert_eq!(memo.get(&track), &expected);
    assert_eq!(memo.get(&track), &expected);
    assert_eq!(track.count(), 2);
    assert_eq!(memo.is_ready(), true);

    memo.update(&track);
    assert_eq!(track.count(), 3);
}

#[test]
fn track_calls_own() {
    use Memo;

    let expected = TestOut(MAGIC);
    let track = CallTracker::new();
    let mut memo: Memo<TestOut> = Memo::new(track);

    assert_eq!(memo.is_ready(), false);
    assert_eq!(memo.try_get(), None);
    assert_eq!(memo.param().count(), 0);

    memo.ready();

    assert_eq!(memo.is_ready(), true);
    assert_eq!(memo.param().count(), 1);

    assert_eq!(memo.try_get(), Some(&expected));
    assert_eq!(memo.try_get(), Some(&expected));
    assert_eq!(memo.param().count(), 1);

    assert_eq!(memo.get(), &expected);
    assert_eq!(memo.get(), &expected);
    assert_eq!(memo.param().count(), 1);

    memo.clear();

    assert_eq!(memo.is_ready(), false);
    assert_eq!(memo.try_get(), None);
    assert_eq!(memo.param().count(), 1);

    assert_eq!(memo.get(), &expected);
    assert_eq!(memo.get(), &expected);
    assert_eq!(memo.param().count(), 2);
    assert_eq!(memo.is_ready(), true);

    memo.update();
    assert_eq!(memo.param().count(), 3);
}

#[test]
fn track_calls_ref() {
    use MemoOnce;

    let expected = TestOut(MAGIC);
    let track = CallTracker::new();
    let mut memo: MemoOnce<TestOut> = MemoOnce::new(&track);

    assert_eq!(memo.is_ready(), false);
    assert_eq!(memo.try_get(), None);
    assert_eq!(track.count(), 0);

    memo.ready();

    assert_eq!(memo.is_ready(), true);
    assert_eq!(track.count(), 1);

    assert_eq!(memo.try_get(), Some(&expected));
    assert_eq!(memo.try_get(), Some(&expected));
    assert_eq!(track.count(), 1);

    assert_eq!(memo.get(), &expected);
    assert_eq!(memo.get(), &expected);
    assert_eq!(track.count(), 1);

    memo.clear();

    assert_eq!(memo.is_ready(), false);
    assert_eq!(memo.try_get(), None);
    assert_eq!(track.count(), 1);

    assert_eq!(memo.get(), &expected);
    assert_eq!(memo.get(), &expected);
    assert_eq!(track.count(), 2);
    assert_eq!(memo.is_ready(), true);

    memo.update();
    assert_eq!(track.count(), 3);
}

#[test]
fn track_calls_mutation() {
    use Memo;

    let track = CallTracker::new();
    let mut memo: Memo<TestOut> = Memo::new(track);

    assert_eq!(memo.is_ready(), false);
    assert_eq!(memo.try_get(), None);
    assert_eq!(memo.param().count(), 0);

    memo.get();

    assert_eq!(memo.is_ready(), true);
    assert_eq!(memo.param().count(), 1);

    memo.update_param(|_p| ());

    assert_eq!(memo.is_ready(), false);
    assert_eq!(memo.param().count(), 1);

    memo.get();

    assert_eq!(memo.is_ready(), true);
    assert_eq!(memo.param().count(), 2);

    memo.param_mut();

    assert_eq!(memo.is_ready(), false);
    assert_eq!(memo.param().count(), 2);

    memo.get();

    assert_eq!(memo.is_ready(), true);
    assert_eq!(memo.param().count(), 3);
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct MemoSum(i32);

impl Memoize for MemoSum {
    type Param = [i32];

    fn memoize(p: &[i32]) -> MemoSum {
        MemoSum(p.iter().sum())
    }
}

#[test]
fn sums() {
    use Memo;

    let vals = vec![1, 2];
    let mut memo: Memo<MemoSum, _> = Memo::new(vals);

    assert_eq!(memo.get(), &MemoSum(3));

    memo.param_mut().push(3);

    assert_eq!(memo.get(), &MemoSum(6));

    memo.update_param(|p| p.push(4));

    assert_eq!(memo.get(), &MemoSum(10));
}