shadow_counted 0.7.0

An iterator that counts every iteration in a hidden counter, nested iterators may commit the count to parents
Documentation
use shadow_counted::IntoShadowCounted;

/// Test abort (drop) of nested iterator doesn't affect parent
#[test]
fn test_nested_abort_doesnt_affect_parent() {
    let mut parent = vec![1, 2, 3].into_iter().shadow_counted();
    parent.next();
    assert_eq!(parent.counter(), 1);

    {
        let mut nested = vec![10, 20, 30]
            .into_iter()
            .nested_shadow_counted(&mut parent);
        nested.next();
        nested.next();
        nested.next();
        assert_eq!(nested.counter(), 4);
        // Drop nested without commit
    }

    // Parent should be unchanged
    assert_eq!(parent.counter(), 1);

    // Parent can continue normally
    parent.next();
    assert_eq!(parent.counter(), 2);
}

/// Test multiple sequential nests with mixed commits and aborts
#[test]
fn test_mixed_commit_and_abort_sequential() {
    let mut parent = vec![1, 2, 3, 4, 5].into_iter().shadow_counted();
    parent.next();
    assert_eq!(parent.counter(), 1);

    // First nested - committed
    {
        let mut nested1 = vec![10, 11].into_iter().nested_shadow_counted(&mut parent);
        nested1.next();
        nested1.next();
        assert_eq!(nested1.counter(), 3);
        nested1.commit().unwrap();
    }
    assert_eq!(parent.counter(), 3);

    // Second nested - aborted
    {
        let mut nested2 = vec![20, 21, 22]
            .into_iter()
            .nested_shadow_counted(&mut parent);
        nested2.next();
        nested2.next();
        nested2.next();
        assert_eq!(nested2.counter(), 6);
        // Drop without commit
    }
    assert_eq!(parent.counter(), 3); // Unchanged

    // Third nested - committed
    {
        let mut nested3 = vec![30].into_iter().nested_shadow_counted(&mut parent);
        nested3.next();
        assert_eq!(nested3.counter(), 4);
        nested3.commit().unwrap();
    }
    assert_eq!(parent.counter(), 4);

    parent.next();
    assert_eq!(parent.counter(), 5);
}

/// Test deep nesting with middle level abort
#[test]
fn test_deep_nesting_with_middle_abort() {
    let mut level1 = vec![1].into_iter().shadow_counted();
    level1.next();
    assert_eq!(level1.counter(), 1);

    {
        let mut level2 = vec![2, 3].into_iter().nested_shadow_counted(&mut level1);
        level2.next();
        assert_eq!(level2.counter(), 2);

        {
            let mut level3 = vec![4].into_iter().nested_shadow_counted(&mut level2);
            level3.next();
            assert_eq!(level3.counter(), 3);
            level3.commit().unwrap();
        }

        assert_eq!(level2.counter(), 3);
        level2.next(); // Continue level2 - this adds 1 more
        assert_eq!(level2.counter(), 4);
        // Abort level2 - don't commit
    }

    // level1 should still be at 1 because level2 was aborted
    assert_eq!(level1.counter(), 1);
}

/// Test deep nesting with bottom level abort
#[test]
fn test_deep_nesting_with_bottom_abort() {
    let mut level1 = vec![1].into_iter().shadow_counted();
    level1.next();
    assert_eq!(level1.counter(), 1);

    {
        let mut level2 = vec![2].into_iter().nested_shadow_counted(&mut level1);
        level2.next();
        assert_eq!(level2.counter(), 2);

        {
            let mut level3 = vec![3, 4, 5].into_iter().nested_shadow_counted(&mut level2);
            level3.next();
            level3.next();
            level3.next();
            assert_eq!(level3.counter(), 5);
            // Abort level3 - don't commit
        }

        // level2 should still be at 2
        assert_eq!(level2.counter(), 2);

        level2.commit().unwrap();
    }

    // level1 gets level2's count
    assert_eq!(level1.counter(), 2);
}

/// Test complex scenario with multiple branches
#[test]
fn test_complex_multi_branch_scenario() {
    let mut root = vec![1, 2, 3, 4, 5, 6, 7, 8].into_iter().shadow_counted();
    root.next(); // count = 1
    assert_eq!(root.counter(), 1);

    // Branch 1: commit
    {
        let mut branch1 = vec![10, 11].into_iter().nested_shadow_counted(&mut root);
        branch1.next();
        branch1.next();
        assert_eq!(branch1.counter(), 3);
        branch1.commit().unwrap();
    }
    assert_eq!(root.counter(), 3);

    root.next(); // count = 4
    assert_eq!(root.counter(), 4);

    // Branch 2: abort
    {
        let mut branch2 = vec![20, 21, 22]
            .into_iter()
            .nested_shadow_counted(&mut root);
        branch2.next();
        branch2.next();
        assert_eq!(branch2.counter(), 6);
        // Abort
    }
    assert_eq!(root.counter(), 4); // Unchanged

    // Branch 3: nested with partial commit
    {
        let mut branch3 = vec![30].into_iter().nested_shadow_counted(&mut root);
        branch3.next();
        assert_eq!(branch3.counter(), 5);

        {
            let mut nested3a = vec![40, 41].into_iter().nested_shadow_counted(&mut branch3);
            nested3a.next();
            assert_eq!(nested3a.counter(), 6);
            nested3a.commit().unwrap();
        }
        assert_eq!(branch3.counter(), 6);

        {
            let mut nested3b = vec![50, 51, 52]
                .into_iter()
                .nested_shadow_counted(&mut branch3);
            nested3b.next();
            nested3b.next();
            nested3b.next();
            assert_eq!(nested3b.counter(), 9);
            // Abort nested3b
        }
        assert_eq!(branch3.counter(), 6); // Unchanged by abort

        branch3.commit().unwrap();
    }
    assert_eq!(root.counter(), 6);

    root.next(); // count = 7
    root.next(); // count = 8
    assert_eq!(root.counter(), 8);
}

/// Test that abort of parent doesn't affect sibling that was committed
#[test]
fn test_committed_sibling_survives_parent_abort() {
    let mut level1 = vec![1].into_iter().shadow_counted();
    level1.next();
    assert_eq!(level1.counter(), 1);

    {
        let mut level2 = vec![2].into_iter().nested_shadow_counted(&mut level1);
        level2.next();
        assert_eq!(level2.counter(), 2);

        // First child - commit
        {
            let mut child1 = vec![10].into_iter().nested_shadow_counted(&mut level2);
            child1.next();
            assert_eq!(child1.counter(), 3);
            child1.commit().unwrap();
        }
        assert_eq!(level2.counter(), 3);

        // Second child - abort
        {
            let mut child2 = vec![20, 21].into_iter().nested_shadow_counted(&mut level2);
            child2.next();
            child2.next();
            assert_eq!(child2.counter(), 5);
            // Abort
        }
        assert_eq!(level2.counter(), 3); // Still has first child's commit

        // Abort level2
    }

    // level1 doesn't get any updates because level2 was aborted
    assert_eq!(level1.counter(), 1);
}

/// Test alternating commits and aborts at same level
#[test]
fn test_alternating_commits_and_aborts() {
    let mut parent = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        .into_iter()
        .shadow_counted();
    let mut expected = 0;

    for i in 0..5 {
        parent.next();
        expected += 1;

        if i % 2 == 0 {
            // Even iterations: commit
            let mut nested = vec![100 + i].into_iter().nested_shadow_counted(&mut parent);
            nested.next();
            expected += 1;
            nested.commit().unwrap();
            assert_eq!(parent.counter(), expected);
        } else {
            // Odd iterations: abort
            let mut nested = vec![100 + i, 200 + i]
                .into_iter()
                .nested_shadow_counted(&mut parent);
            nested.next();
            nested.next();
            // expected doesn't change - we abort
            // Abort
            assert_eq!(parent.counter(), expected);
        }
    }

    assert_eq!(parent.counter(), 8); // 5 parent items + 3 committed nests
}

/// Test very deep nesting with selective commits
#[test]
fn test_very_deep_selective_commits() {
    let mut l0 = vec![0].into_iter().shadow_counted();
    l0.next();

    {
        let mut l1 = vec![1].into_iter().nested_shadow_counted(&mut l0);
        l1.next();

        {
            let mut l2 = vec![2].into_iter().nested_shadow_counted(&mut l1);
            l2.next();

            {
                let mut l3 = vec![3].into_iter().nested_shadow_counted(&mut l2);
                l3.next();

                {
                    let mut l4 = vec![4].into_iter().nested_shadow_counted(&mut l3);
                    l4.next();
                    // Abort l4
                }
                assert_eq!(l3.counter(), 4); // Unchanged

                l3.commit().unwrap();
            }
            assert_eq!(l2.counter(), 4);

            // Abort l2
        }
        assert_eq!(l1.counter(), 2); // Unchanged by l2 abort

        l1.commit().unwrap();
    }
    assert_eq!(l0.counter(), 2); // Gets l1's count
}

/// Test that cloned nested iterator can be aborted while original commits
#[test]
fn test_clone_one_commits_one_aborts() {
    let mut parent = vec![1, 2, 3].into_iter().shadow_counted();
    parent.next();

    let mut nested = vec![10, 11, 12]
        .into_iter()
        .nested_shadow_counted(&mut parent);
    let _nested_clone = nested.clone(); // This will be aborted

    nested.next();
    nested.next();
    assert_eq!(nested.counter(), 3);
    nested.commit().unwrap();

    // The clone is dropped/aborted here
    assert_eq!(parent.counter(), 3);
}

/// Test empty nested iterators mixed with non-empty
#[test]
fn test_mixed_empty_and_nonempty_commits_aborts() {
    let mut parent = vec![1, 2, 3].into_iter().shadow_counted();
    parent.next();
    assert_eq!(parent.counter(), 1);

    // Empty commit
    {
        let empty: Vec<i32> = vec![];
        let nested = empty.into_iter().nested_shadow_counted(&mut parent);
        nested.commit().unwrap();
    }
    assert_eq!(parent.counter(), 1);

    // Non-empty abort
    {
        let mut nested = vec![10, 11].into_iter().nested_shadow_counted(&mut parent);
        nested.next();
        nested.next();
        // Abort
    }
    assert_eq!(parent.counter(), 1);

    // Non-empty commit
    {
        let mut nested = vec![20].into_iter().nested_shadow_counted(&mut parent);
        nested.next();
        nested.commit().unwrap();
    }
    assert_eq!(parent.counter(), 2);

    // Empty abort (should be no different than commit)
    {
        let empty: Vec<i32> = vec![];
        let _nested = empty.into_iter().nested_shadow_counted(&mut parent);
        // Abort
    }
    assert_eq!(parent.counter(), 2);
}