simple-update-in 0.2.0

Immutable nested update with structural sharing
Documentation
//! Structural sharing: subtrees off the path keep their pointer, subtrees on
//! the path get cloned.
//!
//! These mirror the `toBe` and `not.toBe` assertions from the source suite.
//! `ptr_eq` stands in for JavaScript reference identity.

mod common;

use common::*;

/// Read a child of a map by key.
fn child_key(v: &simple_update_in::Value, k: &str) -> simple_update_in::Value {
    v.as_object().unwrap().get(k).unwrap().clone()
}

/// Read a child of an array by index.
fn child_idx(v: &simple_update_in::Value, i: usize) -> simple_update_in::Value {
    v.as_array().unwrap()[i].clone()
}

#[test]
fn nested_map_shares_sibling() {
    let from = obj(vec![
        ("odd", obj(vec![("one", n(1.0)), ("three", n(3.0))])),
        ("even", obj(vec![("two", n(2.0))])),
    ]);
    let actual = run(from.clone(), &[key("odd"), key("one")], multiply(10.0));

    assert!(!from.ptr_eq(&actual));
    assert!(!child_key(&from, "odd").ptr_eq(&child_key(&actual, "odd")));
    assert!(child_key(&from, "even").ptr_eq(&child_key(&actual, "even")));
}

#[test]
fn remove_in_nested_map_shares_sibling() {
    let from = obj(vec![
        ("odd", obj(vec![("one", n(1.0)), ("three", n(3.0))])),
        ("even", obj(vec![("two", n(2.0))])),
    ]);
    let actual = remove(from.clone(), &[key("odd"), key("three")]);

    assert!(!from.ptr_eq(&actual));
    assert!(child_key(&from, "even").ptr_eq(&child_key(&actual, "even")));
}

#[test]
fn nested_array_shares_sibling() {
    let from = arr(vec![
        n(0.0),
        arr(vec![n(1.1), n(1.2)]),
        arr(vec![n(2.1), n(2.2)]),
    ]);
    let actual = run(from.clone(), &[idx(1), idx(1)], constant(n(3.0)));

    assert!(!from.ptr_eq(&actual));
    assert!(!child_idx(&from, 1).ptr_eq(&child_idx(&actual, 1)));
    assert!(child_idx(&from, 2).ptr_eq(&child_idx(&actual, 2)));
}

#[test]
fn remove_in_nested_array_shares_sibling() {
    let from = arr(vec![arr(vec![n(1.0), n(3.0)]), arr(vec![n(2.0), n(4.0)])]);
    let actual = remove(from.clone(), &[idx(0), idx(1)]);

    assert!(!from.ptr_eq(&actual));
    assert!(child_idx(&from, 1).ptr_eq(&child_idx(&actual, 1)));
}

#[test]
fn mix_map_array_shares_sibling() {
    let from = obj(vec![
        ("one", arr(vec![n(1.1), n(1.2), n(1.3)])),
        ("two", arr(vec![n(2.1), n(2.2), n(2.3)])),
    ]);
    let actual = run(from.clone(), &[key("one"), idx(1)], multiply(10.0));

    assert!(!from.ptr_eq(&actual));
    assert!(!child_key(&from, "one").ptr_eq(&child_key(&actual, "one")));
    assert!(child_key(&from, "two").ptr_eq(&child_key(&actual, "two")));
}

#[test]
fn mix_array_map_shares_sibling() {
    let from = arr(vec![
        obj(vec![("one", n(1.0)), ("three", n(3.0))]),
        obj(vec![("two", n(2.0))]),
    ]);
    let actual = run(from.clone(), &[idx(0), key("three")], multiply(10.0));

    assert!(!from.ptr_eq(&actual));
    assert!(!child_idx(&from, 0).ptr_eq(&child_idx(&actual, 0)));
    assert!(child_idx(&from, 1).ptr_eq(&child_idx(&actual, 1)));
}

#[test]
fn expand_map_shares_existing_subtree() {
    let from = obj(vec![("two", obj(vec![("three", n(2.3))]))]);
    let actual = run(from.clone(), &[key("one"), key("two")], constant(n(1.2)));

    assert!(!from.ptr_eq(&actual));
    assert!(child_key(&from, "two").ptr_eq(&child_key(&actual, "two")));
}

#[test]
fn deep_sharing_two_levels_down() {
    // Change one leaf three levels deep. A cousin subtree two levels deep stays
    // shared by pointer.
    let cousin = obj(vec![("keep", n(99.0))]);
    let from = obj(vec![
        (
            "a",
            obj(vec![
                ("target", obj(vec![("leaf", n(1.0))])),
                ("sibling", cousin.clone()),
            ]),
        ),
        ("b", obj(vec![("other", n(2.0))])),
    ]);
    let actual = run(
        from.clone(),
        &[key("a"), key("target"), key("leaf")],
        multiply(10.0),
    );

    assert!(!from.ptr_eq(&actual));
    // Off-path top-level subtree shared.
    assert!(child_key(&from, "b").ptr_eq(&child_key(&actual, "b")));
    // Cousin two levels down shared.
    let from_sib = child_key(&child_key(&from, "a"), "sibling");
    let actual_sib = child_key(&child_key(&actual, "a"), "sibling");
    assert!(from_sib.ptr_eq(&actual_sib));
    assert_eq!(
        actual,
        obj(vec![
            (
                "a",
                obj(vec![
                    ("target", obj(vec![("leaf", n(10.0))])),
                    ("sibling", cousin),
                ]),
            ),
            ("b", obj(vec![("other", n(2.0))])),
        ]),
    );
}

#[test]
fn source_is_not_mutated() {
    // The input keeps its original value after the call.
    let from = obj(vec![
        ("a", obj(vec![("leaf", n(1.0))])),
        ("b", arr(vec![n(2.0), n(3.0)])),
    ]);
    let snapshot = obj(vec![
        ("a", obj(vec![("leaf", n(1.0))])),
        ("b", arr(vec![n(2.0), n(3.0)])),
    ]);
    let _ = run(from.clone(), &[key("a"), key("leaf")], multiply(10.0));
    assert_eq!(from, snapshot);
}