|
| 1 | +# Object |
| 2 | + |
| 3 | +Opaque container for `Value::Object`'s key→value storage, enabling |
| 4 | +alternative backends without call-site changes. |
| 5 | + |
| 6 | +## Design |
| 7 | + |
| 8 | +`Object` wraps the storage for a key→value collection of `Value`s and |
| 9 | +provides a curated set of methods (`get`, `insert`, `remove`, `iter`, |
| 10 | +`iter_sorted`, `cursor`, serde). The backing store is private; callers |
| 11 | +never see or pattern-match on it, so the representation can change |
| 12 | +without rippling through call sites. |
| 13 | + |
| 14 | +Multiple backends can coexist at runtime. Because the backing store is |
| 15 | +private, different `Object` instances in the same process can use |
| 16 | +different implementations — e.g., a lazy DB-backed object for `input`, |
| 17 | +inline small-map objects for SARIF location records, and a regular |
| 18 | +sorted map elsewhere — all interoperating through the same opaque |
| 19 | +type. This is stronger than the typical Cargo-feature-selected backend |
| 20 | +seen in precedent crates. |
| 21 | + |
| 22 | +Iteration is split intentionally. `iter()` makes no ordering promise, |
| 23 | +which lets backends that don't keep entries sorted skip any sort work. |
| 24 | +`iter_sorted()` returns entries in `Value` order and is what |
| 25 | +serialization and `Ord` rely on for deterministic output. Cursor types |
| 26 | +add resumable, incremental traversal for the RVM iteration state |
| 27 | +without leaking iterator internals. |
| 28 | + |
| 29 | +`Ord` and `PartialOrd` are defined against `iter_sorted()` rather than |
| 30 | +derived from the storage. Two `Object`s built on different backends — |
| 31 | +or with different insertion histories — compare equal whenever their |
| 32 | +sorted entries match, so changing the backend never changes observable |
| 33 | +comparison results. |
| 34 | + |
| 35 | +## Precedents |
| 36 | + |
| 37 | +Other crates that hide storage behind a stable API so the implementation |
| 38 | +can change without breaking callers: |
| 39 | + |
| 40 | +- **`serde_json::Map`** — opaque newtype allowing cargo-feature based |
| 41 | + swap between `BTreeMap` (canonical order) and `IndexMap` (insertion |
| 42 | + order). |
| 43 | +- **`toml::Table`** — opaque newtype allowing cargo-feature based swap |
| 44 | + between `BTreeMap` and `IndexMap`. |
| 45 | +- **`simdjson` DOM** — opaque tree that lazily materializes nodes on |
| 46 | + access instead of parsing the whole document up front. |
| 47 | + |
| 48 | +## Use cases |
| 49 | + |
| 50 | +- **SARIF small-object pressure** — SARIF reports contain millions of |
| 51 | + small objects (location records, rule references, message arguments), |
| 52 | + most with 2-5 keys. A small-map-optimized backend (inline storage |
| 53 | + for ≤N entries, heap above) eliminates per-object BTreeMap allocation |
| 54 | + for the common case. |
| 55 | + |
| 56 | +- **Kubernetes admission policies** — large, deeply-nested resource |
| 57 | + objects (Pod specs, CRDs) where policies typically touch a handful |
| 58 | + of paths. A lazy-materializing backend (`LazyObjectProvider` over |
| 59 | + the incoming JSON) parses only the accessed subtrees. |
| 60 | + |
| 61 | +- **Azure Policy aliases** — ARM exposes the same logical property |
| 62 | + under multiple aliases (e.g. paths like |
| 63 | + `Microsoft.Compute/virtualMachines/storageProfile.osDisk.managedDisk.id`). |
| 64 | + An alias-aware backend resolves lookups across canonical and alias |
| 65 | + forms without rewriting every policy. |
| 66 | + |
| 67 | +- **Azure Policy case-insensitive compare** — ARM property names are |
| 68 | + case-preserving but case-insensitive on lookup (`tags.Environment` |
| 69 | + and `tags.environment` resolve identically). A case-insensitive |
| 70 | + backend centralizes this once at the storage layer instead of at |
| 71 | + every comparison site. |
| 72 | + |
| 73 | +- **External data sources** — `input` or `data` backed by a database |
| 74 | + query, CBOR slice, REST endpoint, or other streaming source via a |
| 75 | + `LazyObjectProvider`. Entries materialize on demand; the policy |
| 76 | + only pays for what it touches. |
| 77 | + |
| 78 | +- **Eval-time temporaries** — objects constructed during evaluation |
| 79 | + (comprehensions, intermediate rule results) on a bumpalo arena. |
| 80 | + The whole arena drops at query end with zero per-entry free cost. |
| 81 | + |
| 82 | +- **Host-language interop** — Python dicts or JS objects accessed via |
| 83 | + FFI callbacks from the embedding application, without copying into |
| 84 | + Rust on every binding boundary. |
0 commit comments