simple-update-in 0.2.0

Immutable nested update with structural sharing
Documentation
# simple-update-in

Immutable nested update with structural sharing.

`update_in` walks a path into a JSON-like tree, transforms the value at the end,
and returns a new root. Every subtree off the path is shared by pointer, so a
one-leaf edit clones only the nodes along the path. If the edit changes nothing,
the original root comes back by the same pointer.

## Installation

```toml
[dependencies]
simple-update-in = "0.1"
```

## Usage

```rust
use simple_update_in::{update_in, Accessor, Value};

let from = Value::from_pairs([
    ("one", Value::Number(1.0)),
    ("two", Value::Number(2.0)),
]);

let actual = update_in(
    from,
    &[Accessor::key("one")],
    Some(&|v| match v {
        Value::Number(n) => Value::Number(n * 10.0),
        other => other,
    }),
);

assert_eq!(
    actual,
    Value::from_pairs([
        ("one", Value::Number(10.0)),
        ("two", Value::Number(2.0)),
    ]),
);
```

## Behavior

- Immutable. The input is never mutated. The result is deep along the path and
  shared off it.
- No-op identity. If the edit makes no change under SameValue, the original root
  is returned by the same pointer. `Value::ptr_eq` reports this.
- Upsert. Missing intermediates are created: a map for a key, an array for an
  index.
- Type coercion. An incompatible container is replaced. A key needs a map, an
  index needs an array.
- Removal. Pass `None` as the updater, or return `Value::Undefined`, to remove
  the addressed key or index. Arrays shrink. Maps drop the key.
- Predicate paths. A path step can be a predicate that selects matching
  children, branching the update across each match.
- Reserved keys. A step equal to `__proto__`, `constructor`, or `prototype`
  makes the whole call a no-op.

## SameValue

The no-op check uses the SameValue algorithm, the same one as JavaScript
`Object.is`. `NaN` equals `NaN`, so writing `NaN` over `NaN` is a no-op. `+0.0`
does not equal `-0.0`, so writing `-0.0` over `0.0` is a change.

## Async

`update_in_async` is an async wrapper over `update_in`. The engine does no IO,
so it resolves immediately and returns the same result. It exists so async
callers can await a single entry point.

## License

Licensed under the [MIT license](LICENSE).