orbit-cell 0.6.0

Fleet-shared typed atomic cells over Orbit shared memory, addressed by id rather than by name.
Documentation
use std::sync::Arc;

use orbit_cell::{Cells, Claim, Error};
use orbit_core::Fleet;

fn cells() -> Cells {
    Cells::new(Arc::new(Fleet::join("example", 1).unwrap())).unwrap()
}

#[test]
fn signed_subtraction_accepts_min_and_preserves_overflowed_values() {
    let cell = cells().allocate(0_i64).unwrap();
    let cases = [
        (i64::MIN, i64::MIN, Some(0)),
        (-1, i64::MIN, Some(i64::MAX)),
        (-2, i64::MIN, Some(i64::MAX - 1)),
        (i64::MIN + 1, i64::MIN, Some(1)),
        (i64::MAX, i64::MAX, Some(0)),
        (i64::MIN + 1, 1, Some(i64::MIN)),
        (0, i64::MAX, Some(-i64::MAX)),
        (0, -i64::MAX, Some(i64::MAX)),
        (i64::MIN, -1, Some(i64::MIN + 1)),
        (i64::MIN, 0, Some(i64::MIN)),
        (i64::MAX, 0, Some(i64::MAX)),
        (i64::MAX, -1, None),
        (i64::MIN, 1, None),
        (0, i64::MIN, None),
        (i64::MAX, i64::MIN, None),
        (i64::MIN, i64::MAX, None),
    ];
    for (initial, by, expected) in cases {
        cell.store(initial).unwrap();
        let version = cell.version().unwrap();
        let result = cell.fetch_sub(by);
        if let Some(value) = expected {
            assert_eq!(result.unwrap(), value, "{initial} - {by}");
            assert_eq!(cell.load().unwrap(), value);
            assert_eq!(cell.version().unwrap(), version + 1);
        } else {
            assert!(matches!(result, Err(Error::Overflow)), "{initial} - {by}");
            assert_eq!(cell.load().unwrap(), initial);
            assert_eq!(cell.version().unwrap(), version);
        }
    }
}

#[test]
fn bounded_signed_claims_handle_extreme_distances_and_rejected_steps() {
    use Claim::{Exhausted, Took};

    let cell = cells().allocate(0_i64).unwrap();
    let cases = [
        (i64::MIN, 1, i64::MAX, Some(Took { taken: 1, after: i64::MIN + 1 })),
        (
            i64::MIN,
            i64::MAX,
            i64::MAX,
            Some(Took { taken: i64::MAX, after: -1 }),
        ),
        (i64::MIN, i64::MAX, 0, Some(Took { taken: i64::MAX, after: -1 })),
        (
            i64::MIN,
            i64::MAX,
            i64::MIN + 7,
            Some(Took { taken: 7, after: i64::MIN + 7 }),
        ),
        (
            -1,
            i64::MAX,
            i64::MAX,
            Some(Took { taken: i64::MAX, after: i64::MAX - 1 }),
        ),
        (-10, 4, -7, Some(Took { taken: 3, after: -7 })),
        (-10, 4, 10, Some(Took { taken: 4, after: -6 })),
        (
            i64::MAX - 1,
            i64::MAX,
            i64::MAX,
            Some(Took { taken: 1, after: i64::MAX }),
        ),
        (i64::MAX, 1, i64::MAX, Some(Exhausted)),
        (i64::MIN, 1, i64::MIN, Some(Exhausted)),
        (7, 1, 3, Some(Exhausted)),
        (-1, 1, i64::MIN, Some(Exhausted)),
        (0, 0, i64::MAX, None),
        (i64::MIN, i64::MIN, i64::MAX, None),
        (i64::MIN, -1, i64::MAX, None),
        (i64::MAX, 0, i64::MAX, None),
    ];
    for (initial, by, limit, expected) in cases {
        cell.store(initial).unwrap();
        let version = cell.version().unwrap();
        let result = cell.add_until(by, limit);
        match expected {
            Some(Took { taken, after }) => {
                assert_eq!(result.unwrap(), Took { taken, after });
                assert!(taken > 0 && taken <= by);
                assert!(after > initial && after <= limit);
                assert_eq!(
                    i128::from(after) - i128::from(initial),
                    i128::from(taken)
                );
                assert_eq!(cell.load().unwrap(), after);
                assert_eq!(cell.version().unwrap(), version + 1);
            }
            Some(Exhausted) => {
                assert_eq!(result.unwrap(), Exhausted);
                assert_eq!(cell.load().unwrap(), initial);
                assert_eq!(cell.version().unwrap(), version);
            }
            None => {
                assert!(matches!(result, Err(Error::Overflow)));
                assert_eq!(cell.load().unwrap(), initial);
                assert_eq!(cell.version().unwrap(), version);
            }
        }
    }
}

#[test]
fn bounded_claims_cross_the_entire_signed_range_without_overshooting() {
    let cell = cells().allocate(i64::MIN).unwrap();
    let mut total = 0_i128;
    for (taken, after) in
        [(i64::MAX, -1), (i64::MAX, i64::MAX - 1), (1, i64::MAX)]
    {
        assert_eq!(
            cell.add_until(i64::MAX, i64::MAX).unwrap(),
            Claim::Took { taken, after }
        );
        total += i128::from(taken);
        assert_eq!(cell.load().unwrap(), after);
    }
    assert_eq!(total, i128::from(u64::MAX));
    let version = cell.version().unwrap();
    assert_eq!(cell.add_until(i64::MAX, i64::MAX).unwrap(), Claim::Exhausted);
    assert_eq!(cell.version().unwrap(), version);
    assert_eq!(cell.load().unwrap(), i64::MAX);
}