alux-shape-json 0.1.0

JSON interpretation of ALUX data shapes
Documentation
# alux-shape-json

`alux-shape-json` interprets an [`alux-shape`](../../alux-shape) term as a decision about a JSON value.

```rust
use alux_shape::{FieldAlg, ShapeAlg, ShapeExt, Spelling};
use alux_shape_json::Judge;
use serde_json::json;

let alg = Judge::new(Spelling::LowerCamel);
let checksum = alg.field(&["checksum"], alg.bytes_hex(Some(2)));
let display_name = alg.field(&["display", "name"], alg.text());

let user = alg.named_product(&["user"], vec![checksum, display_name]);

assert!(user.holds(&json!({"checksum": "0xbeef", "displayName": "ada"})).is_ok());

// The surface says how a name is written, so a differently spelled key is a different value.
assert!(user.holds(&json!({"checksum": "0xbeef", "display_name": "ada"})).is_err());
```

This is the interpretation that makes a shape answerable rather than merely describable: a term either
describes what was written or names where it did not. Judging is strict in both directions — a member
the shape describes must be present unless it may be absent, and a member the value carries must be
described — because a shape that quietly tolerates an extra key cannot be trusted to state what a
type writes.

A name is spelled here as the surface spells it, which is why `Judge::new` takes a `Spelling` and the
term does not carry one.

Two things it does not decide on its own. Bare bytes describe no JSON value, since nothing states how
they are written; `bytes_hex` or `base64` does. And a `reference` describes anything, because
resolving a name needs the whole shape at once rather than the fold that builds it — so a term read
back by [`alux-shape-term`](../alux-shape-term) is resolved first, and then a name states what it
introduced.